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);
+ }
+}