Hibernate version: 3.2.4
Description of problem:
We have to use primary keys that are NOT null when a new instance of our data class is created (please don't ask why this is the case, we HAVE to do that as most of the system relies on that fact and we don't use String, Long etc. but are own "atom" types that work as a singleton with metadata).
When we load an instance from the database, we get a proxy on a relationship property even if the FK attribute is null.
We debugged through the code and found out that during hydration the primary key of the referenced entity is investiaged. As the PK of this referenced class is not null after instanciation, Hibernate guesses that it has to create a proxy for that instance.
This results in many error e.g. when deleting the parent object or when trying to simply compare the property with null (like in the code above).
Example:
MyData has properties id (PK), otherData (ref to MyOtherData) and otherDataId (FK).
MyOtherData has id (PK), data (ref back to MyData).
When loading an instance of MyData that has NO relation to MyOtherData set, we get a proxy on MyData.otherData even if MyData.otherDataId==null!!!!
Mapping documents:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate//Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-access="field">
<class name="data.MyData" table="MY_DATA">
<id name="id" access="field" unsaved-value="undefined">
<column name="ID" length="24"/>
<type name="data.MyStringType"/>
<generator class="data.MyGenerator"/>
</id>
<many-to-one name="otherData" class="data.MyOtherData" access="field" cascade="save-update">
<!-- One2One FK #1 of 1 -->
<column name="MY_OTHER_DATA_ID" length="24"/>
</many-to-one>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():
MyData myFetchedData = (MyData)HibernateUtil.getCurrentSession().load(MyData.class, myDataId);
System.out.println("MyData: id=" + myFetchedData.getId());
System.out.println(" ref to MyOtherData: isNull=" + (myFetchedData.getOtherData()==null));
Full stack trace of any exception that occurs:
org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [data.MyOtherData#null]
at org.hibernate.impl.SessionFactoryImpl$1.handleEntityNotFound(SessionFactoryImpl.java:377)
at org.hibernate.proxy.AbstractLazyInitializer.checkTargetState(AbstractLazyInitializer.java:79)
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:72)
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111)
at org.hibernate.engine.StatefulPersistenceContext.unproxyAndReassociate(StatefulPersistenceContext.java:568)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:66)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.engine.CascadingAction$1.cascade(CascadingAction.java:218)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:131)
at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:122)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at data.HibernateUtil.commit(HibernateUtil.java:26)
at MyDataTest.testSimple(MyDataTest.java:23)
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:585)
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.junit3.JUnit3TestReference.run(JUnit3TestReference.java:128)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Name and version of the database you are using:
HSQL, Oracle, MySQL -> error is not related to DB
The generated SQL (show_sql=true):
there shouldn't be any sql query (see comments later on)
|