Hello,
I better give a short summary at first. My app is a comlex xml->database importer. The mass data consists of computer systems (class CSystem) and their properties (class Property). I extracted 4 classes to isolate my problem, incl. a testcase.
First I delete all CSystem's in my database. Then I import some dummy data (to avoid the xml-parsing). Works very fine in my development env which uses MySQL. In production the same code throws a PropertyValueException: not-null property references a null or transient value: bug.Property.system.
But my logfile shows that the about2be saved CSystem has Property-References and every property has a link to CSystem.
Hibernate version: Hibernate 2.1.8
Mapping documents:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping SYSTEM "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping package="bug">
<class name="CSystem" table="tSystem" lazy="false">
<id name="ip" column="ip">
<generator class="assigned"/>
</id>
....
<set name="zustandsSet" inverse="true" lazy="true" cascade="all-delete-orphan">
<key column="ip"></key>
<one-to-many class="Property" />
</set>
</class>
<class name="Property" table="tZustand" lazy="false">
<id name="id" column="zustand_id">
<generator class="native">
<param name="sequence">ZUSTAND_ID_SEQ</param>
</generator>
</id>
<many-to-one name="system" class="CSystem"
column="IP" not-null="true"/>
....
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():
Code:
Session session = HibernateUtil.currentSession();
Transaction hibernateTransaction = session.beginTransaction();
session.delete("from CSystem sys where sys.ip like '192.168.001.%'");
hibernateTransaction.commit();
//Create some Systems with Properties
Vector v = createDummyData();
//.... FOR EACH system IN v
session.save(system);
Full stack trace of any exception that occurs:Code:
net.sf.hibernate.PropertyValueException: not-null property references a null or transient value: bug.Property.system
at net.sf.hibernate.impl.SessionImpl.checkNullability(SessionImpl.java:1287)
at net.sf.hibernate.impl.SessionImpl.doSave(SessionImpl.java:939)
at net.sf.hibernate.impl.SessionImpl.doSave(SessionImpl.java:868)
at net.sf.hibernate.impl.SessionImpl.saveWithGeneratedIdentifier(SessionImpl.java:790)
at net.sf.hibernate.impl.SessionImpl.save(SessionImpl.java:749)
at net.sf.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:1398)
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.impl.SessionImpl.doSave(SessionImpl.java:962)
at net.sf.hibernate.impl.SessionImpl.doSave(SessionImpl.java:868)
at net.sf.hibernate.impl.SessionImpl.saveWithGeneratedIdentifier(SessionImpl.java:790)
at net.sf.hibernate.impl.SessionImpl.save(SessionImpl.java:749)
at bug.ImporterTest.secImport(ImporterTest.java:54)
at bug.ImporterTest.testImporter(ImporterTest.java:75)
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 junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
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)
Name and version of the database you are using:
Oracle9i Enterprise Edition Release 9.2.0.5.0 - 64bit Production
Debug level Hibernate log excerpt:
DEBUG saving system: bug.CSystem@1b66aff[ip=192.168.001.001,untersucht=true,status=0]
with Properties:
1:bug.Property@54782a[id=<null>,quelle=A,feld=A,text=xmuster,klassifizierung=4,zeitstempel=Fri Apr 01 10:52:10 CEST 2005,transaktionen=4711,system=192.168.001.001]
2:bug.Property@19d2f6b[id=<null>,quelle=A,feld=B,text=700,klassifizierung=1,zeitstempel=Fri Apr 01 10:52:10 CEST 2005,transaktionen=4711,system=192.168.001.001]
at bug.ImporterTest.secImport(ImporterTest.java:53)
DEBUG generated identifier: 192.168.001.001 at net.sf.hibernate.impl.SessionImpl.saveWithGeneratedIdentifier(SessionImpl.java:789)
DEBUG saving [bug.CSystem#192.168.001.001] at net.sf.hibernate.impl.SessionImpl.doSave(SessionImpl.java:836)
DEBUG calling onSave() at net.sf.hibernate.impl.SessionImpl.doSave(SessionImpl.java:861)
DEBUG processing cascades for: bug.CSystem at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:497)
DEBUG done processing cascades for: bug.CSystem at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:506)
DEBUG Wrapped collection in role: bug.CSystem.zustandsSet at net.sf.hibernate.impl.WrapVisitor.processArrayOrNewCollection(WrapVisitor.java:81)
DEBUG processing cascades for: bug.CSystem at net.sf.hibernate.engine.Cascades.cascade(Cascades.java:497)
DEBUG cascading to collection: bug.CSystem.zustandsSet at net.sf.hibernate.engine.Cascades.cascadeCollection(Cascades.java:524)
DEBUG cascading to saveOrUpdate() at net.sf.hibernate.engine.Cascades$4.cascade(Cascades.java:113)
DEBUG saveOrUpdate() unsaved instance at net.sf.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:1397)
DEBUG about to open: 0 open PreparedStatements, 0 open ResultSets at net.sf.hibernate.impl.BatcherImpl.logOpenPreparedStatement(BatcherImpl.java:204)
DEBUG select ZUSTAND_ID_SEQ.nextval from dual at net.sf.hibernate.impl.BatcherImpl.log(BatcherImpl.java:230)
DEBUG preparing statement at net.sf.hibernate.impl.BatcherImpl.getPreparedStatement(BatcherImpl.java:253)
DEBUG Sequence identifier generated: 974 at net.sf.hibernate.id.SequenceGenerator.generate(SequenceGenerator.java:76)
DEBUG done closing: 0 open PreparedStatements, 0 open ResultSets at net.sf.hibernate.impl.BatcherImpl.logClosePreparedStatement(BatcherImpl.java:211)
DEBUG closing statement at net.sf.hibernate.impl.BatcherImpl.closePreparedStatement(BatcherImpl.java:275)
DEBUG generated identifier: 974 at net.sf.hibernate.impl.SessionImpl.saveWithGeneratedIdentifier(SessionImpl.java:789)
DEBUG saving [bug.Property#974] at net.sf.hibernate.impl.SessionImpl.doSave(SessionImpl.java:836)
DEBUG getSystem: bug.Property@54782a[id=974,quelle=A,feld=A,text=xmuster,klassifizierung=4,zeitstempel=Fri Apr 01 10:52:10 CEST 2005,transaktionen=4711,system=192.168.001.001]
->bug.CSystem@1b66aff[ip=192.168.001.001,untersucht=true,status=0] at bug.Property.getSystem(Property.java:132)
ERROR Error! at bug.ImporterTest.testImporter(ImporterTest.java:79)
net.sf.hibernate.PropertyValueException: not-null property references a null or transient value: bug.Property.system
Here are my examination results so far:
(today is the 3rd day of frustration)
The error only occurs if there are persisted objects in the database. If I do commit after the testcase (deletes are 'persistet') everything goes fine.
Using the debugger (logs didn't show me an error) I found out following, all in net.sf.hibernate.impl.SessionImpl while saving my first Property object.
On line 938 nullifyTransientReferences(values, types, useIdentityColumn, object) is called. This method calls isUnsaved() in line 1073. The return statement (return e.status==SAVING || (earlyInsert ? !e.existsInDatabase : nullifiables.contains( new Key (e.id, e.persister) ));) evaluates to true because nullifiables contains my parent CSystem object. This true causes Property.system to be set to null (line 1047 in nullifyTransientReferences()).
So my objects are (IMHO) clean until they are saved, then the reference from Property to CSystem is cleared in nullifyTransientReferences(), I think because I deleted the old persisted ones in the database (that should be the time they get recorded in 'nullifiables'.
Why does this happen while using oracle, and not while using mysql?
I would be very happy if someone has a clue what goes wrong, which would prevent my computer get shot because of frustration... :-(
(My mind forms the word bug, but I won't feel blamed if I'm just stupid)
-Sascha Spiekermann