Hibernate version: 2.1.7
Name and version of the database you are using:PostgreSQL 8.0.0beta5
Mapping documents:
Code:
<class name="SDO" table="SDO_table">
<id name="oid" column="object_id" type="long">
<generator class="OidGenerator"/>
</id>
<joined-subclass name="User" table="user_table">
<key column="object_id"/>
<property name="name" type="string" unique="true"/>
</joined-subclass>
<joined-subclass name="Foo" table="foo_table">
<key column="object_id"/>
<property name="name" type="string" unique="true"/>
</joined-subclass>
<joined-subclass name="Task" table="task_table">
<key column="object_id"/>
<many-to-one name="foo" cascade="save-update"/>
<many-to-one name="aUser" cascade="save-update">
<column name="user_id" index="user_indx"/>
</many-to-one>
</joined-subclass>
</class>
Intentions:- implement an identifier generator that accomplishes:
o objects without an identifier get one when saved.
o not saved yet objects, with their identifier already set, preserve this id when saved. No attemp for an update operation should occur in this case.
o Update operations would apply only for objects with their id set (as expected)
- Make sure all cascading take place as expected wherever you save or update an object.
Identifier generator class:o OidGenerator class implements PersistentIdentifierGenerator and Configurable
o It’s relevant to mention only parts from the generate method from this class:
Code:
public Serializable generate(SessionImplementor session,
Object obj) throws SQLException, HibernateException {
// preventing hibernate from setting a different oid
// when this exists already
SDO sdo = (SDO) obj;
if (sdo != null && sdo.getOid() != null)
return sdo.getOid();
// otherwise generate one
Long result = …;
return result;
}
Now come discussions about 2 cases handling the previous data: a JUnit test and a bean hosted by a JBoss 3.2.1 server with container managed transactions:calls and behavior within a JUnit test:
Code:
Task t = new Task();
Long newOid = ..;
Session session = ..;// becomes an instance of net.sf.hibernate.SessionImpl
Transaction tx = session.beginTransaction();
// 1: (works)
t.setOid(newOid);
session.save(t); // see the difference from the JBoss section from below
// or 2: (works)
session.save(t, newOid);
// or 3: (fails)
t.setOid(newOid);
session.saveOrUpdateCopy(t);
/* fails with: java.lang.NullPointerException
at java.lang.reflect.Method.invoke(Native Method)
at net.sf.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:96)
…
*/
// or 4: (fails)
session.saveOrUpdateCopy(t, newOid);
/* fails with: java.lang.NullPointerException
at java.lang.reflect.Method.invoke(Native Method)
at net.sf.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:96)
…
*/
tx.commit();
session.flush();
session.close();
calls and behavior while hosted by a JBoss server with container managed transactions:
Code:
Task t = new Task();
Long newOid = ..;
Session session = ..;// becomes an instance of net.sf.hibernate.SessionImpl
User u = …;//loads a certain user within this session
Foo foo = …;//loads a foo within the same session
// 1:
t.setOid(newOid);
session.save(t); // jumps astonishing to !! SessionImpl.saveOrUpdate
// (the stack trace shows this weird jump)
// and performs an !! update
// or 2:
session.save(t, newOid);
// works except persisting the reference to the u:User
// although the reference to the foo:Foo gets correctly
// within the new row of task_table
// I didn’t try saveOrUpdateCopy since it didn’t work within JUnit test
session.flush();
session.close();
I’d appreciate some directions for the following issues:
- how can I prevent session.save(Obj) jumping directly to SessionImpl.saveOrUpdate(Obj) instead of SessionImpl.save(Obj) when hosted by JBoss?
- As session.save(obj) jumps to the undesired session.saveOrUpdate(obj), we must use session.save(obj, newOid). But this doesn’t allow newOid to be null, then each time I want to save an object, I have to call either session.save(obj) – when it doesn’t have an oid, or session.save(obj, oid) – when the oid is not null. What alternatives do I have ?
- Debugging the lost reference to the aUser field of the Task shows that cascading reaches the point: session.saveOrUpdate(aUser) and does nothing, as it correctly observes the object is loaded, not deleted, nor modified. But why is it lost when the mapping for foo:Foo from Task is the same except the indexing that’s applied for aUser? What can be done in this case ?