diff --git a/src/main/java/com/avaje/ebean/PersistenceContextScope.java b/src/main/java/com/avaje/ebean/PersistenceContextScope.java new file mode 100644 index 000000000..37ebefe08 --- /dev/null +++ b/src/main/java/com/avaje/ebean/PersistenceContextScope.java @@ -0,0 +1,52 @@ +package com.avaje.ebean; + +/** + * Defines the scope for PersistenceContext. + *

+ * Ebean has traditionally used Transaction scope for the PersistenceContext. This is used to change the scope to + * use (by default) and explicitly set the scope to use for an individual query. + * + * @see com.avaje.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope) + * @see com.avaje.ebean.Query#setPersistenceContextScope(PersistenceContextScope) + */ +public enum PersistenceContextScope { + + /** + * PersistenceContext is scoped to the transaction. + *

+ * If a transaction spans 2 or more queries that fetch the same bean in terms of same type + * and same Id value then they share the same bean instance. + *

+ * You may want to change to use QUERY scope when you want a query executing in a transaction to effectively + * ignore beans that have already been loaded (by other queries in the same transaction) and instead get a + * 'fresh copy' of the bean. + */ + TRANSACTION, + + /** + * PersistenceContext is scoped to the query. + *

+ * This means that for this query running in an existing transaction then it will effectively ignore any beans + * that have already been queried/loaded by prior queries in the same transaction. + *

+ * You may use QUERY scope on a query that is executed in a transaction and you want to get a 'fresh copy' of the bean. + */ + QUERY, + + /** + * EXPERIMENTAL FEATURE - This is not expected to be used and somewhat experimental. + * This NONE option effectively means that a PersistenceContext is not used when building the object graph and + * subsequent lazy loading. + *

+ * You should ONLY use this when treating the resulting object graph as read only and even then you would be best + * to use QUERY (or TRANSACTION). + *

+ * A query executed with NONE can build a object graph where there are multiple instances that represent the same + * 'logical bean' by type and Id value (multiple instances of 'Customer 42'). Getting multiple instances that + * represent the same logical bean (same row in the database) means that it is potentially dangerous/confusing to + * use this scope when modifying the beans as multiple instances represent the same underlying rows in the database. + *

+ * Generally you would expect to always use TRANSACTION or QUERY scope. + */ + NONE +} diff --git a/src/main/java/com/avaje/ebean/Query.java b/src/main/java/com/avaje/ebean/Query.java index 96fb71093..56c453ff5 100644 --- a/src/main/java/com/avaje/ebean/Query.java +++ b/src/main/java/com/avaje/ebean/Query.java @@ -301,6 +301,21 @@ public interface Query extends Serializable { */ public Query copy(); + /** + * Specify the PersistenceContextScope to use for this query. + *

+ * When this is not set the 'default' configured on {@link com.avaje.ebean.config.ServerConfig#setPersistenceContextScope(PersistenceContextScope)} + * is used - this value defaults to {@link com.avaje.ebean.PersistenceContextScope#TRANSACTION}. + *

+ * Note that the same persistence Context is used for subsequent lazy loading and query join queries. + *

+ * Note that #findEach uses a 'per object graph' PersistenceContext so this scope is ignored for + * queries executed as #findIterate, #findEach, #findEachWhile. + * + * @param scope The scope to use for this query and subsequent lazy loading. + */ + public Query setPersistenceContextScope(PersistenceContextScope scope); + /** * Return the ExpressionFactory used by this query. */ diff --git a/src/main/java/com/avaje/ebean/config/ServerConfig.java b/src/main/java/com/avaje/ebean/config/ServerConfig.java index 2330f7e59..acd49a473 100644 --- a/src/main/java/com/avaje/ebean/config/ServerConfig.java +++ b/src/main/java/com/avaje/ebean/config/ServerConfig.java @@ -1,27 +1,22 @@ package com.avaje.ebean.config; -import java.util.ArrayList; -import java.util.List; - -import javax.sql.DataSource; - import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.PersistenceContextScope; import com.avaje.ebean.annotation.Encrypted; import com.avaje.ebean.cache.ServerCacheFactory; import com.avaje.ebean.cache.ServerCacheManager; import com.avaje.ebean.config.GlobalProperties.PropertySource; import com.avaje.ebean.config.dbplatform.DatabasePlatform; import com.avaje.ebean.config.dbplatform.DbEncrypt; -import com.avaje.ebean.event.BeanPersistController; -import com.avaje.ebean.event.BeanPersistListener; -import com.avaje.ebean.event.BeanQueryAdapter; -import com.avaje.ebean.event.BulkTableEventListener; -import com.avaje.ebean.event.ServerConfigStartup; -import com.avaje.ebean.event.TransactionEventListener; +import com.avaje.ebean.event.*; import com.avaje.ebean.meta.MetaInfoManager; import com.avaje.ebean.util.ClassUtil; import com.fasterxml.jackson.core.JsonFactory; +import javax.sql.DataSource; +import java.util.ArrayList; +import java.util.List; + /** * The configuration used for creating a EbeanServer. *

@@ -247,6 +242,11 @@ public class ServerConfig { private boolean collectQueryOrigins; + /** + * The default PersistenceContextScope used if one is not explicitly set on a query. + */ + private PersistenceContextScope persistenceContextScope = PersistenceContextScope.TRANSACTION; + private JsonFactory jsonFactory; private boolean localTimeWithNanos; @@ -254,8 +254,7 @@ public class ServerConfig { private boolean durationWithNanos; /** - * Construct a Server Configuration for programmatically creating an - * EbeanServer. + * Construct a Server Configuration for programmatically creating an EbeanServer. */ public ServerConfig() { @@ -1305,6 +1304,35 @@ public class ServerConfig { this.persistListeners = persistListeners; } + /** + * Return the default PersistenceContextScope to be used if one is not explicitly set on a query. + *

+ * The PersistenceContextScope can specified on each query via {@link com.avaje.ebean + * .Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope)}. If it + * is not set on the query this default scope is used. + * + * @see com.avaje.ebean.Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope) + */ + public PersistenceContextScope getPersistenceContextScope() { + // if somehow null return TRANSACTION scope + return persistenceContextScope == null ? PersistenceContextScope.TRANSACTION : persistenceContextScope; + } + + /** + * Set the PersistenceContext scope to be used if one is not explicitly set on a query. + *

+ * This defaults to {@link PersistenceContextScope#TRANSACTION}. + *

+ * The PersistenceContextScope can specified on each query via {@link com.avaje.ebean + * .Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope)}. If it + * is not set on the query this scope is used. + * + * @see com.avaje.ebean.Query#setPersistenceContextScope(com.avaje.ebean.PersistenceContextScope) + */ + public void setPersistenceContextScope(PersistenceContextScope persistenceContextScope) { + this.persistenceContextScope = persistenceContextScope; + } + /** * Load the settings from the ebean.properties file. */ @@ -1359,8 +1387,6 @@ public class ServerConfig { /** * This is broken out for the same reason as above - preserve existing behaviour but let it be overridden. - * - * @param p */ protected void loadAutofetchConfig(PropertySource p) { autofetchConfig.loadSettings(p); @@ -1418,6 +1444,8 @@ public class ServerConfig { int batchSize = p.getInt("batch.size", 20); persistBatchSize = p.getInt("persistBatchSize", batchSize); + persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope","TRANSACTION")); + dataSourceJndiName = p.get("dataSourceJndiName", null); databaseSequenceBatchSize = p.getInt("databaseSequenceBatchSize", 20); databaseBooleanTrue = p.get("databaseBooleanTrue", null); diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java index a69edb67e..2feea537a 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiEbeanServer.java @@ -2,10 +2,7 @@ package com.avaje.ebeaninternal.api; import java.util.List; -import com.avaje.ebean.EbeanServer; -import com.avaje.ebean.Query; -import com.avaje.ebean.Transaction; -import com.avaje.ebean.TxScope; +import com.avaje.ebean.*; import com.avaje.ebean.bean.BeanCollectionLoader; import com.avaje.ebean.bean.BeanLoader; import com.avaje.ebean.bean.CallStack; @@ -63,6 +60,11 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL */ public CallStack createCallStack(); + /** + * Return the PersistenceContextScope to use defined at query or server level. + */ + public PersistenceContextScope getPersistenceContextScope(SpiQuery query); + /** * Return the DDL generator. */ diff --git a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java index 3c6db789e..490145c5b 100644 --- a/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/api/SpiQuery.java @@ -5,6 +5,7 @@ import java.util.List; import com.avaje.ebean.ExpressionList; import com.avaje.ebean.OrderBy; +import com.avaje.ebean.PersistenceContextScope; import com.avaje.ebean.Query; import com.avaje.ebean.bean.BeanCollectionTouched; import com.avaje.ebean.bean.CallStack; @@ -79,7 +80,15 @@ public interface SpiQuery extends Query { */ SUBQUERY } - + + /** + * Return the PersistenceContextScope that this query should use. + *

+ * This can be null and in that case use the default scope. + *

+ */ + public PersistenceContextScope getPersistenceContextScope(); + /** * Return true if select all properties was used to ensure the property * invoking a lazy load was included in the query. diff --git a/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManager.java b/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManager.java index eac7ef78e..f335eef67 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManager.java +++ b/src/main/java/com/avaje/ebeaninternal/server/cache/DefaultServerCacheManager.java @@ -40,10 +40,13 @@ public class DefaultServerCacheManager implements ServerCacheManager { cacheFactory.init(server); this.ebeanServer = (SpiEbeanServer)server; } - - public void setCaching(Class beanType, boolean useCache) { - ebeanServer.getBeanDescriptor(beanType).getCacheOptions().setUseCache(useCache); - } + + /** + * Set bean caching on or off for a given bean type. + */ + public void setCaching(Class beanType, boolean useCache) { + ebeanServer.getBeanDescriptor(beanType).setUseCache(useCache); + } /** * Clear both the bean cache and the query cache for a diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index 03172c75d..81431d375 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -137,7 +137,6 @@ public final class DefaultServer implements SpiEbeanServer { private final CQueryEngine cqueryEngine; - //@Deprecated private DdlGenerator ddlGenerator; private final ExpressionFactory expressionFactory; @@ -157,6 +156,11 @@ public final class DefaultServer implements SpiEbeanServer { */ private String mbeanName; + /** + * The default PersistenceContextScope used if it is not explicitly set on a query. + */ + private final PersistenceContextScope defaultPersistenceContextScope; + /** * The MBeanServer Ebean is registered with. */ @@ -215,6 +219,7 @@ public final class DefaultServer implements SpiEbeanServer { this.cqueryEngine = config.getCQueryEngine(); this.expressionFactory = config.getExpressionFactory(); this.encryptKeyManager = serverConfig.getEncryptKeyManager(); + this.defaultPersistenceContextScope = serverConfig.getPersistenceContextScope(); this.beanDescriptorManager = config.getBeanDescriptorManager(); beanDescriptorManager.setEbeanServer(this); @@ -1131,8 +1136,8 @@ public final class DefaultServer implements SpiEbeanServer { t = getCurrentServerTransaction(); } PersistenceContext context = null; - if (t != null) { - // first look in the persistence context + if (t != null && useTransactionPersistenceContext(query)) { + // first look in the transaction scoped persistence context context = t.getPersistenceContext(); if (context != null) { WithOption o = context.getWithOption(beanDescriptor.getBeanType(), query.getId()); @@ -1155,7 +1160,22 @@ public final class DefaultServer implements SpiEbeanServer { // Hit the L2 bean cache return beanDescriptor.cacheBeanGet(query, context); } - + + /** + * Return true if transactions PersistenceContext should be used. + */ + private boolean useTransactionPersistenceContext(SpiQuery query) { + return PersistenceContextScope.TRANSACTION.equals(getPersistenceContextScope(query)); + } + + /** + * Return the PersistenceContextScope to use defined at query or server level. + */ + public PersistenceContextScope getPersistenceContextScope(SpiQuery query) { + PersistenceContextScope scope = query.getPersistenceContextScope(); + return (scope != null) ? scope : defaultPersistenceContextScope; + } + @SuppressWarnings("unchecked") private T findId(Query query, Transaction t) { diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java index b3b701f38..ac7c474f9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/OrmQueryRequest.java @@ -28,6 +28,7 @@ import com.avaje.ebeaninternal.server.loadcontext.DLoadContext; import com.avaje.ebeaninternal.server.query.CQueryPlan; import com.avaje.ebeaninternal.server.query.CancelableQuery; import com.avaje.ebeaninternal.server.transaction.DefaultPersistenceContext; +import com.avaje.ebeaninternal.server.transaction.NoopPersistenceContext; /** * Wraps the objects involved in executing a Query. @@ -53,7 +54,7 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe private HashQuery cacheKey; private HashQueryPlan queryPlanHash; - + /** * Create the InternalQueryRequest. */ @@ -69,7 +70,7 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe this.readOnly = query.isReadOnly(); } - + /** * Return the database platform like clause. */ @@ -183,18 +184,32 @@ public final class OrmQueryRequest extends BeanRequest implements BeanQueryRe persistenceContext = new DefaultPersistenceContext(); loadContext.setPersistenceContext(persistenceContext); } - + /** * Get the TransactionContext either explicitly set on the query or * transaction scoped. */ private PersistenceContext getPersistenceContext(SpiQuery query, SpiTransaction t) { + // check if there is already a persistence context set which is the case + // when lazy loading or query joins are executed PersistenceContext ctx = query.getPersistenceContext(); - if (ctx == null) { - ctx = t.getPersistenceContext(); + if (ctx != null) return ctx; + + // determine the scope (from the query and then server) + PersistenceContextScope scope = ebeanServer.getPersistenceContextScope(query); + switch (scope) { + case QUERY: + // Create a new PersistenceContext for this query + return new DefaultPersistenceContext(); + case NONE: + // Effectively don't use a PersistenceContext + return new NoopPersistenceContext(); + default: { + // Use the transaction scoped PersistenceContext + return t.getPersistenceContext(); + } } - return ctx; } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index 5e564cb56..133a1d0f9 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -725,6 +725,13 @@ public class BeanDescriptor implements MetaBeanInfo { return inheritInfo == null || inheritInfo.isRoot(); } + /** + * Set the bean caching on or off. + */ + public void setUseCache(boolean useCache) { + cacheHelp.setUseCache(useCache); + } + /** * Return true if there is currently query caching for this type of bean. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java index 2cb667fd3..4f8a19e49 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptorCacheHelp.java @@ -105,7 +105,16 @@ public final class BeanDescriptorCacheHelp { } } } - + + public void setUseCache(boolean useCache) { + if (useCache) { + getBeanCache(); + } else { + beanCacheClear(); + beanCache = null; + } + } + /** * Return true if there is currently query caching for this type of bean. */ diff --git a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java index de67a9949..0f999fe64 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java +++ b/src/main/java/com/avaje/ebeaninternal/server/querydefn/DefaultOrmQuery.java @@ -167,6 +167,8 @@ public class DefaultOrmQuery implements SpiQuery { private Boolean readOnly; + private PersistenceContextScope persistenceContextScope; + private boolean sqlSelect; /** @@ -459,6 +461,7 @@ public class DefaultOrmQuery implements SpiQuery { if (havingExpressions != null) { copy.havingExpressions = havingExpressions.copy(copy); } + copy.persistenceContextScope = persistenceContextScope; copy.usageProfiling = usageProfiling; copy.autoFetch = autoFetch; copy.parentNode = parentNode; @@ -468,6 +471,16 @@ public class DefaultOrmQuery implements SpiQuery { return copy; } + @Override + public Query setPersistenceContextScope(PersistenceContextScope scope) { + this.persistenceContextScope = scope; + return this; + } + + public PersistenceContextScope getPersistenceContextScope() { + return persistenceContextScope; + } + public Type getType() { return type; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/transaction/NoopPersistenceContext.java b/src/main/java/com/avaje/ebeaninternal/server/transaction/NoopPersistenceContext.java new file mode 100644 index 000000000..5a1736cef --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/transaction/NoopPersistenceContext.java @@ -0,0 +1,60 @@ +package com.avaje.ebeaninternal.server.transaction; + +import com.avaje.ebean.bean.PersistenceContext; + +/** + * PersistenceContext used with scope of NONE. + *

+ * When used effectively means no PersistenceContext is used at all. This is not expected to be used much and + * actually is not recommended. + */ +public class NoopPersistenceContext implements PersistenceContext { + + @Override + public void put(Object id, Object bean) { + // do nothing + } + + @Override + public Object putIfAbsent(Object id, Object bean) { + // do nothing + return null; + } + + @Override + public Object get(Class beanType, Object uid) { + // do nothing, return null + return null; + } + + @Override + public WithOption getWithOption(Class beanType, Object uid) { + // do nothing, return null + return null; + } + + @Override + public void clear() { + // do nothing + } + + @Override + public void clear(Class beanType) { + // do nothing + } + + @Override + public void clear(Class beanType, Object uid) { + // do nothing + } + + @Override + public void deleted(Class beanType, Object id) { + // do nothing + } + + @Override + public int size(Class beanType) { + return 0; + } +} diff --git a/src/test/java/com/avaje/tests/persistencecontext/TestPersistencContextNone.java b/src/test/java/com/avaje/tests/persistencecontext/TestPersistencContextNone.java new file mode 100644 index 000000000..4da92ff7b --- /dev/null +++ b/src/test/java/com/avaje/tests/persistencecontext/TestPersistencContextNone.java @@ -0,0 +1,81 @@ +package com.avaje.tests.persistencecontext; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.ebean.PersistenceContextScope; +import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.Order; +import com.avaje.tests.model.basic.ResetBasicData; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class TestPersistencContextNone extends BaseTestCase { + + @Test + public void test_persistenceContextScopeNone() { + + ResetBasicData.reset(); + + + List orders = Ebean.find(Order.class) + .setPersistenceContextScope(PersistenceContextScope.NONE) + .fetch("customer", "id, name") + .where().istartsWith("customer.name", "rob").eq("customer.id", 1) + .orderBy().asc("customer.name") + .findList(); + + assertTrue(!orders.isEmpty()); + + // collect the customer instances + List customers = new ArrayList(); + Set identities = new HashSet(); + + for (Order order : orders) { + Customer customer = order.getCustomer(); + identities.add(System.identityHashCode(customer)); + customers.add(customer); + } + + // Many instances of 'logically the same Customer' + assertTrue(identities.size() > 1); + assertEquals(identities.size(), customers.size()); + } + + @Test + public void test_persistenceContextScopeQuery() { + + ResetBasicData.reset(); + + List orders = Ebean.find(Order.class) + // Use QUERY or TRANSACTION scope (just not NONE) + .setPersistenceContextScope(PersistenceContextScope.QUERY) + .fetch("customer", "id, name") + .where().istartsWith("customer.name", "rob").eq("customer.id", 1) + .orderBy().asc("customer.name") + .findList(); + + assertTrue(!orders.isEmpty()); + + // collect the customer instances + List customers = new ArrayList(); + Set identities = new HashSet(); + + for (Order order : orders) { + Customer customer = order.getCustomer(); + identities.add(System.identityHashCode(customer)); + customers.add(customer); + } + + // only one instance of Customer this time ... + assertEquals(1, identities.size()); + assertTrue(customers.size() > 1); + } + +} diff --git a/src/test/java/com/avaje/tests/persistencecontext/TestPersistenceContextQueryScope.java b/src/test/java/com/avaje/tests/persistencecontext/TestPersistenceContextQueryScope.java new file mode 100644 index 000000000..325127d2e --- /dev/null +++ b/src/test/java/com/avaje/tests/persistencecontext/TestPersistenceContextQueryScope.java @@ -0,0 +1,85 @@ +package com.avaje.tests.persistencecontext; + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.tests.model.basic.EBasicVer; +import org.junit.Test; + +import static com.avaje.ebean.PersistenceContextScope.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + + +public class TestPersistenceContextQueryScope extends BaseTestCase { + + @Test + public void test() { + + EBasicVer bean = new EBasicVer(); + bean.setName("first"); + Ebean.save(bean); + + //Ebean.getServerCacheManager().setCaching(EBasicVer.class, true); + + Ebean.beginTransaction(); + try { + EBasicVer bean1 = Ebean.find(EBasicVer.class, bean.getId()); + + // do an update of the name + EBasicVer updateBean = new EBasicVer(); + updateBean.setId(bean.getId()); + updateBean.setName("second"); + + // perform an update to the DB + Ebean.update(updateBean); + + // fetch the bean again... but doesn't hit DB as it + // is in the PersistenceContext which is transaction scoped + EBasicVer bean2 = Ebean.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .findUnique(); + + // QUERY scope hits the DB (doesn't use the existing transactions persistence context) + // ... also explicitly not use bean cache + EBasicVer bean3 = Ebean.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .setPersistenceContextScope(QUERY) + .findUnique(); + + // NONE scope also hits the DB + // ... also explicitly not use bean cache + EBasicVer bean4 = Ebean.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .setPersistenceContextScope(NONE) + .findUnique(); + + + // TRANsACTION scope ... same as bean2 and does not hit the DB + EBasicVer bean5 = Ebean.find(EBasicVer.class) + .setId(bean.getId()) + .setUseCache(false) // ignore L2 cache + .setPersistenceContextScope(TRANSACTION) + .findUnique(); + + assertEquals("first", bean.getName()); + assertEquals("first", bean1.getName()); + assertEquals("first", bean2.getName()); + assertEquals("first", bean5.getName()); + assertSame(bean1, bean2); + assertSame(bean1, bean5); + + assertEquals("second", bean3.getName()); + assertEquals("second", bean4.getName()); + + Ebean.delete(bean4); + + Ebean.commitTransaction(); + + } finally { + Ebean.endTransaction(); + } + } +} diff --git a/src/test/java/com/avaje/tests/persistencecontext/TestPersistenceContextServerConfig.java b/src/test/java/com/avaje/tests/persistencecontext/TestPersistenceContextServerConfig.java new file mode 100644 index 000000000..161a46e2e --- /dev/null +++ b/src/test/java/com/avaje/tests/persistencecontext/TestPersistenceContextServerConfig.java @@ -0,0 +1,38 @@ +package com.avaje.tests.persistencecontext; + +import com.avaje.ebean.*; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebeaninternal.api.SpiEbeanServer; +import com.avaje.ebeaninternal.api.SpiQuery; +import com.avaje.tests.model.basic.EBasicVer; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TestPersistenceContextServerConfig extends BaseTestCase { + + @Test + public void test_config() { + + SpiEbeanServer ebeanServer = (SpiEbeanServer)create(); + + Query query = ebeanServer.find(EBasicVer.class); + + PersistenceContextScope scope = ebeanServer.getPersistenceContextScope((SpiQuery) query); + + assertEquals(PersistenceContextScope.QUERY, scope); + } + + static EbeanServer create() { + + ServerConfig config = new ServerConfig(); + config.setName("h2"); + config.loadFromProperties(); + config.setName("withPCQuery"); + config.setPersistenceContextScope(PersistenceContextScope.QUERY); + + config.addClass(EBasicVer.class); + + return EbeanServerFactory.create(config); + } +}