With hibernate.order_inserts=true Hibernate executes SQL inserts in wrong order

Description

General:
With hibernate.order_inserts configuration property set to true Hibernate executes SQL inserts in wrong order, so that dependent entities are attempted to be inserted before parent entities. This is a production case, so we had to switch off hibernate insert ordering to workaround the problem.

Test case:
Test case for reproduce is attached (jpatest.zip). It uses hibernate-entitymanager 3.4.0.GA (hibernate-core 3.3.0 SP1), hsqldb 2.0.0. The problem is also reproducible in later versions of hibernate-core (3.3.1, 3.3.2, 3.6.0.Beta2).

Test case is a typical maven2 project.

In order to run the test case:

  • unzip the archive to any folder

  • cd jpatest

  • mvn test

If change hibernate.order_inserts to false in /jpatest/src/main/resources/META-INF/persistence.xml, then the test passes.

Mappings are located under \jpatest\src\main\resources\META-INF\mappings.hbm.xml

Data model:
[Person] 1 – * [Phone]
[Person] 1 – * [Relation] * – 1 [Person]

Scenario:
1) Insert plain person P1.
2) Update person P1,
– add new phone PH1 to person P1
– add new relation R1 to person P1. Relation R1 refers to a new person P2 having new phone PH2 Update of person P1 generates insert for phone PH1 and cascade inserts for P2, PH2 and R1.

Once inserts are ordered, insert clauses for phones PH1 and PH2 come first. As insert for PH2 is executed before the insert of its parent entity P2, the whole operation fails due to parent key not found.

With Oracle DB it results in java.sql.BatchUpdateException: ORA-02291: integrity constraint (PHONE_PERSON_FK) violated - parent key not found
With HSQLDB - java.sql.BatchUpdateException: integrity constraint violation: foreign key no parent; PHONE_PERSON_FK table: PHONE

Problematic code
org.hibernate.engine.ActionQueue.InsertActionSorter.findBatchNumber(), uses only property types to determine batch number, but in given case cascade goes through composite primary key of Phone, so the code should also deep-traverse action.getPersister().getClassMetadata().getIdentifierType().

Another problem
When I tried to change phone collection mapping in Person entity from <map> to <set> (with hope to workaround the issue), then I got another error:

java.lang.NullPointerException
at org.hibernate.type.AbstractType.getHashCode(AbstractType.java:136)
at org.hibernate.type.AbstractType.getHashCode(AbstractType.java:144)
at org.hibernate.type.EntityType.getHashCode(EntityType.java:312)
at org.hibernate.type.ComponentType.getHashCode(ComponentType.java:212)
at org.hibernate.engine.EntityKey.generateHashCode(EntityKey.java:126)
at org.hibernate.engine.EntityKey.<init>(EntityKey.java:70)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:184)
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.DefaultPersistEventListener.entityIsPersistent(DefaultPersistEventListener.java:134)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:107)
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)
at com.test.AppTest.testJPA(AppTest.java:77)
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:597)
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 org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Please pay attention to it as well.

Environment

Initially occurred with hibernate-core 3.3.1, Oracle 11g.
Reproduced with hibernate-core 3.3.0 SP1 (and later) and HSQLDB 2.0.0

Status

Assignee

Unassigned

Reporter

Oleg Tsernetsov

Fix versions

None

Labels

None

backPortable

None

Suitable for new contributors

None

Requires Release Note

None

Pull Request

None

backportDecision

None

Components

Affects versions

Priority

Major
Configure