Skip to content

Commit b486471

Browse files
committed
PR ebean-orm#3148 - FIX: Recursive batch flush can save beans in wrong order
1 parent 6cd356c commit b486471

3 files changed

Lines changed: 66 additions & 16 deletions

File tree

ebean-core/src/main/java/io/ebeaninternal/server/persist/BatchControl.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,14 +224,21 @@ private void flushPstmtHolder(boolean reset) throws BatchedSqlException {
224224
* Execute all the requests contained in the list.
225225
*/
226226
void executeNow(ArrayList<PersistRequest> list) throws BatchedSqlException {
227-
for (int i = 0; i < list.size(); i++) {
228-
if (i % batchSize == 0) {
229-
// hit the batch size so flush
230-
flushPstmtHolder();
227+
boolean old = transaction.isFlushOnQuery();
228+
transaction.setFlushOnQuery(false);
229+
// disable flush on query due transsaction callbacks
230+
try {
231+
for (int i = 0; i < list.size(); i++) {
232+
if (i % batchSize == 0) {
233+
// hit the batch size so flush
234+
flushPstmtHolder();
235+
}
236+
list.get(i).executeNow();
231237
}
232-
list.get(i).executeNow();
238+
flushPstmtHolder();
239+
} finally {
240+
transaction.setFlushOnQuery(old);
233241
}
234-
flushPstmtHolder();
235242
}
236243

237244
public void flushOnCommit() throws BatchedSqlException {

ebean-test/src/test/java/org/tests/batchinsert/TestBatchInsertFlush.java

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,62 @@
11
package org.tests.batchinsert;
22

3-
import io.ebean.*;
4-
import io.ebean.xtest.BaseTestCase;
5-
import io.ebean.xtest.IgnorePlatform;
3+
import io.ebean.DB;
4+
import io.ebean.Database;
5+
import io.ebean.Transaction;
66
import io.ebean.annotation.PersistBatch;
77
import io.ebean.annotation.Platform;
88
import io.ebean.annotation.Transactional;
99
import io.ebean.meta.MetaTimedMetric;
1010
import io.ebean.meta.ServerMetrics;
1111
import io.ebean.test.LoggedSql;
12+
import io.ebean.xtest.BaseTestCase;
13+
import io.ebean.xtest.IgnorePlatform;
1214
import io.ebean.xtest.base.DtoQuery2Test;
1315
import io.ebeaninternal.api.SpiTransaction;
1416
import org.junit.jupiter.api.Test;
15-
import org.tests.model.basic.Customer;
16-
import org.tests.model.basic.EBasicVer;
17-
import org.tests.model.basic.TSDetail;
18-
import org.tests.model.basic.TSMaster;
17+
import org.tests.model.basic.*;
1918

2019
import java.sql.Timestamp;
20+
import java.util.ArrayList;
2121
import java.util.List;
2222

2323
import static org.assertj.core.api.Assertions.assertThat;
2424
import static org.junit.jupiter.api.Assertions.assertNotNull;
2525

2626
public class TestBatchInsertFlush extends BaseTestCase {
2727

28+
@Test
29+
public void batchFlush() {
30+
31+
TSMaster m = new TSMaster();
32+
m.setName("master1");
33+
DB.save(m);
34+
DB.getDefault().cacheManager().clearAll();
35+
36+
try (Transaction txn = DB.beginTransaction()) {
37+
txn.setBatchSize(2);
38+
txn.setBatchMode(true);
39+
40+
List<Customer> customers = new ArrayList<>();
41+
42+
for (int i = 0; i < 3; i++) {
43+
Customer customer = ResetBasicData.createCustomer("BatchFlushPreInsert " + i, null, null, 3);
44+
customer.addContact(new Contact("Fred" + i, "Blue"));
45+
customers.add(customer);
46+
}
47+
48+
for (int i = 3; i < 6; i++) {
49+
Customer customer = ResetBasicData.createCustomer("BatchFlushPostInsert " + i, null, null, 3);
50+
customer.addContact(new Contact("Fred" + i, "Blue"));
51+
customers.add(customer);
52+
}
53+
54+
DB.saveAll(customers);
55+
56+
txn.commit();
57+
}
58+
}
59+
2860
@Test
2961
public void no_cascade() {
3062

@@ -70,7 +102,7 @@ public void no_cascade() {
70102
// detail
71103
assertThat(sql.get(3)).contains("insert into t_detail_with_other_namexxxyy");
72104

73-
assertThat(((SpiTransaction)transaction).label()).isEqualTo("TestBatchInsertFlush.no_cascade");
105+
assertThat(((SpiTransaction) transaction).label()).isEqualTo("TestBatchInsertFlush.no_cascade");
74106

75107
} finally {
76108
transaction.end();
@@ -133,7 +165,7 @@ public void transactional_flushOnDtoQuery() {
133165

134166
// trigger JDBC batch by default
135167
DB.findDto(DtoQuery2Test.DCust.class, "select id, name from o_customer")
136-
.findList();
168+
.findList();
137169

138170
List<String> sql = LoggedSql.stop();
139171
assertSql(sql.get(0)).contains("insert into e_basicver");

ebean-test/src/test/java/org/tests/model/basic/event/CustomerPersistAdapter.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package org.tests.model.basic.event;
22

3+
import io.ebean.DB;
34
import io.ebean.event.BeanPersistAdapter;
45
import io.ebean.event.BeanPersistRequest;
56
import org.tests.model.basic.Customer;
7+
import org.tests.model.basic.TSMaster;
68

79
public class CustomerPersistAdapter extends BeanPersistAdapter {
810

@@ -16,7 +18,9 @@ public boolean preInsert(BeanPersistRequest<?> request) {
1618

1719
// StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
1820
// request.getTransaction().log("+++++ "+Arrays.toString(stackTrace));
19-
21+
if (((Customer) request.bean()).getName().startsWith("BatchFlushPreInsert")) {
22+
DB.find(TSMaster.class).where().eq("name", "master1").exists();
23+
}
2024
return true;
2125
}
2226

@@ -28,4 +32,11 @@ public boolean preUpdate(BeanPersistRequest<?> request) {
2832
return true;
2933
}
3034

35+
@Override
36+
public void postInsert(BeanPersistRequest<?> request) {
37+
super.postInsert(request);
38+
if (((Customer) request.bean()).getName().startsWith("BatchFlushPostInsert")) {
39+
DB.find(TSMaster.class).where().eq("name", "master1").exists();
40+
}
41+
}
3142
}

0 commit comments

Comments
 (0)