]
Krasimir Chobantonov updated HHH-3544:
--------------------------------------
Attachment: 3.3.1.GA-HHH-3544
This patch fixes the problem but can be improved so that if it detect dependencies between
the object in the graph that is about to be saved then to nullify only the nullable
foreign key relationships and add an update action to establish the relation after the
insert.
Currently the patch does not do that - it will order all possible dependencies in the
correct order but it will not try to solve cyclic object dependency by trying to nullifiy
the nullable relationships and add an update action to fix the relationship after the
insert.
The original problem indeed is because Hibernate will nullify all references to entities
that are having status of SAVING - meaning those object are going to be inserted in the
dababase but their insert action is not yet scheduled.
Jeppe, would you mind to try to patch and see if it will fix your problem as well ? Of
course the bugfix needs to be reviewed by the Hibernate team member (e.g. a person with
more knowledge of the whole Hibernate code base)
Thanks
Krasimir
Nullability.checkNullability() throws PropertyValueException
(not-null property references a null property) for a property that
ForeignKeys.Nullifier.nullifyTransientReferences() just nulled
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Key: HHH-3544
URL:
http://opensource.atlassian.com/projects/hibernate/browse/HHH-3544
Project: Hibernate Core
Issue Type: Bug
Components: core
Affects Versions: 3.3.1
Environment: Hibernate 3.3.1, Hibernate Annotations 3.4, Hibernate
Commons-Annotation 3.3.1, Hibernate EntityManager 3.4, PostgreSQL 8.2.5, Java 5
Reporter: Jeppe Cramon
Attachments: 3.3.1.GA-HHH-3544, HibernateNullabilityProblem.zip
Nullability.checkNullability throws PropertyValueException (not-null property references
a null property) for a property that ForeignKeys.Nullifier.nullifyTransientReferences
just nulled.
I've included an Eclipse Java project which can reproduce the problem (didn't
include the jars).
The problem requires a pretty big graph to be reproduce able. The file "Domain
Model.jpg" displays the Classes that take part in the problem.
The Domain Model is a simplification of the entities from our application.
In the example, ModelTest.java, there's only one active instance entity of each type
(from A to H).
With the given test case, when an instance of a is passed to entityManager.persist(..),
then the following exception is thrown:
javax.persistence.PersistenceException: org.hibernate.PropertyValueException: not-null
property references a null or transient value: dk.hibernatetest.model.C.b
at
org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:614)
at
org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:226)
at dk.hibernatetest.model.ModelTest.test(ModelTest.java:49)
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
org.junit.internal.runners.TestMethodRunner.executeMethodBody(TestMethodRunner.java:99)
at org.junit.internal.runners.TestMethodRunner.runUnprotected(TestMethodRunner.java:81)
at
org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
at org.junit.internal.runners.TestMethodRunner.runMethod(TestMethodRunner.java:75)
at org.junit.internal.runners.TestMethodRunner.run(TestMethodRunner.java:45)
at
org.junit.internal.runners.TestClassMethodsRunner.invokeTestMethod(TestClassMethodsRunner.java:66)
at
org.junit.internal.runners.TestClassMethodsRunner.run(TestClassMethodsRunner.java:35)
at org.junit.internal.runners.TestClassRunner$1.runUnprotected(TestClassRunner.java:42)
at
org.junit.internal.runners.BeforeAndAfterRunner.runProtected(BeforeAndAfterRunner.java:34)
at org.junit.internal.runners.TestClassRunner.run(TestClassRunner.java:52)
at
org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
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)
Caused by: org.hibernate.PropertyValueException: not-null property references a null or
transient value: dk.hibernatetest.model.C.b
at org.hibernate.engine.Nullability.checkNullability(Nullability.java:95)
at
org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:313)
at
org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at
org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:144)
at
org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
at
org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154)
at
org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:636)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:628)
at org.hibernate.engine.EJB3CascadingAction$1.cascade(EJB3CascadingAction.java:28)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:291)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:239)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:319)
at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:265)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:242)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
at org.hibernate.engine.Cascade.cascade(Cascade.java:153)
at
org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:479)
at
org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:357)
at
org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at
org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:144)
at
org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
at
org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154)
at
org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:636)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:628)
at org.hibernate.engine.EJB3CascadingAction$1.cascade(EJB3CascadingAction.java:28)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:291)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:239)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
at org.hibernate.engine.Cascade.cascade(Cascade.java:153)
at
org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:454)
at
org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288)
at
org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at
org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:144)
at
org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
at
org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154)
at
org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:636)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:628)
at org.hibernate.engine.EJB3CascadingAction$1.cascade(EJB3CascadingAction.java:28)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:291)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:239)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
at org.hibernate.engine.Cascade.cascade(Cascade.java:153)
at
org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:454)
at
org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288)
at
org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at
org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:144)
at
org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
at
org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154)
at
org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:636)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:628)
at org.hibernate.engine.EJB3CascadingAction$1.cascade(EJB3CascadingAction.java:28)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:291)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:239)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
at org.hibernate.engine.Cascade.cascade(Cascade.java:153)
at
org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:454)
at
org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:288)
at
org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at
org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:144)
at
org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
at
org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154)
at
org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:636)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:628)
at org.hibernate.engine.EJB3CascadingAction$1.cascade(EJB3CascadingAction.java:28)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:291)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:239)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:319)
at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:265)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:242)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:192)
at org.hibernate.engine.Cascade.cascade(Cascade.java:153)
at
org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:479)
at
org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:357)
at
org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:204)
at
org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:144)
at
org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:49)
at
org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:154)
at
org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:110)
at
org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:645)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:619)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:623)
at
org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:220)
... 21 more
As noted in TestModel.java, if some of the other entities (namely c, g or h) are passed
to persist(), then the problem doesn't occur.
I've tried to debug the problem and the only difference I've seen inside
ForeignKeys.Nullifier.nullifyTransientReferences() is that, in the cases where an
exception is thrown, then the B instance that C.b points to has the state SAVING, whereas
when the exception isn't thrown, then it has state MANAGED.
When the B instance has state SAVING, then
ForeignKeys.Nullifier.nullifyTransientReferences() nulls then entry in it's value
array.
After that, Nullability.checkNullability(), performs a null check, which fails because
ForeignKeys.Nullifier.nullifyTransientReferences() just nulled the property in the values
array.
Could it be a solution to reverse the order of the
ForeignKeys.Nullifier.nullifyTransientReferences() and Nullability.checkNullability()
(See AbstractEventListener.performSaveOrReplicate(...))
or does ForeignKeys.Nullifier.nullifyTransientReferences() instead have to take
nullability into considerations?
/Jeppe
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: