Skip to content

Commit 791a3a5

Browse files
committed
HHH-9864 - foreign key violation with order_inserts=true
(cherry picked from commit 8916346)
1 parent ca876f2 commit 791a3a5

File tree

1 file changed

+220
-0
lines changed

1 file changed

+220
-0
lines changed
Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.test.insertordering;
8+
9+
import java.sql.PreparedStatement;
10+
import java.util.ArrayList;
11+
import java.util.HashSet;
12+
import java.util.List;
13+
import java.util.Map;
14+
import java.util.Set;
15+
import javax.persistence.Access;
16+
import javax.persistence.AccessType;
17+
import javax.persistence.CascadeType;
18+
import javax.persistence.Column;
19+
import javax.persistence.DiscriminatorColumn;
20+
import javax.persistence.DiscriminatorType;
21+
import javax.persistence.DiscriminatorValue;
22+
import javax.persistence.Entity;
23+
import javax.persistence.GeneratedValue;
24+
import javax.persistence.GenerationType;
25+
import javax.persistence.Id;
26+
import javax.persistence.Inheritance;
27+
import javax.persistence.InheritanceType;
28+
import javax.persistence.JoinColumn;
29+
import javax.persistence.OneToMany;
30+
import javax.persistence.SequenceGenerator;
31+
import javax.persistence.Table;
32+
33+
import org.hibernate.Session;
34+
import org.hibernate.annotations.BatchSize;
35+
import org.hibernate.cfg.Environment;
36+
import org.hibernate.engine.jdbc.batch.internal.BatchBuilderImpl;
37+
import org.hibernate.engine.jdbc.batch.internal.BatchBuilderInitiator;
38+
import org.hibernate.engine.jdbc.batch.internal.BatchingBatch;
39+
import org.hibernate.engine.jdbc.batch.spi.Batch;
40+
import org.hibernate.engine.jdbc.batch.spi.BatchKey;
41+
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
42+
43+
import org.hibernate.testing.FailureExpected;
44+
import org.hibernate.testing.TestForIssue;
45+
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
46+
import org.junit.Test;
47+
48+
import static org.junit.Assert.assertEquals;
49+
50+
/**
51+
* @author Steve Ebersole
52+
*/
53+
@TestForIssue( jiraKey = "HHH-9864" )
54+
public class InsertOrderingWithInheritance extends BaseNonConfigCoreFunctionalTestCase {
55+
@Test
56+
@FailureExpected( jiraKey = "HHH-9864" )
57+
public void testBatchOrdering() {
58+
Session session = openSession();
59+
session.getTransaction().begin();
60+
61+
// First object with dependent object (address)
62+
final Person person = new Person();
63+
person.addAddress(new Address());
64+
session.persist(person);
65+
66+
// Derived Object with dependent object (address)
67+
final SpecialPerson specialPerson = new SpecialPerson();
68+
specialPerson.addAddress(new Address());
69+
session.persist( specialPerson );
70+
71+
session.getTransaction().commit();
72+
session.close();
73+
74+
cleanupData();
75+
}
76+
77+
@Test
78+
@FailureExpected( jiraKey = "HHH-9864" )
79+
public void testBatchingAmongstSubClasses() {
80+
StatsBatch.reset();
81+
Session session = openSession();
82+
session.getTransaction().begin();
83+
int iterations = 12;
84+
for ( int i = 0; i < iterations; i++ ) {
85+
final Person person = new Person();
86+
person.addAddress( new Address() );
87+
session.persist( person );
88+
89+
final SpecialPerson specialPerson = new SpecialPerson();
90+
specialPerson.addAddress(new Address());
91+
session.persist( specialPerson );
92+
}
93+
StatsBatch.reset();
94+
session.getTransaction().commit();
95+
session.close();
96+
97+
// 2 batches of Person
98+
// 2 batches of SpecialPerson
99+
// 2 batches of Address
100+
assertEquals( 6, StatsBatch.batchSizes.size() );
101+
102+
cleanupData();
103+
}
104+
105+
private void cleanupData() {
106+
Session session = openSession();
107+
session.getTransaction().begin();
108+
session.createQuery( "delete Address" ).executeUpdate();
109+
session.createQuery( "delete Person" ).executeUpdate();
110+
session.getTransaction().commit();
111+
session.close();
112+
}
113+
114+
@Override
115+
protected Class[] getAnnotatedClasses() {
116+
return new Class[] { Address.class, Person.class, SpecialPerson.class };
117+
}
118+
119+
@Override
120+
protected void addSettings(Map settings) {
121+
super.addSettings( settings );
122+
settings.put( Environment.ORDER_INSERTS, "true" );
123+
settings.put( Environment.STATEMENT_BATCH_SIZE, "10" );
124+
settings.put( BatchBuilderInitiator.BUILDER, StatsBatchBuilder.class.getName() );
125+
}
126+
127+
@Entity(name = "Address")
128+
@Table(name = "ADDRESS")
129+
@Access(AccessType.FIELD)
130+
public static class Address {
131+
@Id
132+
@Column(name = "ID", nullable = false)
133+
@SequenceGenerator(name = "ID", sequenceName = "ADDRESS_SEQ")
134+
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ID")
135+
private Long id;
136+
}
137+
138+
@Entity( name = "Person" )
139+
@Access(AccessType.FIELD)
140+
@Table(name = "PERSON")
141+
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
142+
@DiscriminatorColumn(name = "CLASSINDICATOR", discriminatorType = DiscriminatorType.INTEGER)
143+
@DiscriminatorValue("1")
144+
public static class Person {
145+
@Id
146+
@Column(name = "ID", nullable = false)
147+
@SequenceGenerator(name = "ID", sequenceName = "PERSON_SEQ")
148+
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ID")
149+
private Long id;
150+
151+
@OneToMany(orphanRemoval = true, cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
152+
@JoinColumn(name = "PERSONID", referencedColumnName = "ID", nullable = false, updatable = false)
153+
@BatchSize(size = 100)
154+
private Set<Address> addresses = new HashSet<Address>();
155+
156+
public void addAddress(Address address) {
157+
this.addresses.add(address);
158+
}
159+
160+
}
161+
162+
@Entity
163+
@Access(AccessType.FIELD)
164+
@DiscriminatorValue("2")
165+
public static class SpecialPerson extends Person {
166+
@Column(name = "special")
167+
private String special;
168+
}
169+
170+
public static class Counter {
171+
public int count = 0;
172+
}
173+
174+
public static class StatsBatch extends BatchingBatch {
175+
private static String batchSQL;
176+
private static List batchSizes = new ArrayList();
177+
private static int currentBatch = -1;
178+
179+
public StatsBatch(BatchKey key, JdbcCoordinator jdbcCoordinator, int jdbcBatchSize) {
180+
super( key, jdbcCoordinator, jdbcBatchSize );
181+
}
182+
183+
static void reset() {
184+
batchSizes = new ArrayList();
185+
currentBatch = -1;
186+
batchSQL = null;
187+
}
188+
189+
@Override
190+
public PreparedStatement getBatchStatement(String sql, boolean callable) {
191+
if ( batchSQL == null || ! batchSQL.equals( sql ) ) {
192+
currentBatch++;
193+
batchSQL = sql;
194+
batchSizes.add( currentBatch, new Counter() );
195+
}
196+
return super.getBatchStatement( sql, callable );
197+
}
198+
199+
@Override
200+
public void addToBatch() {
201+
Counter counter = ( Counter ) batchSizes.get( currentBatch );
202+
counter.count++;
203+
super.addToBatch();
204+
}
205+
}
206+
207+
public static class StatsBatchBuilder extends BatchBuilderImpl {
208+
private int jdbcBatchSize;
209+
210+
@Override
211+
public void setJdbcBatchSize(int jdbcBatchSize) {
212+
this.jdbcBatchSize = jdbcBatchSize;
213+
}
214+
215+
@Override
216+
public Batch buildBatch(BatchKey key, JdbcCoordinator jdbcCoordinator) {
217+
return new StatsBatch( key, jdbcCoordinator, jdbcBatchSize );
218+
}
219+
}
220+
}

0 commit comments

Comments
 (0)