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