From 66234249c63edc4733922c80224e5613d13b53fe Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Tue, 9 Jan 2024 07:44:27 +1300 Subject: [PATCH 1/3] #3295 Clear PersistenceContext on execution of bulk updates or deletes Using SqlUpdate or an ORM Update query clear the appropriate part of the PersistenceContext. The effect is that ORM queries executed after a bulk update will effectively load a fresh copy of the data from the database and will not reuse an instance from the persistence context if the bean in question had already been loaded. --- .../server/core/OrmQueryRequest.java | 1 + .../server/core/PersistRequestOrmUpdate.java | 5 +- .../server/core/PersistRequestUpdateSql.java | 11 ++ .../server/deploy/BeanDescriptor.java | 7 + .../server/deploy/BeanDescriptorManager.java | 2 +- .../TestPersistenceContextQueryScope.java | 125 ++++++++++++++++-- 6 files changed, 136 insertions(+), 15 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java index 3dfca446b..830b825f5 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java @@ -338,6 +338,7 @@ public final class OrmQueryRequest extends BeanRequest implements SpiOrmQuery private int notifyCache(int rows, boolean update) { if (rows > 0) { + beanDescriptor.contextClear(transaction.persistenceContext()); beanDescriptor.cacheUpdateQuery(update, transaction); } return rows; diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java index 03aa874f8..4c6771cbb 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java @@ -77,10 +77,13 @@ public final class PersistRequestOrmUpdate extends PersistRequest { */ @Override public void postExecute() { + OrmUpdateType ormUpdateType = ormUpdate.ormUpdateType(); + if (OrmUpdateType.INSERT != ormUpdateType) { + beanDescriptor.contextClear(transaction.persistenceContext()); + } if (startNanos > 0) { persistExecute.collectOrmUpdate(label, startNanos); } - OrmUpdateType ormUpdateType = ormUpdate.ormUpdateType(); String tableName = ormUpdate.baseTable(); if (transaction.isLogSummary()) { transaction.logSummary("{0} table[{1}] rows[{2}] bind[{3}]", ormUpdateType, tableName, rowCount, bindLog); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java index e4f0c023f..dc6d0b5de 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java @@ -3,10 +3,13 @@ package io.ebeaninternal.server.core; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.api.SpiSqlUpdate; import io.ebeaninternal.api.SpiTransaction; +import io.ebeaninternal.server.deploy.BeanDescriptor; import io.ebeaninternal.server.persist.BatchControl; import io.ebeaninternal.server.persist.PersistExecute; import io.ebeaninternal.server.persist.TrimLogSql; +import java.util.List; + /** * Persist request specifically for CallableSql. */ @@ -156,6 +159,14 @@ public final class PersistRequestUpdateSql extends PersistRequest { */ @Override public void postExecute() { + if (sqlType != SqlType.SQL_INSERT) { + List> descriptors = server.descriptors(tableName); + if (descriptors != null) { + for (BeanDescriptor descriptor : descriptors) { + descriptor.contextClear(transaction.persistenceContext()); + } + } + } if (startNanos > 0) { persistExecute.collectSqlUpdate(label, startNanos); } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java index 081ae5d77..dbb803fc9 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java @@ -2056,6 +2056,13 @@ public class BeanDescriptor implements BeanType, STreeType, SpiBeanType { pc.clear(rootBeanType, idValue); } + /** + * Clear a bean from the persistence context. + */ + public void contextClear(PersistenceContext pc) { + pc.clear(rootBeanType); + } + /** * Delete a bean from the persistence context (such that we don't fetch it in the same transaction). */ diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java index 240ab41a2..c4d179e6d 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java @@ -399,7 +399,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy * Return the BeanDescriptors mapped to the table. */ public List> descriptors(String tableName) { - return tableToDescMap.get(tableName.toLowerCase()); + return tableName == null ? Collections.emptyList() : tableToDescMap.get(tableName.toLowerCase()); } /** diff --git a/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java b/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java index 56988bc4b..f82f855d1 100644 --- a/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java +++ b/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java @@ -1,5 +1,6 @@ package org.tests.persistencecontext; +import io.ebean.Transaction; import io.ebean.xtest.BaseTestCase; import io.ebean.DB; import org.junit.jupiter.api.Test; @@ -7,22 +8,20 @@ import org.tests.model.basic.EBasicVer; import static io.ebean.PersistenceContextScope.QUERY; import static io.ebean.PersistenceContextScope.TRANSACTION; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.*; -public class TestPersistenceContextQueryScope extends BaseTestCase { +class TestPersistenceContextQueryScope extends BaseTestCase { @Test - public void test() { + void test() { EBasicVer bean = new EBasicVer("first"); DB.save(bean); //DB.cacheManager().setCaching(EBasicVer.class, true); - DB.beginTransaction(); - try { + try (Transaction txn = DB.beginTransaction()) { EBasicVer bean1 = DB.find(EBasicVer.class, bean.getId()); // do an update of the name in the DB @@ -57,18 +56,118 @@ public class TestPersistenceContextQueryScope extends BaseTestCase { assertEquals("first", bean.getName()); assertEquals("first", bean1.getName()); - assertEquals("first", bean2.getName()); - assertEquals("first", bean5.getName()); - assertSame(bean1, bean2); - assertSame(bean1, bean5); + assertEquals("second", bean2.getName()); + assertEquals("second", bean5.getName()); + assertNotSame(bean1, bean2); + assertNotSame(bean1, bean5); assertEquals("second", bean3.getName()); DB.delete(bean3); - DB.commitTransaction(); + txn.commit(); + } + } - } finally { - DB.endTransaction(); + @Test + void ormUpdateQuery_expect_clearsContext() { + + EBasicVer bean = new EBasicVer("first"); + DB.save(bean); + + try (Transaction txn = DB.beginTransaction()) { + EBasicVer bean1 = DB.find(EBasicVer.class, bean.getId()); + + // do an update of the name in the DB + int rowCount = DB.update(EBasicVer.class) + .set("name", "second") + .where().idEq(bean.getId()) + .update(); + assertEquals(1, rowCount); + + // fetch the bean again... but doesn't hit DB as it + // is in the PersistenceContext which is transaction scoped + EBasicVer bean2 = DB.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .findOne(); + + // QUERY scope hits the DB (doesn't use the existing transactions persistence context) + // ... also explicitly not use bean cache + EBasicVer bean3 = DB.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .setPersistenceContextScope(QUERY) + .findOne(); + + // TRANsACTION scope ... same as bean2 and does not hit the DB + EBasicVer bean5 = DB.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .setPersistenceContextScope(TRANSACTION) + .findOne(); + + assertEquals("first", bean.getName()); + assertEquals("first", bean1.getName()); + assertEquals("second", bean2.getName()); + assertEquals("second", bean3.getName()); + assertEquals("second", bean5.getName()); + assertNotSame(bean1, bean2); + assertNotSame(bean1, bean5); + + DB.delete(bean3); + txn.commit(); + } + } + + + @Test + void ormUpdate_expect_clearsContext() { + + EBasicVer bean = new EBasicVer("first"); + DB.save(bean); + + try (Transaction txn = DB.beginTransaction()) { + EBasicVer bean1 = DB.find(EBasicVer.class, bean.getId()); + + // do an update of the name in the DB + int rowCount = DB.createUpdate(EBasicVer.class, "update ebasicver set name = ? where id = ?") + .setParameter(1, "second") + .setParameter(2, bean.getId()) + .execute(); + assertEquals(1, rowCount); + + // fetch the bean again... but doesn't hit DB as it + // is in the PersistenceContext which is transaction scoped + EBasicVer bean2 = DB.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .findOne(); + + // QUERY scope hits the DB (doesn't use the existing transactions persistence context) + // ... also explicitly not use bean cache + EBasicVer bean3 = DB.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .setPersistenceContextScope(QUERY) + .findOne(); + + // TRANsACTION scope ... same as bean2 and does not hit the DB + EBasicVer bean5 = DB.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .setPersistenceContextScope(TRANSACTION) + .findOne(); + + assertEquals("first", bean.getName()); + assertEquals("first", bean1.getName()); + assertEquals("second", bean2.getName()); + assertEquals("second", bean3.getName()); + assertEquals("second", bean5.getName()); + assertNotSame(bean1, bean2); + assertNotSame(bean1, bean5); + + DB.delete(bean3); + txn.commit(); } } } From 3576aafffc0fdce47f74af6615a25a2b5b4d1d06 Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Wed, 10 Jan 2024 23:23:47 +1300 Subject: [PATCH 2/3] #3295 When isAutoPersistUpdates is used do NOT clear the PersistenceContext Also make improvements to the test asserts in TestPersistenceContextQueryScope --- .../server/core/OrmQueryRequest.java | 7 +- .../server/core/PersistRequestOrmUpdate.java | 2 +- .../server/core/PersistRequestUpdateSql.java | 2 +- .../server/query/CQueryEngine.java | 3 + .../server/query/CQueryUpdate.java | 2 + .../TestPersistenceContextQueryScope.java | 71 +++++++++++++++++-- 6 files changed, 78 insertions(+), 9 deletions(-) diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java index 830b825f5..82ae78451 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/OrmQueryRequest.java @@ -338,7 +338,6 @@ public final class OrmQueryRequest extends BeanRequest implements SpiOrmQuery private int notifyCache(int rows, boolean update) { if (rows > 0) { - beanDescriptor.contextClear(transaction.persistenceContext()); beanDescriptor.cacheUpdateQuery(update, transaction); } return rows; @@ -783,4 +782,10 @@ public final class OrmQueryRequest extends BeanRequest implements SpiOrmQuery public int forwardOnlyFetchSize() { return queryEngine.forwardOnlyFetchSize(); } + + public void clearContext() { + if (!transaction.isAutoPersistUpdates()) { + beanDescriptor.contextClear(transaction.persistenceContext()); + } + } } diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java index 4c6771cbb..9e93a064f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestOrmUpdate.java @@ -78,7 +78,7 @@ public final class PersistRequestOrmUpdate extends PersistRequest { @Override public void postExecute() { OrmUpdateType ormUpdateType = ormUpdate.ormUpdateType(); - if (OrmUpdateType.INSERT != ormUpdateType) { + if (OrmUpdateType.INSERT != ormUpdateType && !transaction.isAutoPersistUpdates()) { beanDescriptor.contextClear(transaction.persistenceContext()); } if (startNanos > 0) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java index dc6d0b5de..a5453420f 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/PersistRequestUpdateSql.java @@ -159,7 +159,7 @@ public final class PersistRequestUpdateSql extends PersistRequest { */ @Override public void postExecute() { - if (sqlType != SqlType.SQL_INSERT) { + if (sqlType != SqlType.SQL_INSERT && !transaction.isAutoPersistUpdates()) { List> descriptors = server.descriptors(tableName); if (descriptors != null) { for (BeanDescriptor descriptor : descriptors) { diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java index 969d7ca25..a0dab920a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryEngine.java @@ -74,6 +74,9 @@ public final class CQueryEngine { if (request.logSql()) { request.logSql("{0}; --bind({1}) --micros({2}) --rows({3})", query.generatedSql(), query.bindLog(), query.micros(), rows); } + if (rows > 0) { + request.clearContext(); + } return rows; } catch (SQLException e) { throw translate(request, query.bindLog(), query.generatedSql(), e); diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java index 85081238b..add07241a 100644 --- a/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java +++ b/ebean-core/src/main/java/io/ebeaninternal/server/query/CQueryUpdate.java @@ -61,6 +61,7 @@ final class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery /** * Execute the update or delete statement returning the row count. */ + @SuppressWarnings("resource") public int execute() throws SQLException { long startNano = System.nanoTime(); try { @@ -109,6 +110,7 @@ final class CQueryUpdate implements SpiProfileTransactionEvent, CancelableQuery pstmt = null; } + @SuppressWarnings("resource") @Override public void profile() { transaction() diff --git a/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java b/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java index f82f855d1..2715196a9 100644 --- a/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java +++ b/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java @@ -19,8 +19,6 @@ class TestPersistenceContextQueryScope extends BaseTestCase { EBasicVer bean = new EBasicVer("first"); DB.save(bean); - //DB.cacheManager().setCaching(EBasicVer.class, true); - try (Transaction txn = DB.beginTransaction()) { EBasicVer bean1 = DB.find(EBasicVer.class, bean.getId()); @@ -47,7 +45,7 @@ class TestPersistenceContextQueryScope extends BaseTestCase { .setPersistenceContextScope(QUERY) .findOne(); - // TRANsACTION scope ... same as bean2 and does not hit the DB + // TRANSACTION scope ... same as bean2 and does not hit the DB EBasicVer bean5 = DB.find(EBasicVer.class) .setId(bean.getId()) .setUseCache(false) // ignore L2 cache @@ -57,11 +55,13 @@ class TestPersistenceContextQueryScope extends BaseTestCase { assertEquals("first", bean.getName()); assertEquals("first", bean1.getName()); assertEquals("second", bean2.getName()); + assertEquals("second", bean3.getName()); assertEquals("second", bean5.getName()); assertNotSame(bean1, bean2); assertNotSame(bean1, bean5); + assertSame(bean2, bean5); + assertNotSame(bean3, bean5); - assertEquals("second", bean3.getName()); DB.delete(bean3); txn.commit(); @@ -99,7 +99,7 @@ class TestPersistenceContextQueryScope extends BaseTestCase { .setPersistenceContextScope(QUERY) .findOne(); - // TRANsACTION scope ... same as bean2 and does not hit the DB + // TRANSACTION scope ... same as bean2 and does not hit the DB EBasicVer bean5 = DB.find(EBasicVer.class) .setId(bean.getId()) .setUseCache(false) // ignore L2 cache @@ -113,6 +113,8 @@ class TestPersistenceContextQueryScope extends BaseTestCase { assertEquals("second", bean5.getName()); assertNotSame(bean1, bean2); assertNotSame(bean1, bean5); + assertSame(bean2, bean5); + assertNotSame(bean3, bean5); DB.delete(bean3); txn.commit(); @@ -151,7 +153,7 @@ class TestPersistenceContextQueryScope extends BaseTestCase { .setPersistenceContextScope(QUERY) .findOne(); - // TRANsACTION scope ... same as bean2 and does not hit the DB + // TRANSACTION scope ... same as bean2 and does not hit the DB EBasicVer bean5 = DB.find(EBasicVer.class) .setId(bean.getId()) .setUseCache(false) // ignore L2 cache @@ -165,6 +167,63 @@ class TestPersistenceContextQueryScope extends BaseTestCase { assertEquals("second", bean5.getName()); assertNotSame(bean1, bean2); assertNotSame(bean1, bean5); + assertSame(bean2, bean5); + assertNotSame(bean3, bean5); + + DB.delete(bean3); + txn.commit(); + } + } + + + @Test + void ormUpdateQueryWithAutoPersist_expect_clearsContext() { + + EBasicVer bean = new EBasicVer("first"); + DB.save(bean); + + try (Transaction txn = DB.beginTransaction()) { + txn.setAutoPersistUpdates(true); + EBasicVer bean1 = DB.find(EBasicVer.class, bean.getId()); + + // do an update of the name in the DB + int rowCount = DB.update(EBasicVer.class) + .set("name", "second") + .where().idEq(bean.getId()) + .update(); + assertEquals(1, rowCount); + + // fetch the bean again... but doesn't hit DB as it + // is in the PersistenceContext which is transaction scoped + EBasicVer bean2 = DB.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .findOne(); + + // QUERY scope hits the DB (doesn't use the existing transactions persistence context) + // ... also explicitly not use bean cache + EBasicVer bean3 = DB.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .setPersistenceContextScope(QUERY) + .findOne(); + + // TRANSACTION scope ... same as bean2 and does not hit the DB + EBasicVer bean5 = DB.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .setPersistenceContextScope(TRANSACTION) + .findOne(); + + assertEquals("first", bean.getName()); + assertEquals("first", bean1.getName()); + assertEquals("first", bean2.getName()); // This is still first + assertEquals("second", bean3.getName()); + assertEquals("first", bean5.getName()); // This is still first + assertSame(bean1, bean2); // Now the same + assertSame(bean1, bean5); // Now the same + assertSame(bean2, bean5); + assertNotSame(bean3, bean5); DB.delete(bean3); txn.commit(); From 6645e5b9ec44ac2c1a944e418b61f5b84545f5cc Mon Sep 17 00:00:00 2001 From: Rob Bygrave Date: Fri, 19 Jan 2024 08:58:19 +1300 Subject: [PATCH 3/3] Update the test comments in TestPersistenceContextQueryScope To reflect the behaviour with #3295 --- .../TestPersistenceContextQueryScope.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java b/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java index 2715196a9..680200de7 100644 --- a/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java +++ b/ebean-test/src/test/java/org/tests/persistencecontext/TestPersistenceContextQueryScope.java @@ -23,6 +23,7 @@ class TestPersistenceContextQueryScope extends BaseTestCase { EBasicVer bean1 = DB.find(EBasicVer.class, bean.getId()); // do an update of the name in the DB + // With #3295 this now automatically clears the persistence context for BasicVer.class int rowCount = DB.sqlUpdate("update e_basicver set name=? where id=?") .setParameter("second") .setParameter(bean.getId()) @@ -30,15 +31,14 @@ class TestPersistenceContextQueryScope extends BaseTestCase { assertEquals(1, rowCount); - // fetch the bean again... but doesn't hit DB as it - // is in the PersistenceContext which is transaction scoped + // Prior to #3295 this returns the same bean via transaction scoped Persistence Context + // With #3295 this is now a fresh bean EBasicVer bean2 = DB.find(EBasicVer.class) .setId(bean.getId()) .setUseCache(false) // ignore L2 cache .findOne(); // QUERY scope hits the DB (doesn't use the existing transactions persistence context) - // ... also explicitly not use bean cache EBasicVer bean3 = DB.find(EBasicVer.class) .setId(bean.getId()) .setUseCache(false) // ignore L2 cache @@ -78,21 +78,21 @@ class TestPersistenceContextQueryScope extends BaseTestCase { EBasicVer bean1 = DB.find(EBasicVer.class, bean.getId()); // do an update of the name in the DB + // With #3295 this now automatically clears the persistence context for BasicVer.class int rowCount = DB.update(EBasicVer.class) .set("name", "second") .where().idEq(bean.getId()) .update(); assertEquals(1, rowCount); - // fetch the bean again... but doesn't hit DB as it - // is in the PersistenceContext which is transaction scoped + // Prior to #3295 this returns the same bean via transaction scoped Persistence Context + // With #3295 this is now a fresh bean EBasicVer bean2 = DB.find(EBasicVer.class) .setId(bean.getId()) .setUseCache(false) // ignore L2 cache .findOne(); // QUERY scope hits the DB (doesn't use the existing transactions persistence context) - // ... also explicitly not use bean cache EBasicVer bean3 = DB.find(EBasicVer.class) .setId(bean.getId()) .setUseCache(false) // ignore L2 cache @@ -121,7 +121,6 @@ class TestPersistenceContextQueryScope extends BaseTestCase { } } - @Test void ormUpdate_expect_clearsContext() { @@ -132,21 +131,22 @@ class TestPersistenceContextQueryScope extends BaseTestCase { EBasicVer bean1 = DB.find(EBasicVer.class, bean.getId()); // do an update of the name in the DB + // With #3295 this now automatically clears the persistence context for BasicVer.class int rowCount = DB.createUpdate(EBasicVer.class, "update ebasicver set name = ? where id = ?") .setParameter(1, "second") .setParameter(2, bean.getId()) .execute(); assertEquals(1, rowCount); - // fetch the bean again... but doesn't hit DB as it - // is in the PersistenceContext which is transaction scoped + // fetch the bean again... + // Prior to #3295 this returns the same bean via transaction scoped Persistence Context + // With #3295 this is now a fresh bean EBasicVer bean2 = DB.find(EBasicVer.class) .setId(bean.getId()) .setUseCache(false) // ignore L2 cache .findOne(); // QUERY scope hits the DB (doesn't use the existing transactions persistence context) - // ... also explicitly not use bean cache EBasicVer bean3 = DB.find(EBasicVer.class) .setId(bean.getId()) .setUseCache(false) // ignore L2 cache @@ -187,21 +187,21 @@ class TestPersistenceContextQueryScope extends BaseTestCase { EBasicVer bean1 = DB.find(EBasicVer.class, bean.getId()); // do an update of the name in the DB + // With #3295 this now automatically clears the persistence context for BasicVer.class int rowCount = DB.update(EBasicVer.class) .set("name", "second") .where().idEq(bean.getId()) .update(); assertEquals(1, rowCount); - // fetch the bean again... but doesn't hit DB as it - // is in the PersistenceContext which is transaction scoped + // Prior to #3295 this returns the same bean via transaction scoped Persistence Context + // With #3295 this is now a fresh bean EBasicVer bean2 = DB.find(EBasicVer.class) .setId(bean.getId()) .setUseCache(false) // ignore L2 cache .findOne(); // QUERY scope hits the DB (doesn't use the existing transactions persistence context) - // ... also explicitly not use bean cache EBasicVer bean3 = DB.find(EBasicVer.class) .setId(bean.getId()) .setUseCache(false) // ignore L2 cache