mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Merge pull request #3301 from ebean-orm/feature/3295-PC-clear-on-bulk-update
#3295 Clear PersistenceContext on execution of bulk updates or deletes
This commit is contained in:
@@ -773,4 +773,10 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
public int forwardOnlyFetchSize() {
|
||||
return queryEngine.forwardOnlyFetchSize();
|
||||
}
|
||||
|
||||
public void clearContext() {
|
||||
if (!transaction.isAutoPersistUpdates()) {
|
||||
beanDescriptor.contextClear(transaction.persistenceContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,10 +77,13 @@ public final class PersistRequestOrmUpdate extends PersistRequest {
|
||||
*/
|
||||
@Override
|
||||
public void postExecute() {
|
||||
OrmUpdateType ormUpdateType = ormUpdate.ormUpdateType();
|
||||
if (OrmUpdateType.INSERT != ormUpdateType && !transaction.isAutoPersistUpdates()) {
|
||||
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);
|
||||
|
||||
@@ -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 && !transaction.isAutoPersistUpdates()) {
|
||||
List<BeanDescriptor<?>> descriptors = server.descriptors(tableName);
|
||||
if (descriptors != null) {
|
||||
for (BeanDescriptor<?> descriptor : descriptors) {
|
||||
descriptor.contextClear(transaction.persistenceContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (startNanos > 0) {
|
||||
persistExecute.collectSqlUpdate(label, startNanos);
|
||||
}
|
||||
|
||||
@@ -2056,6 +2056,13 @@ public class BeanDescriptor<T> implements BeanType<T>, 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).
|
||||
*/
|
||||
|
||||
@@ -399,7 +399,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
|
||||
* Return the BeanDescriptors mapped to the table.
|
||||
*/
|
||||
public List<BeanDescriptor<?>> descriptors(String tableName) {
|
||||
return tableToDescMap.get(tableName.toLowerCase());
|
||||
return tableName == null ? Collections.emptyList() : tableToDescMap.get(tableName.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
|
||||
+177
-15
@@ -8,24 +8,22 @@ 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);
|
||||
|
||||
try (Transaction txn = DB.beginTransaction()) {
|
||||
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())
|
||||
@@ -33,22 +31,21 @@ public 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
|
||||
.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,12 +54,177 @@ 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", bean3.getName());
|
||||
assertEquals("second", bean5.getName());
|
||||
assertNotSame(bean1, bean2);
|
||||
assertNotSame(bean1, bean5);
|
||||
assertSame(bean2, bean5);
|
||||
assertNotSame(bean3, bean5);
|
||||
|
||||
DB.delete(bean3);
|
||||
|
||||
txn.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
// 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);
|
||||
|
||||
// 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)
|
||||
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);
|
||||
assertSame(bean2, bean5);
|
||||
assertNotSame(bean3, 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
|
||||
// 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...
|
||||
// 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)
|
||||
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);
|
||||
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
|
||||
// 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);
|
||||
|
||||
// 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)
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user