diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java index 4b8e3d8dcd..62c0ce7c21 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/loadcontext/DLoadBeanContext.java @@ -227,7 +227,7 @@ public void loadBean(EntityBeanIntercept ebi) { // re-add to the batch and lazy load from DB skipping l2 cache if (loadingStarted.get()) { if (CoreLog.markedAsDeleted.isLoggable(DEBUG)) { - CoreLog.markedAsDeleted.log(DEBUG, "Adding " + ebi + "to batch " + this + "after loadingStarted(2) ", new RuntimeException("Adding to batch after load(2")); + CoreLog.markedAsDeleted.log(DEBUG, "Adding " + ebi + "to batch " + this + "after loadingStarted(2) ", new RuntimeException("Adding to batch after load(2)")); } } batch.add(ebi); @@ -239,9 +239,31 @@ public void loadBean(EntityBeanIntercept ebi) { return; } } - - context.desc.ebeanServer().loadBean(new LoadBeanRequest(this, ebi, context.hitCache)); - batch.clear(); + // ensure, that every bean in the batch is in the persistence context. + // this may happen, when bean was previously deleted, but the result is not yet committed. + List reincarnatedIds = null; + for (EntityBeanIntercept batchEbi : batch) { + Object id = context.desc.getId(batchEbi.owner()); + if (id != null && context.desc.contextPutIfAbsent(persistenceContext, id, batchEbi.owner()) == null) { + if (reincarnatedIds == null) { + reincarnatedIds = new ArrayList<>(); + } + reincarnatedIds.add(id); + if (CoreLog.markedAsDeleted.isLoggable(DEBUG)) { + CoreLog.markedAsDeleted.log(DEBUG, "Temporary adding " + ebi + "to persistence context", new RuntimeException("Temporary adding bean to persistence context")); + } + } + } + try { + context.desc.ebeanServer().loadBean(new LoadBeanRequest(this, ebi, context.hitCache)); + batch.clear(); + } finally { + if (reincarnatedIds != null) { + for (Object id : reincarnatedIds) { + context.desc.contextClear(persistenceContext, id); + } + } + } } } diff --git a/ebean-test/src/test/java/io/ebean/xtest/event/BeanPersistControllerTest.java b/ebean-test/src/test/java/io/ebean/xtest/event/BeanPersistControllerTest.java index f90b228ea2..7d635150f2 100644 --- a/ebean-test/src/test/java/io/ebean/xtest/event/BeanPersistControllerTest.java +++ b/ebean-test/src/test/java/io/ebean/xtest/event/BeanPersistControllerTest.java @@ -2,13 +2,15 @@ import io.ebean.Database; +import io.ebean.DatabaseBuilder; import io.ebean.DatabaseFactory; import io.ebean.Transaction; -import io.ebean.DatabaseBuilder; import io.ebean.config.DatabaseConfig; import io.ebean.event.BeanDeleteIdRequest; import io.ebean.event.BeanPersistAdapter; +import io.ebean.event.BeanPersistController; import io.ebean.event.BeanPersistRequest; +import io.ebean.test.LoggedSql; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -139,7 +141,7 @@ public void testInsertUpdateDelete_given_stopPersistingAdapter() { assertThat(stopPersistingAdapter.methodsCalled).containsExactly("preDeleteById"); stopPersistingAdapter.methodsCalled.clear(); - db.deleteAll(EBasicVer.class, Arrays.asList(22,23,24)); + db.deleteAll(EBasicVer.class, Arrays.asList(22, 23, 24)); assertThat(stopPersistingAdapter.methodsCalled).hasSize(3); assertThat(stopPersistingAdapter.methodsCalled).containsExactly("preDeleteById", "preDeleteById", "preDeleteById"); stopPersistingAdapter.methodsCalled.clear(); @@ -202,12 +204,12 @@ public boolean preUpdate(BeanPersistRequest request) { Object bean = request.bean(); if (bean instanceof UTDetail) { - UTDetail detail = (UTDetail)bean; + UTDetail detail = (UTDetail) bean; // invoke lazy loading ... which invoke the flush of the jdbc batch detail.setQty(42); } if (bean instanceof UTMaster) { - UTMaster master = (UTMaster)bean; + UTMaster master = (UTMaster) bean; UTMaster.Journal journal = master.getJournal(); if (journal == null) { journal = new UTMaster.Journal();