From 12207f51395653069f27e382f2aa6c492a92f7c8 Mon Sep 17 00:00:00 2001 From: Anders Wallgren Date: Sun, 19 Nov 2017 19:59:41 -0800 Subject: [PATCH] HHH-12105 - Batch order_inserts: flush during transaction causes incorrect insert ordering and subsequent constraint violation (cherry picked from commit 7a32ea62b610e3a0fb4580471f37730791d452c7) --- .../org/hibernate/engine/spi/ActionQueue.java | 6 +- ...WithBidirectionalOneToOneFlushProblem.java | 139 ++++++++++++++++++ 2 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/test/insertordering/InsertOrderingWithBidirectionalOneToOneFlushProblem.java diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java index 96e33dfe52ba..c89d85b4bad0 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/ActionQueue.java @@ -1089,7 +1089,11 @@ boolean hasAnyChildEntityNames(BatchIdentifier batchIdentifier) { * @return This {@link BatchIdentifier} has a parent matching the given {@link BatchIdentifier reference */ boolean hasParent(BatchIdentifier batchIdentifier) { - return parent == batchIdentifier || parent != null && parent.hasParent( batchIdentifier ); + return ( + parent == batchIdentifier || + ( parent != null && parent.hasParent( batchIdentifier ) ) || + ( parentEntityNames.contains( batchIdentifier.getEntityName() ) ) + ); } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/insertordering/InsertOrderingWithBidirectionalOneToOneFlushProblem.java b/hibernate-core/src/test/java/org/hibernate/test/insertordering/InsertOrderingWithBidirectionalOneToOneFlushProblem.java new file mode 100644 index 000000000000..306a4d852cd2 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/insertordering/InsertOrderingWithBidirectionalOneToOneFlushProblem.java @@ -0,0 +1,139 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or . + */ +package org.hibernate.test.insertordering; + +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.junit.Test; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.ManyToOne; +import javax.persistence.OneToMany; +import javax.persistence.OneToOne; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.hibernate.cfg.AvailableSettings.ORDER_INSERTS; +import static org.hibernate.cfg.AvailableSettings.STATEMENT_BATCH_SIZE; +import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; + +@TestForIssue(jiraKey = "HHH-12105") +public class InsertOrderingWithBidirectionalOneToOneFlushProblem + extends BaseNonConfigCoreFunctionalTestCase { + + @Test + public void testInsertSortingWithFlushPersistLeftBeforeRight() { + doInHibernate( + this::sessionFactory, + session -> { + TopEntity top1 = new TopEntity(); + + session.persist(top1); + session.flush(); + + LeftEntity left = new LeftEntity(); + RightEntity right = new RightEntity(); + TopEntity top2 = new TopEntity(); + + top1.lefts.add(left); + left.top = top1; + top1.rights.add(right); + right.top = top1; + + // This one-to-one triggers the problem + right.left = left; + + // If you persist right before left the problem goes away + session.persist(left); + session.persist(right); + session.persist(top2); + } + ); + } + + @Test + public void testInsertSortingWithFlushPersistRightBeforeLeft() { + doInHibernate( + this::sessionFactory, + session -> { + TopEntity top1 = new TopEntity(); + + session.persist(top1); + session.flush(); + + LeftEntity left = new LeftEntity(); + RightEntity right = new RightEntity(); + TopEntity top2 = new TopEntity(); + + top1.lefts.add(left); + left.top = top1; + top1.rights.add(right); + right.top = top1; + + // This one-to-one triggers the problem + right.left = left; + + // If you persist right before left the problem goes away + session.persist(right); + session.persist(left); + session.persist(top2); + } + ); + } + + @Override + protected void addSettings(Map settings) { + settings.put( ORDER_INSERTS, "true" ); + settings.put( STATEMENT_BATCH_SIZE, "10" ); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{ + LeftEntity.class, RightEntity.class, TopEntity.class, + }; + } + + @Entity(name = "LeftEntity") + public static class LeftEntity { + @GeneratedValue + @Id + private Long id; + + @ManyToOne + private TopEntity top; + } + + @Entity(name = "RightEntity") + public static class RightEntity { + @GeneratedValue + @Id + private Long id; + + @ManyToOne + private TopEntity top; + + @OneToOne + private LeftEntity left; + } + + @Entity(name = "TopEntity") + public static class TopEntity { + @GeneratedValue + @Id + private Long id; + + @OneToMany(mappedBy = "top") + private List rights = new ArrayList<>(); + + @OneToMany(mappedBy = "top") + private List lefts = new ArrayList<>(); + } +}