From c0efd49c73c67030f29f426db3f25e95d0387694 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Thu, 8 Mar 2018 14:12:32 +1300 Subject: [PATCH] #1341 - Cascade persist with JDBC batch and BeanPersistAdapter with preUpdate() that invokes lazy loading on persisted bean ... results in OptimisticLockException --- .../server/persist/BatchedBeanHolder.java | 15 ++++--- .../event/BeanPersistControllerTest.java | 43 +++++++++++++++++++ 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/ebeaninternal/server/persist/BatchedBeanHolder.java b/src/main/java/io/ebeaninternal/server/persist/BatchedBeanHolder.java index cfe05cd8c..6c974cc56 100644 --- a/src/main/java/io/ebeaninternal/server/persist/BatchedBeanHolder.java +++ b/src/main/java/io/ebeaninternal/server/persist/BatchedBeanHolder.java @@ -85,16 +85,19 @@ public class BatchedBeanHolder { // Note updates and deletes can result in many PreparedStatements // if their where clauses differ via use of IS NOT NULL. if (deletes != null && !deletes.isEmpty()) { - control.executeNow(deletes); - deletes.clear(); + ArrayList bufferedDeletes = deletes; + deletes = new ArrayList<>(); + control.executeNow(bufferedDeletes); } if (inserts != null && !inserts.isEmpty()) { - control.executeNow(inserts); - inserts.clear(); + ArrayList bufferedInserts = inserts; + inserts = new ArrayList<>(); + control.executeNow(bufferedInserts); } if (updates != null && !updates.isEmpty()) { - control.executeNow(updates); - updates.clear(); + ArrayList bufferedUpdates = updates; + updates = new ArrayList<>(); + control.executeNow(bufferedUpdates); } persistedBeans.clear(); } diff --git a/src/test/java/io/ebean/event/BeanPersistControllerTest.java b/src/test/java/io/ebean/event/BeanPersistControllerTest.java index 58d351a1c..e63f77bc3 100644 --- a/src/test/java/io/ebean/event/BeanPersistControllerTest.java +++ b/src/test/java/io/ebean/event/BeanPersistControllerTest.java @@ -3,9 +3,12 @@ package io.ebean.event; import io.ebean.EbeanServer; import io.ebean.EbeanServerFactory; +import io.ebean.Transaction; import io.ebean.config.ServerConfig; import org.junit.Test; import org.tests.model.basic.EBasicVer; +import org.tests.model.basic.UTDetail; +import org.tests.model.basic.UTMaster; import java.util.ArrayList; import java.util.Arrays; @@ -19,6 +22,36 @@ public class BeanPersistControllerTest { private PersistAdapter stopPersistingAdapter = new PersistAdapter(false); + @Test + public void issue_1341() { + + EbeanServer ebeanServer = getEbeanServer(continuePersistingAdapter); + + UTMaster bean0 = new UTMaster("one0"); + UTDetail detail0 = new UTDetail("detail0", 12, 23D); + bean0.getDetails().add(detail0); + + ebeanServer.save(bean0); + + UTMaster master = ebeanServer.find(UTMaster.class) + .setId(bean0.getId()) + .fetch("details", "name, version") + .findOne(); + + UTDetail utDetail = master.getDetails().get(0); + utDetail.setName("detail0 mod"); + + Transaction txn = ebeanServer.beginTransaction(); + try { + txn.setBatchMode(true); + ebeanServer.save(master); + txn.commit(); + } finally { + txn.end(); + } + + } + @Test public void testInsertUpdateDelete_given_continuePersistingAdapter() { @@ -91,6 +124,8 @@ public class BeanPersistControllerTest { config.setRegister(false); config.setDefaultServer(false); config.getClasses().add(EBasicVer.class); + config.getClasses().add(UTMaster.class); + config.getClasses().add(UTDetail.class); config.add(persistAdapter); @@ -130,6 +165,14 @@ public class BeanPersistControllerTest { @Override public boolean preUpdate(BeanPersistRequest request) { methodsCalled.add("preUpdate"); + + Object bean = request.getBean(); + if (bean instanceof UTDetail) { + UTDetail detail = (UTDetail)bean; + // invoke lazy loading ... which invoke the flush of the jdbc batch + detail.setQty(42); + } + return continueDefaultPersisting; }