These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 
Author Message
 Post subject: How to map set inside component ?
PostPosted: Wed Feb 07, 2007 12:09 am 
Newbie

Joined: Tue Feb 06, 2007 11:42 pm
Posts: 6
Hi all,

I am new to Hibernate and I spent 2 days trying to resovle this problem so at this point any pointers, suggestion are really welcome ;).

In my domain model I have an Entity object. Entity suppose to have an attributeMap which maps attribute name to Attribute object.

Every Attribute has String name and set of String values.

table Entity (
entity_id
)

table Attribute(
attribute_name,
entity_id,
attribute_value,

PRIMARY KEY(attribute_name, entity_id, attribute_value)
)

If Attribute had just one value in other words if PRIMARY KEY were (attribute_name,entity_id) then I could map it using


<map table="Attribute" name="attributeMap" >
<key column="entity_id" />

<map-key type="java.lang.String" column="attribute_name" />
<composite-element class="Attribute">
<property name="value" column="attribute_value" />
</composite-element>
</map>

but Attribute has a collection of values. Conceptually I need to be able to include a set insdie comosite-element, but it looks like it is impossible.

Please help me. Any suggestions are welcome. I am willing to change DB schema if this one cannot work.

Thanks, Alexi


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 07, 2007 12:26 am 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Don't use composite-element. Give Attribute its own mapping, and reference that from Entity.
Code:
<class name="Attribute" ...>
  <composite-id>
    <key-many-to-one class="Entity" column="entity_id" .../>
    <key-property name="Name" column="attribute_name" .../>
  </composite-id>
  <set name="Values" ...>
    <element column="attribute_value".../>
  </set>
</class>

<class name="Entity" ...>
  ...
  <map name="Attributes" ...>
    <key column="entity_id" .../>
    <map-key column="attribute_name" .../>
    <one-to-many class="Attribute" .../>
  </map>
  ...
</class>
Note that while you may choose to make attribute_value part of the Attribute table's PK (for uniqueness purposes), it should probably not be part of any <id> or <composite-id>. This is because you (probably) don't want to delete and insert a row whenever you update the value of an attribute.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject: More questions
PostPosted: Wed Feb 07, 2007 3:04 am 
Newbie

Joined: Tue Feb 06, 2007 11:42 pm
Posts: 6
Tenwit,

Thanks a lot for your help. I am much closer now.

Using mapping you have suggested I can read my Entities and Attributes just fine ( when I manually insert data in DB), but I cannot insert Attributes using my Hiberante Java code. For what ever reasons Hibernate issues update insted insert and even updates does not look right.

Actual update looks like


update IdentityAttribute set identityId=?, attributeName=? where identityId=? and attributeName=?

It fails because there is no raw like that in DB. It should be insert. On top of that it should also have attributeValues.

First, I thought that the problem exists because I don't hve equals and hashCode in Attribute class. So I went ahead and implemented it, but it did not help

I must not fill the blanks in example you gave me correctly. I was not sure what to use for set key in Attribute mapping.

Here is my actuall mapping (note that Entity=User)

Code:
    <class name="User" table="Users">

        <id name="username" column="username">
           
        </id>
       
        <map table="IdentityAttribute" name="attributeMap" >
            <key column="identityId" />
            <map-key type="java.lang.String" column="attributeName"  />
            <one-to-many  class="Attribute" />
        </map>

    </class>
   
   
   
    <class name="Attribute" table="IdentityAttribute">
        <composite-id>
            <key-many-to-one class="User" name="user"            column="identityId"/>

            <key-property name="name" column="attributeName" />
           
        </composite-id>       
       
        <set name="values" table="IdentityAttribute">
            <key>
                <column name="identityId"/>
                <column name="attributeName"/>
            </key>
            <element type="java.lang.String" column="attributeValue"/>
        </set>
       
    </class>






Thanks again for your help, Alexi[/code]


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 07, 2007 4:26 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
The first problem is that you have table attributes on your set and map. This means that Hibernate thinks that there are join tables involved, which there aren't. So remove those table attributes. Only the two class elements should mention tables.

Let me know if that doesn't fix it, I'll look for more problems.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject: Still no luck
PostPosted: Wed Feb 07, 2007 5:57 pm 
Newbie

Joined: Tue Feb 06, 2007 11:42 pm
Posts: 6
Thanks again,

I did change mapping as you suggested, however I still have exactly the same problem.

Mapping now looks like

Code:
<hibernate-mapping auto-import="true" default-lazy="false"
                   package="com.jackbe.jbp.sas.security.usermgt.identity">
   
    <class name="User" table="Users">
       
       
        <id name="username" column="username">
           
        </id>
       
        <map name="attributeMap" >
            <key column="identityId" />
            <map-key type="java.lang.String" column="attributeName"  />
            <one-to-many  class="Attribute" />
        </map>

    </class>
   
    <class name="Attribute" table="IdentityAttribute">
        <composite-id>
            <key-many-to-one class="User" name="user" column="identityId"/>
            <key-property name="name" column="attributeName" />
           
        </composite-id>       
       
        <set name="values">
            <key>
                <column name="identityId"/>
                <column name="attributeName"/>
            </key>
            <element type="java.lang.String" column="attributeValue"/>
        </set>
       
    </class>
</hibernate-mapping>




Brief error log looks like following. I can make full log with DEBUG on available.

Thanks again for your help, Alexi



Code:

SessHash= 10480266
SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[]])
2007-02-07 16:47:41,093 DEBUG [org.hibernate.SQL] - <select user0_.username as username7_0_ from Users user0_ where user0_.username=?>
2007-02-07 16:47:41,125 DEBUG [org.hibernate.SQL] - <select attributem0_.identityId as identityId1_, attributem0_.attributeName as attribut2_1_, attributem0_.identityId as identityId9_0_, attributem0_.attributeName as attribut2_9_0_ from IdentityAttribute attributem0_ where attributem0_.identityId=?>
before size = 0
Done
2007-02-07 16:47:41,156 DEBUG [org.hibernate.SQL] - <update IdentityAttribute set identityId=?, attributeName=? where identityId=? and attributeName=?>
2007-02-07 16:47:41,156 ERROR [org.hibernate.jdbc.AbstractBatcher] - <Exception executing batch: >
org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
        at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:61)
        at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:46)
        at org.hibernate.jdbc.BatchingBatcher.checkRowCounts(BatchingBatcher.java:68)
        at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
        at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:242)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:235)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:142)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
        at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:580)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:539)
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:509)




Log produced by following code

Code:
     txSt = txMgr.getTransaction(txDef);
       
        User user = (User) idm.findUserById("r3us3r");
       
         Map attrMap  = user.getAttributeMap();
         System.out.println("before size = " + attrMap.entrySet().size());
                 
       
        Attribute a = new Attribute();
        a.setName("password1");
        a.getValues().add("Val1");
        a.getValues().add("Val2");
        a.setUser(user);
        user.getAttributeMap().put("password1",a);
       
        System.out.println("Done");
       
        txMgr.commit(txSt);



Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 07, 2007 7:15 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
According to your mapping, you have the "username" column as the id of Users (I recommend putting type="string" on that id, for increased readability), but you are referring to each Attrbibute's User via the IdentityAttribute table's identityId column. As key-many-to-one doesn't support property-ref, you can't do that. You either have to store the username in the IdentityAttribute table, or else change the id of User to be identityId. I think that the latter is probably correct. Make identityId the id of User, and make username a normal property (and possibly a natural-id, if appropriate).

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Feb 08, 2007 2:46 am 
Newbie

Joined: Tue Feb 06, 2007 11:42 pm
Posts: 6
Honestly I don't fully understand why column name in key-many-to-one of IdentityAttribute table should be the same as id column name in User table.

I still went ahead and changed it the way you suggested.
Unfortunately it did not fix the problem.

I would really like to know what wrong this this mapping, but I have a deadline to meet :). So I decided to change DB schema and mapping (see below) it looks simpler to me and works fine.

I really appreciate all your time an help. Even I was not able to fix the problem I still was able to learn some things.

Thanks a lot, Alexi

Code:
<class name="Identity" table="Identity" >
       
        <id name="id"   column="id">
            <generator class="native"/>
        </id>
       
       
        <property name="name" column="name" />
       
        <map name="attributeMap" cascade="all" lazy="true">
            <key column="identityId" />
            <map-key type="java.lang.String" column="name"  />
            <one-to-many  class="IdentityAttribute" />
        </map>
       
        <many-to-one
            name="parent"
            column="parentId"
            class="Identity"
        />   
   
    </class>
   
    <class name="IdentityAttribute" table="IdentityAttributeName">
        <id name="id" column="id">
            <generator class="native"/>
        </id>     
       
        <property name="name" column="name" />
       
        <set name="values" table="IdentityAttributeValue">
            <key column = "identityAttributeNameId" />
            <element type="java.lang.String" column="value"/>
        </set>
       
    </class>



Code:
CREATE TABLE Identity (
    id  BIGINT generated by default as identity (start with 1),
    parentId BIGINT,
    name VARCHAR(50),
    identityType VARCHAR (12),
    email VARCHAR(50),
    primary key (id)
);


CREATE TABLE IdentityAttributeName (
    id  BIGINT generated by default as identity (start with 1),
    identityId BIGINT ,
    serviceId VARCHAR(50),                               
    name VARCHAR(50) NOT NULL,
    primary key (id)
);

CREATE TABLE IdentityAttributeValue (
    id  BIGINT generated by default as identity (start with 1),
    identityAttributeNameId BIGINT NOT NULL,
    value VARCHAR(50) NOT NULL,
    primary key (id)
);


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 7 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.