-->
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.  [ 18 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: comment on many-to-many with link table section (6.3.2)
PostPosted: Sat Nov 06, 2004 1:39 pm 
Regular
Regular

Joined: Mon Sep 20, 2004 8:42 am
Posts: 58
Location: Boston, US
I don't need to restate that the book rocks, but one of the major subjects that I found missing was the discussion of a many-to-many relationship with a link table that is modeled as 2 one-to-many relationships with an association entity class. Section 6.3.2 goes over the "exotic" version of modeling such a relationship using composite-element and even goes on to say that its better to model such a relationship as 2 many-to-ones with an association class but does not provide any detailed example.

I was able to write such a mapping but was unclear about the behavior of updates and deletes. For example which entity to I call saveOrUpdate() on? The Category instance, The Item instance or the CategoryItem instance.

Also how do I create a new association? I this case do I have to do something different compared to an update like call save() on the CategoryItem instance and programmatically maintain the relationship between Category-CategoryItem and Item-CategoryItem.

I was happy to see that the CaveatEmptor source code has been updated this example to be modeled as 2 many-to-one relationships. Also the test cases provide insight on how to delete associations. It would be nice if there was an example that created a new association as well.

Thanks to the example, I have it figured out now but I think this topic requires further discussion and there should be a section on this in the book along with examples. I hope the next revision of the book includes this.

On a side note, it would be nice if the eBook could be available in .chm format as well. I think its much more user friendly than the pdf format.

Thanks,
Sanjiv


Top
 Profile  
 
 Post subject: Hibernate in Action and many-to-many
PostPosted: Wed Dec 01, 2004 10:18 am 
Newbie

Joined: Fri Oct 22, 2004 11:28 am
Posts: 6
Location: Hertfordshire
Here here!! I couldn't agree more.

I'm struggling with this myself.

Any pointers or examples would be greatly appreciated.

Thanks

_________________
when we set out to write software, we never know enough


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 01, 2004 11:21 am 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
This topic doesn't require further discussion, as you just have to lookup the explanation for a simple one-to-many mapping. If you have one or two of them doesn't matter, especially if you know how object states and cascades work (if not then reread). There is nothing special for this "many to many", because, as you said, its not a many-to-many anymore...


Top
 Profile  
 
 Post subject:
PostPosted: Wed Dec 01, 2004 11:25 am 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Sorry guys, but you just have to spend more time on this. You picked the most difficult Hibernate mapping problem. Give it some days (or even weeks). All explanations you need are out there and you have them in your hands. There is nothing I can add.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 02, 2004 1:38 pm 
Newbie

Joined: Fri Oct 22, 2004 11:28 am
Posts: 6
Location: Hertfordshire
Using the caveatemptor app as an example I can save fine.

However when I delete an object (using similar code from your test case)

Code:
Long drugId = new Long(354);
tx = session.beginTransaction();
ReportedDrug drugToDel = (ReportedDrug)session.get(ReportedDrug.class, drugId);
drugToDel.getDrugEvents().clear();
tx.commit();
session.close();


Drug has one-to-many with DrugEvent
Event has one-to-many with DrugEvent
DrugEventId is composite key class of DrugEvent

I get this error:

net.sf.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): com.roche.dss.extreportdomainmodel.DrugEventId@292, of class: com.roche.dss.extreportdomainmodel.DrugEvent
at net.sf.hibernate.impl.SessionImpl.forceFlush(SessionImpl.java:752)
at net.sf.hibernate.impl.SessionImpl.save(SessionImpl.java:730)
at net.sf.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:1376)
at net.sf.hibernate.engine.Cascades$4.cascade(Cascades.java:114)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:436)
at net.sf.hibernate.engine.Cascades.cascadeCollection(Cascades.java:526)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:452)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:503)
at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:482)
at net.sf.hibernate.impl.SessionImpl.preFlushEntities(SessionImpl.java:2673)
at net.sf.hibernate.impl.SessionImpl.flushEverything(SessionImpl.java:2250)
at net.sf.hibernate.impl.SessionImpl.flush(SessionImpl.java:2239)
at net.sf.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:61)
at com.roche.dss.extreportdomainmodel.FullReportTest.testDeleteDrug(FullReportTest.java:113)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:324)
at junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:421)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:305)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:186)

Not much experience of this on the forum.

Ken

_________________
when we set out to write software, we never know enough


Top
 Profile  
 
 Post subject:
PostPosted: Sat Dec 04, 2004 9:05 am 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Well, the exception message is really clear. In fact, it couln't be more precise about what is wrong and what you have to do.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 06, 2004 2:07 pm 
Newbie

Joined: Fri Oct 22, 2004 11:28 am
Posts: 6
Location: Hertfordshire
What is this error message telling me?

What I had to do was remove
Code:
outer-join="false"
from the mapping for Event. I didn't see this in the exception message!

Surely the exception message wasn't telling me to loop through all the DrugEvents and remove the ones with the deleted Event id:
"remove deleted object from associations" ?

What I don't understand is why the attributes that are related to fetching strategies are having effects on Delete (not Save and Load). If something is deleted, how can the fetching strategy determine whether it will be re-saved by a cascading save?

As per your book, I'm following your recommendations on not using eager fetching on collections etc.. But what is missing is information relating to this kind of problem where it's difficult to determine the behaviour of hibernate.

_________________
when we set out to write software, we never know enough


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 06, 2004 6:33 pm 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Surely outer-join doesn't affect what you are trying to do! You have start thinking about object states, as explained in chapter 4. Your error message is quite clear: One of the states of your objects in memory is "transient" because you just deleted it explicitely. However, some other object is holding a reference to it, and that reference is set to cascade the "persistent" state.

Hibernate is now confused and its asking you: do you want to really delete that first instance or do you want to have it persistent? Either don't delete it or set the cascading rule to something else. Or, don't load the "other" object that is holding the reference. Really, quite simple when it comes to network object models.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Dec 07, 2004 1:41 pm 
Newbie

Joined: Fri Oct 22, 2004 11:28 am
Posts: 6
Location: Hertfordshire
Christian,

thanks for your help.

Apolgies, it was lazy="true" that made it all work not outer-join (cut and paste error!) Of course the objects holding references were in memory as you correctly pointed out.

The down side of the final solution is that we have to save the DrugEvents (the link table objects) independently.

In case anyone knows a better way here are the mapping files:

Code:
<class name="Drug"
      table="DRUG"
      lazy="true">

   <id name="id"
      type="long"
      column="DRUG_ID"
      unsaved-value="null">
      <generator class="sequence">
         <param name="sequence">SQ_EXT_REPORTED_DRUG_ID</param>
      </generator>
   </id>
   
   <property name="drugName"
            type="string"
           column="DRUG_NAME"
           length="200"/>
               
   <set name="drugEvents"
       cascade="delete-orphan"
       inverse="true">
      <key column="DRUG_ID">
         <column name="DRUG_ID" length="19"/>
        </key>
      <one-to-many class="DrugEvent"/>
   </set>

   <many-to-one name="report"
                column="REPORT_ID"
                class="Report"
                not-null="true" />
</class>


Code:
<class name="DrugEvent"
      table="DRUG_EVENT"
      lazy="true">

   <composite-id name="id" class="DrugEvent$Id"
                 unsaved-value="any">

        <key-property name="drugId"
                      access="field"
                      column="DRUG_ID"
                      length="19"/>

        <key-property name="eventId"
                      access="field"
                      column="EVENT_ID"
                      length="19"/>
   </composite-id>

    <property   name="relationship"
                column="RELATIONSHIP"
                type="string"
                update="false"
                not-null="true"/>

   <many-to-one    name="drug"
                    insert="false"
                    update="false"
                    not-null="true"
                    column="DRUG_ID"/>

    <many-to-one    name="event"
                    insert="false"
                    update="false"
                    not-null="true"
                    column="EVENT_ID"/>
</class>


Code:
<class name="Event"
      table="EVENT"
      lazy="true">

   <id name="id"
      type="long"
      column="EVENT_ID"
      unsaved-value="null">
      <generator class="sequence">
          <param name="sequence">SQ_EXT_EVENT_ID</param>
      </generator>
   </id>

   <property   name="reportedTerm"
            type="string"
            column="REPORTED_TERM"
            length="255"
            not-null="true"
            update="false"/>
   
   <set    name="drugEvents"
         cascade="delete-orphan"
         inverse="true">
      <key column = "EVENT_ID">
            <column name="EVENT_ID" length="19"/>
        </key>
        <one-to-many class="DrugEvent"/>
   </set>
   
   <many-to-one name="report"
             column="REPORT_ID"
             class="Report"
             not-null="true"/>   
</class>



Code:
   <class name="Report" table="REPORT" >
      
      <id name="reportId"
         type="long"
         column="REPORT_ID">
           <generator class="sequence">
               <param name="sequence">SQ_EXT_REPORT_ID</param>
         </generator>
      </id>
      
      <bag name="drugs" cascade="all-delete-orphan" inverse="true">
         <key column="REPORT_ID"/>
         <one-to-many class="com.roche.dss.dommodeltest.Drug"/>
      </bag>
      
      <bag name="events" cascade="all-delete-orphan" inverse="true">
         <key column="REPORT_ID"/>
         <one-to-many class="Event"/>
      </bag>

      <property name="reportType"
               type="string"
              column="REPORT_TYPE_CODE"
              length="10"/>
   </class>

_________________
when we set out to write software, we never know enough


Top
 Profile  
 
 Post subject: many-to-many examples
PostPosted: Sun Aug 14, 2005 12:30 am 
Newbie

Joined: Sun Aug 14, 2005 12:25 am
Posts: 5
I have to agree with the initial poster. Documentation on many-to-many relationships using link tables is sparse to say the least


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 17, 2005 2:19 pm 
Newbie

Joined: Wed Aug 17, 2005 2:05 pm
Posts: 2
Quote:
<set name="drugEvents"
cascade="delete-orphan"
inverse="true">
<key column="DRUG_ID">
<column name="DRUG_ID" length="19"/>
</key>
<one-to-many class="DrugEvent"/>
</set>

<many-to-one name="report"
column="REPORT_ID"
class="Report"
not-null="true" />


What is the difference between the
<set name- ...
and the
<many-to-one ...

Why would we not just have used one-to-many insted of a set?

Edward


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 17, 2005 2:39 pm 
Newbie

Joined: Wed Aug 17, 2005 2:05 pm
Posts: 2
Using the mapping provided above, how would you do a query to

Get EVENT.REPORTED_TERM where DRUG.DRUG_NAME is 'abc'

Edward


Top
 Profile  
 
 Post subject: Having stumbled upon the many to many problem
PostPosted: Wed Sep 14, 2005 10:22 am 
Beginner
Beginner

Joined: Sun May 29, 2005 4:03 pm
Posts: 24
I have to agree with the critics, and christian dont take that personally, document many to many more, does not matter where, but it needs more documentation.

While the error in this case is clear for Christian, being a Hibernate core developer, it took me as a normal some time to figure out on what is going on in this nasty case.

Probably the best practice in this case is...

dont use compound keys in the binding table at all but use a surrogate,
if you cannot do that, enforce lazy = true, otherwise you will run into this problem.
I am not sure whether this is a bug, but it is a huge problem every developer runs into almost any time.

Hibernate definitely needs some kind of extension api to deal with cases like those in a saner manner.
Something like

xxx.changemanytomany(new relational binding list)


Top
 Profile  
 
 Post subject: many to many AND many to one link table
PostPosted: Fri Oct 21, 2005 5:25 am 
Newbie

Joined: Fri Oct 21, 2005 4:49 am
Posts: 13
I've got my application working fine apart from one issue.

I have a many to many association between A and B. In the mapping files these are explicitly called many to many.

However, I need to store some extra information about the association. There is an extra column in the link table and the A and B thus have an extra many to one association to the link class AB.
The link table has combo of both foreign keys of A and B as the PK.
The mapping file for AB generated by middlegen includes a composite key but instead declares the only non-key property (extraInfo) in the key. If I try to correct this, the application won't even start.
Code:
______              ______
  A   |*----------*| B    |
PK:AID|            |PK:BID|
______|            |______|
    1               1
       *___________*
       | AB        |
       |PK: AID,BID|
       |extraInfo  |
       -------------

The problem is that when I assign an A to a B (or vice versa) a line is correctly put into the AB table but always the default value of extraInfo is saved. I have tried all sorts of different ways of changing this value, none of which work. What is the correct way to do this? Is it just a mapping issue or is there some java code that I haven't tried yet?

Thanks for your help


Top
 Profile  
 
 Post subject:
PostPosted: Fri Oct 21, 2005 5:36 am 
Hibernate Team
Hibernate Team

Joined: Mon Aug 25, 2003 9:11 pm
Posts: 4592
Location: Switzerland
Go to the regular User Forum.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 18 posts ]  Go to page 1, 2  Next

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.