-->
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.  [ 3 posts ] 
Author Message
 Post subject: Cascade save-update not working on many-to-many association
PostPosted: Fri Sep 30, 2005 10:14 am 
Newbie

Joined: Thu Jul 21, 2005 5:37 am
Posts: 9
Location: Paris
Hi,

I'll try to rephrase my problem because my previous post didn't get answers (see
http://forum.hibernate.org/viewtopic.php?p=2263481#2263481).

I'm still struggling with a transfer of persistent objects between two databases (two Sessions).
I've got one Hibernate Session opened on PostgreSQL (source) and another opened on HSQL Db (target). Both databases share the same schema, but only the source contains data.

So you might why I cannot use the Session.replicate() for transferring my objects ?
The thing is, I have sequence primary key on my tables. So I have to transfer the objects manually. So I evict each object properly and then save them in the target Session .

My domain model consist of an abstract class A which has a many-to-one relationship with a class C (unidirectionnal). My A class also have a many-to-many relationship (bidirectionnal) with a class D (D.as is the inverse side). As a matter of fact, class D extends C.
Finally, I have a concrete class B which extends A.

To make it clearer, here is some code :

My A class :

Code:
public abstract class A {

  private C c;

  private Set<D> ds;
  .... //getters and setters
}



My C class :
Code:
public class C {
  // sequence identifier
  private Long id;
.... //getters and setters
}


My D class :
Code:
public class D extends C {

  private Set<A> as;
.... //getters and setters
}


And finally my B class :
Code:
public class B extends A {

  // sequence identifier
  private Long id;
.... //getters and setters
}


Now here are the mapping files :

Hibernate version: 3.0.5

Name and version of the database you are using:
PostgreSQL 8.0 (source) and HSQL 1.8.0.1 (target)

Mapping documents:

Code:
<hibernate-mapping>
    <class name="B" table="B">

        <id name="id" column="B_ID" type="java.lang.Long">
            <generator class="sequence">
                <param name="sequence">B_SEQ</param>
            </generator>
        </id>

        <many-to-one
            name="c"
            class="C"
            cascade="save-update"
            outer-join="true"
            update="true"
            insert="true"
            column="C_ID"
        />

        <set
            name="ds"
            table="A_D"
            lazy="false"
            cascade="save-update"
            sort="unsorted"
        >

            <key
                column="B_ID"
            >
            </key>

            <many-to-many
                class="D"
                column="D_ID"
                outer-join="auto"
             />

        </set>

    </class>   
</hibernate-mapping>


Code:
<hibernate-mapping>
    <class name="C" table="C" mutable="false">
       
        <cache usage="read-only" />
       
        <id name="id" column="C_ID" type="java.lang.Long">
            <generator class="sequence">
                <param name="sequence">C_SEQ</param>
            </generator>
        </id>

        <joined-subclass
            name="D"
            table="D"
        >
            <key
                column="D_ID"
            />

        <set
            name="as"
            table="A_D"
            lazy="true"
            inverse="true"
            cascade="none"
            sort="unsorted"
        >

            <key
                column="D_ID"
            >
            </key>

            <many-to-many
                class="B"
                column="B_ID"
                outer-join="auto"
             />

        </set>

        </joined-subclass>

    </class>


An here is what I do :
Code between sessionFactory.openSession() and session.close():

Code:
      tx = targetSession.beginTransaction();
       
      //that criteria is built on the sourceSession     
      ScrollableResults bs = criteria.scroll();
     
      while (bs.next())
      {
         B b = (B) bs.get(0);
         
         C c = b.getC();
         
         Hibernate.initialize(b);
         Hibernate.initialize(b.getDs());
         
         //we must evict the proxy from the original session
         sourceSession.evict(b);
         
         for (D d : b.getDs())
         {
            Hibernate.initialize(d);
            serindSession.evict(d);
         }
         
         Hibernate.initialize(c);
         
         sourceSession.evict(c);
         //we need to set the C id to null in order to force Hibernate to persist it as C is an immutable entity
         //which translates into 'if this C instance has a not null Id, it already exists and will not be updated as Cs are immutable'
         c.setId(null);
         
         targetSession.save(b);
         counter++;

         if ( counter % 50 == 0 )
         {
            log.info("flushing and clearing Hibernate sessions...");
           
            //flush a batch of updates and release memory
            targetSession.flush();
            sourceSession.flush();
            targetSession.clear();
            sourceSession.clear();
         }
         
      }
     
      tx.commit();


The result of this is that the association values between A and D are not in the target Session.
Why ? Here is my understanding :
It seems that the CollectionEntry representing the A.ds association is never recreated by Hibernate.
When the ScrollableResults.next() is called, the object graph is loaded by Hibernate, and an instance of CollectionEntry is created representing the ds PersistentSet inside b.

Before I call targetSession.save(b) the loadedPersister of the CollectionEntry is pointing on PostgreSQL (the source Session), which is what I expect. But just after the save() method is called, that CollectionEntry.loadedPersister is changed with a new BasicCollectionPersister pointing on HSQL (the target Session).
Afterwards, the CollectionEntry.currentPersister is set to the same new BasicCollectionPersister.

At this point, you may be asking yourself, what the hell does he have to care about those loaded and current persister ?

Well, I wouldn't care about those if I haven't looked inside the org.hibernate.engine.Collections the method called prepareCollectionForUpdate().
The code I'm interested in lies on lines 170 and 188. If you look at line 188, this is where the Hibernate reminds to recreate a CollectionEntry if needed.

An when is it needed to do so ? Only when the boolean 'ownerChanged' is set to true. Which is the case when the CollectionEntry.loadedPersister is different from the CollectionEntry.currentPersister, OR if the CollectionEntry.loadedKey is not equal to the b]CollectionEntry.currentKey[/b] (see line 170).

Unfortunately, both ot these conditions are false in my case. The first one, because as I explained, the CollectionEntry.loadedPersister is changed mysteriously from a persister pointing on the source Session to new one pointing on the target Session. (That I don't get).
And of course since the currentPersister is then set with the same persister instance, the first condition is false.
The second is also always false because the sequences are in synch on both database. They both start with 1, so this condition is always false when I intend to transfer all the data from the source to the target.

Ok, still there ? You're brave.

So the result is my transfer will only work if my sequences are not in synch (regarding the many-to-many association).

I don't believe this the right behavior to change the loadedPersister that way, am I right ? Which makes me think this might be a bug on cascade save-update ..

Any ideas, you experts ? Please, I would definitely appreciate a little help on this, it's driving me crazy..

Of course I can give you more info if you need it.

Please help me out.


Regards,

Gregory


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 30, 2005 10:54 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
I have no idea what you are trying to communicate here.

The only thing I notice is that you are using save() when you should be using replicate().


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 30, 2005 11:16 am 
Newbie

Joined: Thu Jul 21, 2005 5:37 am
Posts: 9
Location: Paris
First of all, thanks Gavin for considering my problem.

As far as I understand it, I cannot use replicate because in that case Hibernate will not generate the id, but rather use the one coming from the source Session. Which means that I will have conflicts if I try to transfer subsequently some objects from two different sources Session in the same target Session.

In other words

source S1 :
table B : B_ID =1
table D : D_ID =1
table A_D : B_ID =1, D_ID = 1

Now I transfer these objects in my target using replicate():
The target database now contains the same data.

But It will fail if after that I'll try to transfer some other objects from another source, which may use the same auto-generated id value
source S2 :
table B : B_ID =1 //duplicate in target
table D : D_ID =1 //duplicate in target
and table A_D [B_ID =1, D_ID = 1] //duplicate in target

So I guess my only choice is to use save(), am I right ?
Or have I lost it...


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