-->
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.  [ 12 posts ] 
Author Message
 Post subject: Mapping of Two Class Hierarchies with an Association
PostPosted: Tue Mar 28, 2006 8:33 am 
Newbie

Joined: Tue Mar 28, 2006 7:49 am
Posts: 8
Hi,

I'm new to Hibernate (which I'm enjoying -- honestly!) and have a question regarding an association between two class hierarchys, which are mapped to a table-per-class hierarchy strategy. (see bad uml below for class association :-)

Code:
______________             _______
|   Session    | *       1 | State |
|--------------|-----------|-------|
| long id      |           |       |
| Date lastMod |           |       |
| State state  |           |_______|
|______________|               ^
       ^                       |
       |                     State
    Session                subclasses
   subclasses


I.e. each instance of a`session' type has a state, which is a subclass of the abstract `state' class. Each state can belong to zero or more sessions.

What's puzzling me is how I map this in Hibernate... I've tried using a <one-to-one/> mapping with a foreign key in the state table of the session id (mapping file attached below, which contains one subclass of each class hierarchy), but when I call hibernate to save(session_instance), nothing gets saved to the state table.

What I'd *really* like to have is both hierarchies in one table, so that a session_type also had a state_type, but I don't think this is possible - maybe I'm wrong though!

I'm sorry if this is a trivial problem, or if I've got this completely wrong -- but I'm a bit stuck about how to represent this and get it working in hibernate...

Many thanks for any help...

Ed.

Hibernate version:

3.1.2

Mapping documents:

Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping SYSTEM
         "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<!--

the mapping for the class hierarchy of session types.
uses a "table-per-class-hierarchy" model as desribed on
page 99, Section 3.6.2 of Bauer.

each session has four attributes: its id, last updated
time, type and current state. a session can only have
one state.

-->
<hibernate-mapping>

<class
   name = "session"
   table = "sessions"
   discriminator-value = "SESSION">
      
   <!-- 1) unique session id -->
   <id name = "id"
      column = "SESSION_ID"
      type = "long" />
      
   <!-- 2) type of session -->
   <discriminator
      column = "SESSION_TYPE"
      type = "string"
      not-null = "true" />

   <!-- 3) last update time -->
   <property
      name = "lastUpdateTime"
      column = "LAST_UPDATE"
      type = "java.util.Date"
      not-null = "true" />
   
   <!-- 4) current state -->
   <one-to-one
      name = "state" 
           class = "State"
           cascade = "delete"
           constrained = "true" />

   <!-- subclasses of session -->
   
   <!-- session over tcp -->   
   <subclass name = "TcpSession"
      discriminator-value = "TcpSession" />
         
</class>

</hibernate-mapping>

Code:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping SYSTEM
         "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >

<!--

the mapping for the class hierarchy of session states.

-->
<hibernate-mapping>

<class name="State"
      table="states"
      discriminator-value="STATE">
      
   <!-- session id -->      
   <id name="id"
      column="SESSION_ID">
        <generator class="foreign">
            <param name="property">sessions</param>
        </generator>
    </id>

   <discriminator
      column = "STATE_TYPE"
      type = "string" />
      
   <subclass
      name = "Uninitialised"
      discriminator-value = "Uninitialised" />

</class>

</hibernate-mapping>




Name and version of the database you are using:

mySql 4.1.18

The generated SQL (show_sql=true):

insert into agreements (LAST_UPDATE, SESSION_TYPE, ID) values (?, 'TcpSession', ?)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 28, 2006 11:46 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
If each state can belong to zero or more sessions, then you can't use one-to-one. That would require that each state has one and only one session. You need to change that mapping to many-to-one unique="true" property-ref="sessionId" or whatever, and use different stateId/sessionId columns in your state table. If each session has one and only one state, you'll need to add not-null="true", too (without the not-null, each session has zero or one states).

This doesn't explain why your current mapping doesn't save the state with the session. That's because you have the wrong cascade on your one-to-one: you need to include at least save. Alternatively, you could save the state separately, but that doesn't sound like what you want to do.


Top
 Profile  
 
 Post subject: Mapping of Two Class Hierarchies with an Association
PostPosted: Wed Mar 29, 2006 7:40 am 
Newbie

Joined: Tue Mar 28, 2006 7:49 am
Posts: 8
Thanks Tenwit, your comments have been very helpful to me. The cascade comment was realy useful, and the stuff about uniqueness and not-null parameters really good. I'm begining to understand a lot more.

However, a question about the property-ref from your post
Quote:
property-ref="sessionId" or whatever, and use different stateId/sessionId columns in your state table.

Are you saying that each instance of the state must create its own, unique id, that is mapped into the session table? (as the property-ref).

Or, must the state class have a reference the sessions id ? (But then it becomes a one-to-one reference doesn't it ? ...as each row in the state table is for one session with id `id'). I was hoping that the state wouldn't have to have this reference as the state doesn't care which session it belongs to.

Sorry, but I'm still a bit unclear on how this many-to-one mapping occurs between subclasses, given that I don't want a hard reference from the state to the session....

Again, many thanks for your response, sorry if I've misunderstood something.

Ed.


Top
 Profile  
 
 Post subject: Mapping of Two Class Hierarchies with an Association
PostPosted: Wed Mar 29, 2006 10:40 am 
Newbie

Joined: Tue Mar 28, 2006 7:49 am
Posts: 8
Hi,

I've been experimenting some more and have in my sessions mapping file (see above):
Code:
   <!-- 4) current state -->
   <many-to-one
      name = "state" 
           class = "State"
           property-ref = "id"
           unique = "true"
           cascade = "save-update"
           not-null = "true" />

In my state mappings file i now have :
Code:
<class name="State"
      table="states"
      discriminator-value="STATE">
      
   <!-- state id -->      
   <id name = "id"
      column = "STATE_ID"
      type = "int" >
        <generator class = "increment" />
    </id>

   <discriminator
      column = "STATE_TYPE"
      type = "string" />
      
   <!-- subclasses of state -->      

   <subclass
      name = "Initial"
      discriminator-value = "Initial" />

</class>

When I try and run this I get the following exception:
Code:
org.hibernate.MappingException: property-ref not found: id in entity: State

... which I guess means hibernate is looking for the state id in the State object. But, I have a private member variable (called id) of type int in my state object and have (public) getId() and setId() methods on the state class.

I'm a bit clueless as what to do next, as this seems pretty correct (although obviously it isn't!)

Again, any help is appreciated.

Ed.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Mar 29, 2006 6:35 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
edsweet wrote:
Are you saying that each instance of the state must create its own, unique id, that is mapped into the session table? (as the property-ref).

Yes, if you want anything other than a strict one-to-one, both tables must have their own, distinct primary keys. If you want a strict one-to-one, where one state has one session and one session has one state, then you can use one id for both tables, but you said earlier that states can be used by more than one session.

edsweet wrote:
Or, must the state class have a reference the sessions id ? (But then it becomes a one-to-one reference doesn't it ? ...as each row in the state table is for one session with id `id').

No, the session class must have a reference to the state's id. State is the "one" side of the many-to-one: one session has one state, but a different session may have the same state. So you have many sessions referring to one state.

edsweet wrote:
But, I have a private member variable (called id) of type int in my state object and have (public) getId() and setId() methods on the state class.

You have those members, but they're the id, not a property.. it's a subtle difference. The trick is to omit property-ref, as that tells hibernate not to use the id/primary key, and to use a property instead. You specifically want to use the id, and not a property, so leave out property-ref.

You will need to add the column="stateid" attribute to your many-to-one mapping, so that hibernate knows what column in the session table contains the id of the associated state.


Top
 Profile  
 
 Post subject: Mapping of Two Class Hierarchies with an Association
PostPosted: Thu Apr 13, 2006 5:13 pm 
Newbie

Joined: Tue Mar 28, 2006 7:49 am
Posts: 8
Sorry its taken me a while to get back to you, lots of problems at home.

Anyway, I have mostly got this working. The mapping for the Session class to the state is below:

Code:
    <!-- current state -->
    <many-to-one
        name = "state" 
        class = "State"
        column = "STATE_ID"
        unique = "true"
        cascade = "save-update"
        not-null = "true" />


This is ok, but I get multiple entries for each state in the State table (mapping file below).

Code:
    <class name="State"
        table="states"
        discriminator-value="STATE">
      
        <!-- state id -->      
        <id name = "id"
            column = "STATE_ID"
            type = "int" >
            <generator class = "increment" />
        </id>

        <discriminator
            column = "STATE_TYPE"
            type = "string" />
      
        <!-- subclasses of state -->      

        <subclass
       name = "Initial"
            discriminator-value = "Initial" />

        ....

    </class>


How can I ensure that there is only one instance of each state in the State table?

Many thanks for any help,

Ed.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Apr 14, 2006 10:25 pm 
Regular
Regular

Joined: Wed Jul 07, 2004 2:00 pm
Posts: 64
I believe that your session should have a
<many-to-one
name = "state"
class = "State"
column = "STATE_ID"
not-null = "true" />

You probably don't want a unique or a cascade.


Top
 Profile  
 
 Post subject: Mapping of Two Class Hierarchies with an Association
PostPosted: Sat Apr 15, 2006 12:12 pm 
Newbie

Joined: Tue Mar 28, 2006 7:49 am
Posts: 8
Thanks for the reply DWright.

I tried my mapping file without the cascade or unique entries. When I try and save a session to the session table I get the following error:

Code:
Exception: not-null property references a null or transient value: Session.state

The thing is, the State object is always set in the Session object, it is not null. Neither have I marked it as transient (the state is just private).

I've also tried:

1) Only adding the unique entry. This gives the same error message.

2) Only adding the cascade entry. When I do this I get duplicates in the table.

Again, thanks for any help you could give.

Ed.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Apr 15, 2006 1:11 pm 
Regular
Regular

Joined: Wed Jul 07, 2004 2:00 pm
Posts: 64
I'd say that the state is probably transient, and not null. This explains why, when you have cascade, it inserts state records. When you set the state in the session, you should probably set it to an instance of state that you read from the database. Hibernate is detecting that the state is transient because the state object doesn't have an id value.


Top
 Profile  
 
 Post subject: Mapping of Two Class Hierarchies with an Association
PostPosted: Sat Apr 15, 2006 1:41 pm 
Newbie

Joined: Tue Mar 28, 2006 7:49 am
Posts: 8
Hi,

Thanks for the reply (again).

So, each time I create a state I have to save the state to the state table before I can use it in my session? This would make sense, but I was hoping Hibernate would handle this for me!

Thanks,

Ed.

ps. Sorry, I think I got confused between the `transient' java keyword and what hibernate means by transient.


Top
 Profile  
 
 Post subject: Mapping of Two Class Hierarchies with an Association
PostPosted: Sat Apr 15, 2006 2:40 pm 
Newbie

Joined: Tue Mar 28, 2006 7:49 am
Posts: 8
ok, I tried doing a HibernateSession.save(state) to save the state to the database before saving the TcpSession to the db.

This solved the `not null/transient' error, but I'm still getting duplicate State entries in the database. (I tried putting back the unique = true and cascade entries into the mapping file, and it still did this).

Having to save the state seperately seems a bit odd to me - why can't hibernate see that when the Session is saved, the state has to be saved too?

I'm really confused now.

How can I make sure that there is only one of each state in the state table?

Many thanks,

Ed.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Apr 15, 2006 3:15 pm 
Regular
Regular

Joined: Wed Jul 07, 2004 2:00 pm
Posts: 64
OK, lets say you have a transient (never before saved) Session mySession that should have a State with ID 1234.

What you can do is:
(s is a Hibernate session)

State myState = s.get(State.class,new Long(1234));
mySession.setState(myState);
s.save(myState);

It may be useful to just read all of the States into a map in memory so that you can do mySession.setState without having to instantiate a session yet (especially if you are using a DAO pattern).

You don't want to do a HibernateSession.save(state) because the set of states should be read-only (in fact, you can guarantee this by adding a mutable=false in the class element of the State mapping).


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 12 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.