mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-23 10:03:59 +00:00
Merge pull request #1682 from ebean-orm/feature/1679-region
Feature/1679 region
This commit is contained in:
@@ -117,7 +117,7 @@
|
||||
<dependency>
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean-annotation</artifactId>
|
||||
<version>4.7</version>
|
||||
<version>4.9</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
+29
-2
@@ -1,5 +1,7 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The cache service for server side caching of beans and query results.
|
||||
*/
|
||||
@@ -14,6 +16,31 @@ public interface ServerCacheManager {
|
||||
*/
|
||||
boolean isLocalL2Caching();
|
||||
|
||||
/**
|
||||
* Return all the cache regions.
|
||||
*/
|
||||
List<ServerCacheRegion> allRegions();
|
||||
|
||||
/**
|
||||
* Set the regions that are enabled.
|
||||
* <p>
|
||||
* Typically this is set on startup and at runtime (via dynamic configuration).
|
||||
* </p>
|
||||
*
|
||||
* @param regions A region name or comma delimited list of region names.
|
||||
*/
|
||||
void setEnabledRegions(String regions);
|
||||
|
||||
/**
|
||||
* Enable or disable all the cache regions.
|
||||
*/
|
||||
void setAllRegionsEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Return the cache region by name. Typically to enable or disable the region.
|
||||
*/
|
||||
ServerCacheRegion getRegion(String name);
|
||||
|
||||
/**
|
||||
* Return the cache for mapping natural keys to id values.
|
||||
*/
|
||||
@@ -46,7 +73,7 @@ public interface ServerCacheManager {
|
||||
|
||||
/**
|
||||
* Clear all the local caches.
|
||||
*
|
||||
* <p>
|
||||
* This is used when the L2 Cache is based on clustered near-caches (Like Ebean-K8s-L2Cache).
|
||||
* It is not used when the L2 cache is a distributed cache such as HazelCast or Ignite etc.
|
||||
*/
|
||||
@@ -54,7 +81,7 @@ public interface ServerCacheManager {
|
||||
|
||||
/**
|
||||
* Clear the local caches for this bean type.
|
||||
*
|
||||
* <p>
|
||||
* This is used when the L2 Cache is based on clustered near-caches (Like Ebean-K8s-L2Cache).
|
||||
* It is not used when the L2 cache is a distributed cache such as HazelCast or Ignite etc.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
/**
|
||||
* Cache region can be enabled independently.
|
||||
*/
|
||||
public interface ServerCacheRegion {
|
||||
|
||||
/**
|
||||
* Return the region name.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return true if the cache region is enabled.
|
||||
*/
|
||||
boolean isEnabled();
|
||||
|
||||
/**
|
||||
* Set to true to enable the cache region.
|
||||
*/
|
||||
void setEnabled(boolean enabled);
|
||||
|
||||
}
|
||||
@@ -479,6 +479,7 @@ public class ServerConfig {
|
||||
*/
|
||||
private boolean disableL2Cache;
|
||||
|
||||
private String enabledL2Regions;
|
||||
|
||||
/**
|
||||
* Should the javax.validation.constraints.NotNull enforce a notNull column in DB.
|
||||
@@ -2914,6 +2915,7 @@ public class ServerConfig {
|
||||
collectQueryPlans = p.getBoolean("collectQueryPlans", collectQueryPlans);
|
||||
docStoreOnly = p.getBoolean("docStoreOnly", docStoreOnly);
|
||||
disableL2Cache = p.getBoolean("disableL2Cache", disableL2Cache);
|
||||
enabledL2Regions = p.get("enabledL2Regions", enabledL2Regions);
|
||||
notifyL2CacheInForeground = p.getBoolean("notifyL2CacheInForeground", notifyL2CacheInForeground);
|
||||
explicitTransactionBeginMode = p.getBoolean("explicitTransactionBeginMode", explicitTransactionBeginMode);
|
||||
autoCommitMode = p.getBoolean("autoCommitMode", autoCommitMode);
|
||||
@@ -3136,6 +3138,20 @@ public class ServerConfig {
|
||||
this.expressionNativeIlike = expressionNativeIlike;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the enabled L2 cache regions.
|
||||
*/
|
||||
public String getEnabledL2Regions() {
|
||||
return enabledL2Regions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the enabled L2 cache regions (comma delimited).
|
||||
*/
|
||||
public void setEnabledL2Regions(String enabledL2Regions) {
|
||||
this.enabledL2Regions = enabledL2Regions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if L2 cache is disabled.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebeaninternal.server.expression.IdInExpression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Used for bean cache lookup with where ids in expression.
|
||||
*/
|
||||
public class CacheIdLookup<T> {
|
||||
|
||||
private final IdInExpression idInExpression;
|
||||
|
||||
private int remaining;
|
||||
|
||||
public CacheIdLookup(IdInExpression idInExpression) {
|
||||
this.idInExpression = idInExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Id values for the in expression.
|
||||
*/
|
||||
public Collection<?> idValues() {
|
||||
return idInExpression.idValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the hits returning the beans fetched from cache and
|
||||
* adjusting the in expression (to not fetch the hits).
|
||||
*/
|
||||
public List<T> removeHits(BeanCacheResult<T> cacheResult) {
|
||||
|
||||
Set<Object> hitIds = new HashSet<>();
|
||||
List<T> beans = new ArrayList<>(hitIds.size());
|
||||
|
||||
for (BeanCacheResult.Entry<T> hit : cacheResult.hits()) {
|
||||
hitIds.add(hit.getKey());
|
||||
beans.add(hit.getBean());
|
||||
}
|
||||
|
||||
this.remaining = idInExpression.removeIds(hitIds);
|
||||
return beans;
|
||||
}
|
||||
|
||||
public boolean allHits() {
|
||||
return remaining == 0;
|
||||
}
|
||||
}
|
||||
@@ -69,18 +69,18 @@ public class NaturalKeyQueryData<T> {
|
||||
/**
|
||||
* Match for IN expression. We only allow one IN clause.
|
||||
*/
|
||||
public List<Object> matchIn(String propName, List<Object> sourceValues) {
|
||||
public boolean matchIn(String propName, List<Object> inValues) {
|
||||
if (hasIn) {
|
||||
// only 1 IN allowed (to project naturalIds)
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
if (matchProperty(propName)) {
|
||||
this.hasIn = true;
|
||||
this.inProperty = propName;
|
||||
this.inValues = new ArrayList<>(sourceValues);
|
||||
return this.inValues;
|
||||
this.inValues = inValues;
|
||||
return true;
|
||||
}
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,21 +198,19 @@ public class NaturalKeyQueryData<T> {
|
||||
this.hitCount = hits.size();
|
||||
|
||||
List<T> beans = new ArrayList<>(hitCount);
|
||||
|
||||
for (BeanCacheResult.Entry<T> hit : hits) {
|
||||
if (inValues != null) {
|
||||
Object naturalKey = hit.getKey();
|
||||
Object inValue = set.getInValue(naturalKey);
|
||||
inValues.remove(inValue);
|
||||
|
||||
} else if (inPairs != null) {
|
||||
Object naturalKey = hit.getKey();
|
||||
Pairs.Entry inValue = (Pairs.Entry)set.getInValue(naturalKey);
|
||||
inPairs.remove(inValue);
|
||||
}
|
||||
removeKey(set.getInValue(hit.getKey()));
|
||||
beans.add(hit.getBean());
|
||||
}
|
||||
|
||||
return beans;
|
||||
}
|
||||
|
||||
private void removeKey(Object inValue) {
|
||||
if (inValues != null) {
|
||||
inValues.remove(inValue);
|
||||
} else if (inPairs != null) {
|
||||
//noinspection SuspiciousMethodCalls
|
||||
inPairs.remove(inValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
public interface SpiCacheControl {
|
||||
|
||||
/**
|
||||
* Return true if bean caching or query caching.
|
||||
*/
|
||||
boolean isCaching();
|
||||
|
||||
/**
|
||||
* Return true if bean caching.
|
||||
*/
|
||||
boolean isBeanCaching();
|
||||
|
||||
/**
|
||||
* Return true if natural key caching.
|
||||
*/
|
||||
boolean isNaturalKeyCaching();
|
||||
|
||||
/**
|
||||
* Return true if query caching.
|
||||
*/
|
||||
boolean isQueryCaching();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.cache.ServerCacheRegion;
|
||||
|
||||
/**
|
||||
* Cache region can be enabled independently.
|
||||
*/
|
||||
public interface SpiCacheRegion extends ServerCacheRegion {
|
||||
|
||||
}
|
||||
@@ -429,6 +429,11 @@ public interface SpiQuery<T> extends Query<T>, TxnProfileEventCodes {
|
||||
*/
|
||||
void resetBeanCacheAutoMode(boolean findOne);
|
||||
|
||||
/**
|
||||
* Bean cache lookup for find by ids.
|
||||
*/
|
||||
CacheIdLookup<T> cacheIdLookup();
|
||||
|
||||
/**
|
||||
* Collect natural key data for this query or null if the query does not match
|
||||
* the requirements of natural key lookup.
|
||||
|
||||
@@ -2,6 +2,9 @@ package io.ebeaninternal.server.cache;
|
||||
|
||||
import io.ebean.cache.ServerCache;
|
||||
import io.ebean.cache.ServerCacheManager;
|
||||
import io.ebean.cache.ServerCacheRegion;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Adapts SpiCacheManager to ServerCacheManager.
|
||||
@@ -22,6 +25,26 @@ public class DefaultCacheAdapter implements ServerCacheManager {
|
||||
return cacheManager.isLocalL2Caching();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ServerCacheRegion> allRegions() {
|
||||
return cacheManager.allRegions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabledRegions(String regions) {
|
||||
cacheManager.setEnabledRegions(regions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerCacheRegion getRegion(String region) {
|
||||
return cacheManager.getRegion(region);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllRegionsEnabled(boolean enabled) {
|
||||
cacheManager.setAllRegionsEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerCache getNaturalKeyCache(Class<?> beanType) {
|
||||
return cacheManager.getNaturalKeyCache(beanType);
|
||||
|
||||
@@ -1,14 +1,29 @@
|
||||
package io.ebeaninternal.server.cache;
|
||||
|
||||
import io.ebean.cache.ServerCache;
|
||||
import io.ebean.cache.ServerCacheRegion;
|
||||
import io.ebean.cache.ServerCacheType;
|
||||
import io.ebeaninternal.api.SpiCacheRegion;
|
||||
import io.ebeaninternal.server.cluster.ClusterManager;
|
||||
import io.ebeaninternal.server.deploy.DCacheRegion;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Manages the bean and query caches.
|
||||
*/
|
||||
public class DefaultServerCacheManager implements SpiCacheManager {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger("io.ebean.cache.REGION");
|
||||
|
||||
private final Map<String, SpiCacheRegion> regionMap = new HashMap<>();
|
||||
|
||||
private final ClusterManager clusterManager;
|
||||
|
||||
private final DefaultCacheHolder cacheHolder;
|
||||
@@ -39,6 +54,51 @@ public class DefaultServerCacheManager implements SpiCacheManager {
|
||||
return localL2Caching;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ServerCacheRegion> allRegions() {
|
||||
return new ArrayList<>(regionMap.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabledRegions(String regions) {
|
||||
if (regions != null) {
|
||||
List<String> enabledRegionNames = Arrays.asList(regions.split(","));
|
||||
|
||||
List<String> disabled = new ArrayList<>();
|
||||
List<String> enabled = new ArrayList<>();
|
||||
|
||||
for (SpiCacheRegion region : regionMap.values()) {
|
||||
if (enabledRegionNames.contains(region.getName())) {
|
||||
enabled.add(region.getName());
|
||||
if (!region.isEnabled()) {
|
||||
region.setEnabled(true);
|
||||
log.debug("Cache region[{}] enabled", region.getName());
|
||||
}
|
||||
} else {
|
||||
disabled.add(region.getName());
|
||||
if (region.isEnabled()) {
|
||||
region.setEnabled(false);
|
||||
log.debug("Cache region[{}] disabled", region.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("Cache regions enabled:{} disabled:{}", enabled, disabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAllRegionsEnabled(boolean enabled) {
|
||||
log.debug("All cache regions enabled[{}]", enabled);
|
||||
for (SpiCacheRegion region : regionMap.values()) {
|
||||
region.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpiCacheRegion getRegion(String region) {
|
||||
return regionMap.computeIfAbsent(region, DCacheRegion::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all caches.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package io.ebeaninternal.server.cache;
|
||||
|
||||
import io.ebean.cache.ServerCache;
|
||||
import io.ebean.cache.ServerCacheRegion;
|
||||
import io.ebeaninternal.api.SpiCacheRegion;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The cache service for server side caching of beans and query results.
|
||||
@@ -16,6 +20,28 @@ public interface SpiCacheManager {
|
||||
*/
|
||||
boolean isLocalL2Caching();
|
||||
|
||||
/**
|
||||
* Return all the regions.
|
||||
*/
|
||||
List<ServerCacheRegion> allRegions();
|
||||
|
||||
/**
|
||||
* Set the regions that are enabled.
|
||||
*
|
||||
* @param regions A single region name or a comma delimited list of region names.
|
||||
*/
|
||||
void setEnabledRegions(String regions);
|
||||
|
||||
/**
|
||||
* Enable or disable all cache regions.
|
||||
*/
|
||||
void setAllRegionsEnabled(boolean enabled);
|
||||
|
||||
/**
|
||||
* Return the cache region. We can enable L2 caching by region.
|
||||
*/
|
||||
SpiCacheRegion getRegion(String region);
|
||||
|
||||
/**
|
||||
* Return the cache for mapping natural keys to id values.
|
||||
*/
|
||||
|
||||
@@ -12,45 +12,70 @@ public class CacheOptions {
|
||||
*/
|
||||
public static final CacheOptions NO_CACHING = new CacheOptions();
|
||||
|
||||
public static final CacheOptions INVALIDATE_QUERY_CACHE = new CacheOptions(true);
|
||||
private static final String R0 = "r0";
|
||||
|
||||
private static final CacheOptions INVALIDATE_QUERY_CACHE_R0 = new CacheOptions(true);
|
||||
|
||||
private final boolean invalidateQueryCache;
|
||||
private final boolean enableBeanCache;
|
||||
private final boolean enableQueryCache;
|
||||
private final boolean readOnly;
|
||||
private final String[] naturalKey;
|
||||
private final String region;
|
||||
|
||||
public static CacheOptions invalidateQueryCache(String region) {
|
||||
if (R0.equals(region)) {
|
||||
return INVALIDATE_QUERY_CACHE_R0;
|
||||
} else {
|
||||
return new CacheOptions(true, region);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for no caching.
|
||||
*/
|
||||
private CacheOptions() {
|
||||
invalidateQueryCache = false;
|
||||
enableBeanCache = false;
|
||||
enableQueryCache = false;
|
||||
readOnly = false;
|
||||
naturalKey = null;
|
||||
this.invalidateQueryCache = false;
|
||||
this.enableBeanCache = false;
|
||||
this.enableQueryCache = false;
|
||||
this.readOnly = false;
|
||||
this.naturalKey = null;
|
||||
this.region = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for invalidateQueryCache.
|
||||
*/
|
||||
private CacheOptions(boolean invalidateQueryCache) {
|
||||
this(invalidateQueryCache, R0);
|
||||
}
|
||||
|
||||
private CacheOptions(boolean invalidateQueryCache, String region) {
|
||||
this.invalidateQueryCache = invalidateQueryCache;
|
||||
enableBeanCache = false;
|
||||
enableQueryCache = false;
|
||||
readOnly = false;
|
||||
naturalKey = null;
|
||||
this.enableBeanCache = false;
|
||||
this.enableQueryCache = false;
|
||||
this.readOnly = false;
|
||||
this.naturalKey = null;
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct with cache annotation.
|
||||
*/
|
||||
public CacheOptions(Cache cache, String[] naturalKey) {
|
||||
invalidateQueryCache = false;
|
||||
enableBeanCache = cache.enableBeanCache();
|
||||
enableQueryCache = cache.enableQueryCache();
|
||||
readOnly = cache.readOnly();
|
||||
this.invalidateQueryCache = false;
|
||||
this.enableBeanCache = cache.enableBeanCache();
|
||||
this.enableQueryCache = cache.enableQueryCache();
|
||||
this.readOnly = cache.readOnly();
|
||||
this.naturalKey = naturalKey;
|
||||
this.region = cache.region();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the cache region name.
|
||||
*/
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -446,6 +446,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
if (encryptKeyManager != null) {
|
||||
encryptKeyManager.initialise();
|
||||
}
|
||||
serverCacheManager.setEnabledRegions(serverConfig.getEnabledL2Regions());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -484,7 +485,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
*/
|
||||
private void shutdownInternal(boolean shutdownDataSource, boolean deregisterDriver) {
|
||||
|
||||
logger.debug("Shutting down EbeanServer {}", serverName);
|
||||
logger.debug("Shutting down instance:{}", serverName);
|
||||
if (shutdown) {
|
||||
// already shutdown
|
||||
return;
|
||||
@@ -790,23 +791,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
transactionManager.exitScopedTransaction(returnOrThrowable, opCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current transaction (or null) from the scope.
|
||||
*/
|
||||
@Override
|
||||
public SpiTransaction currentServerTransaction() {
|
||||
return transactionManager.getActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a transaction with 'REQUIRED' semantics.
|
||||
* <p>
|
||||
* If a transaction already exists that transaction will be used.
|
||||
* </p>
|
||||
* <p>
|
||||
* Note that the transaction is stored in a ThreadLocal variable.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public Transaction beginTransaction() {
|
||||
return beginTransaction(TxScope.required());
|
||||
@@ -817,12 +806,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return transactionManager.beginScopedTransaction(txScope);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a transaction with a specific Isolation Level.
|
||||
* <p>
|
||||
* Note that the transaction is stored in a ThreadLocal variable.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public Transaction beginTransaction(TxIsolation isolation) {
|
||||
// start an explicit transaction
|
||||
@@ -837,10 +820,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
return t;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current transaction or null if there is not one currently in
|
||||
* scope.
|
||||
*/
|
||||
@Override
|
||||
public Transaction currentTransaction() {
|
||||
return transactionManager.getActive();
|
||||
@@ -851,50 +830,16 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
currentTransaction().flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the current transaction.
|
||||
*/
|
||||
@Override
|
||||
public void commitTransaction() {
|
||||
currentTransaction().commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback the current transaction.
|
||||
*/
|
||||
@Override
|
||||
public void rollbackTransaction() {
|
||||
currentTransaction().rollback();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current transaction has already been committed do nothing otherwise
|
||||
* rollback the transaction.
|
||||
* <p>
|
||||
* Useful to put in a finally block to ensure the transaction is ended, rather
|
||||
* than a rollbackTransaction() in each catch block.
|
||||
* </p>
|
||||
* <p>
|
||||
* Code example:<br />
|
||||
* <p>
|
||||
* <pre>
|
||||
* <code>
|
||||
* Ebean.startTransaction();
|
||||
* try {
|
||||
* // do some fetching and or persisting
|
||||
*
|
||||
* // commit at the end
|
||||
* Ebean.commitTransaction();
|
||||
*
|
||||
* } finally {
|
||||
* // if commit didn't occur then rollback the transaction
|
||||
* Ebean.endTransaction();
|
||||
* }
|
||||
* </code>
|
||||
* </pre>
|
||||
* <p>
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public void endTransaction() {
|
||||
Transaction transaction = transactionManager.getInScope();
|
||||
@@ -903,13 +848,6 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* return the next unique identity value.
|
||||
* <p>
|
||||
* Uses the BeanDescriptor deployment information to determine the sequence to
|
||||
* use.
|
||||
* </p>
|
||||
*/
|
||||
@Override
|
||||
public Object nextId(Class<?> beanType) {
|
||||
BeanDescriptor<?> desc = getBeanDescriptor(beanType);
|
||||
@@ -1133,14 +1071,21 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
<T> SpiOrmQueryRequest<T> createQueryRequest(Type type, Query<T> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = buildQueryRequest(type, query, t);
|
||||
request.prepareQuery();
|
||||
return request;
|
||||
}
|
||||
|
||||
<T> SpiOrmQueryRequest<T> buildQueryRequest(Type type, Query<T> query, Transaction t) {
|
||||
|
||||
SpiQuery<T> spiQuery = (SpiQuery<T>) query;
|
||||
spiQuery.setType(type);
|
||||
spiQuery.checkNamedParameters();
|
||||
|
||||
return createQueryRequest(spiQuery, t);
|
||||
return buildQueryRequest(spiQuery, t);
|
||||
}
|
||||
|
||||
private <T> SpiOrmQueryRequest<T> createQueryRequest(SpiQuery<T> query, Transaction t) {
|
||||
private <T> SpiOrmQueryRequest<T> buildQueryRequest(SpiQuery<T> query, Transaction t) {
|
||||
|
||||
if (t == null) {
|
||||
t = currentServerTransaction();
|
||||
@@ -1163,10 +1108,7 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
query.setOrigin(createCallStack());
|
||||
}
|
||||
|
||||
OrmQueryRequest<T> request = new OrmQueryRequest<>(this, queryEngine, query, (SpiTransaction) t);
|
||||
request.prepareQuery();
|
||||
|
||||
return request;
|
||||
return new OrmQueryRequest<>(this, queryEngine, query, (SpiTransaction) t);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1237,7 +1179,8 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(spiQuery, t);
|
||||
SpiOrmQueryRequest<T> request = buildQueryRequest(spiQuery, t);
|
||||
request.prepareQuery();
|
||||
if (request.isUseDocStore()) {
|
||||
return docStore().find(request);
|
||||
}
|
||||
@@ -1590,15 +1533,18 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> List<T> findList(Query<T> query, Transaction t, boolean findOne) {
|
||||
|
||||
SpiOrmQueryRequest<T> request = createQueryRequest(Type.LIST, query, t);
|
||||
SpiOrmQueryRequest<T> request = buildQueryRequest(Type.LIST, query, t);
|
||||
request.resetBeanCacheAutoMode(findOne);
|
||||
if ((t == null || !t.isSkipCache()) && request.getFromBeanCache()) {
|
||||
// hit bean cache and got all results from cache
|
||||
return request.getBeanCacheHits();
|
||||
}
|
||||
|
||||
request.prepareQuery();
|
||||
Object result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
return (List<T>) result;
|
||||
}
|
||||
if ((t == null || !t.isSkipCache()) && request.getFromBeanCache()) {
|
||||
return request.getBeanCacheHits();
|
||||
}
|
||||
if (request.isUseDocStore()) {
|
||||
return docStore().findList(request);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import io.ebean.event.BeanQueryAdapter;
|
||||
import io.ebean.text.json.JsonReadOptions;
|
||||
import io.ebeaninternal.api.BeanCacheResult;
|
||||
import io.ebeaninternal.api.CQueryPlanKey;
|
||||
import io.ebeaninternal.api.CacheIdLookup;
|
||||
import io.ebeaninternal.api.HashQuery;
|
||||
import io.ebeaninternal.api.LoadContext;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
@@ -208,7 +209,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
/**
|
||||
* Prepare the query and calculate the query plan key.
|
||||
*/
|
||||
void prepareQuery() {
|
||||
@Override
|
||||
public void prepareQuery() {
|
||||
beanDescriptor.prepareQuery(query);
|
||||
adapterPreQuery();
|
||||
this.secondaryQueries = query.convertJoins();
|
||||
@@ -598,6 +600,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
// - merge the 2 results and return
|
||||
//
|
||||
|
||||
CacheIdLookup<T> idLookup = query.cacheIdLookup();
|
||||
if (idLookup != null) {
|
||||
BeanCacheResult<T> cacheResult = beanDescriptor.cacheIdLookup(persistenceContext, idLookup.idValues());
|
||||
// adjust the query (IN clause) based on the cache hits
|
||||
this.cacheBeans = idLookup.removeHits(cacheResult);
|
||||
return idLookup.allHits();
|
||||
}
|
||||
|
||||
if (!beanDescriptor.isNaturalKeyCaching()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ public interface SpiOrmQueryRequest<T> extends BeanQueryRequest<T>, DocQueryRequ
|
||||
*/
|
||||
BeanDescriptor<T> getBeanDescriptor();
|
||||
|
||||
/**
|
||||
* Prepare the query for execution.
|
||||
*/
|
||||
void prepareQuery();
|
||||
|
||||
/**
|
||||
* This will create a local (readOnly) transaction if no current transaction
|
||||
* exists.
|
||||
|
||||
@@ -1490,6 +1490,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return cacheHelp.beanCacheLoad(bean, ebi, cacheKey(id), context);
|
||||
}
|
||||
|
||||
public BeanCacheResult<T> cacheIdLookup(PersistenceContext context, Collection<?> ids) {
|
||||
return cacheHelp.cacheIdLookup(context, ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use natural key lookup to hit the bean cache.
|
||||
*/
|
||||
|
||||
@@ -7,6 +7,8 @@ import io.ebean.bean.PersistenceContext;
|
||||
import io.ebean.cache.QueryCacheEntry;
|
||||
import io.ebean.cache.ServerCache;
|
||||
import io.ebeaninternal.api.BeanCacheResult;
|
||||
import io.ebeaninternal.api.SpiCacheControl;
|
||||
import io.ebeaninternal.api.SpiCacheRegion;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.api.TransactionEventTable.TableIUD;
|
||||
import io.ebeaninternal.server.cache.CacheChangeSet;
|
||||
@@ -72,6 +74,9 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
|
||||
private final boolean noCaching;
|
||||
|
||||
private final SpiCacheControl cacheControl;
|
||||
private final SpiCacheRegion cacheRegion;
|
||||
|
||||
/**
|
||||
* Set to true if all persist changes need to notify the cache.
|
||||
*/
|
||||
@@ -113,6 +118,13 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
this.naturalKeyCache = null;
|
||||
}
|
||||
this.noCaching = (beanCache == null && queryCache == null);
|
||||
if (noCaching) {
|
||||
this.cacheControl = DCacheControlNone.INSTANCE;
|
||||
this.cacheRegion = (invalidateQueryCache) ? cacheManager.getRegion(cacheOptions.getRegion()) : DCacheRegionNone.INSTANCE;
|
||||
} else {
|
||||
this.cacheRegion = cacheManager.getRegion(cacheOptions.getRegion());
|
||||
this.cacheControl = new DCacheControl(cacheRegion, (beanCache != null), (naturalKeyCache != null), (queryCache != null));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -123,8 +135,8 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
cacheNotifyOnDelete = !cacheNotifyOnAll && isNotifyOnDeletes();
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
if (isBeanCaching() || isQueryCaching() || cacheNotifyOnAll || cacheNotifyOnDelete) {
|
||||
String notifyMode = cacheNotifyOnAll ? "All" : (cacheNotifyOnDelete ? "Delete" : "None");
|
||||
if (cacheNotifyOnAll || cacheNotifyOnDelete) {
|
||||
String notifyMode = cacheNotifyOnAll ? "All" : "Delete";
|
||||
logger.debug("l2 caching on {} - beanCaching:{} queryCaching:{} notifyMode:{} ",
|
||||
desc.getFullName(), isBeanCaching(), isQueryCaching(), notifyMode);
|
||||
}
|
||||
@@ -148,36 +160,36 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
* Return true if the persist request needs to notify the cache.
|
||||
*/
|
||||
boolean isCacheNotify(PersistRequest.Type type) {
|
||||
return cacheNotifyOnAll
|
||||
|| cacheNotifyOnDelete && (type == PersistRequest.Type.DELETE || type == PersistRequest.Type.DELETE_PERMANENT);
|
||||
return cacheRegion.isEnabled()
|
||||
&& (cacheNotifyOnAll || cacheNotifyOnDelete && (type == PersistRequest.Type.DELETE || type == PersistRequest.Type.DELETE_PERMANENT));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is currently query caching for this type of bean.
|
||||
*/
|
||||
boolean isQueryCaching() {
|
||||
return queryCache != null;
|
||||
return cacheControl.isQueryCaching();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is currently bean caching for this type of bean.
|
||||
*/
|
||||
boolean isBeanCaching() {
|
||||
return beanCache != null;
|
||||
return cacheControl.isBeanCaching();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is natural key caching for this type of bean.
|
||||
*/
|
||||
boolean isNaturalKeyCaching() {
|
||||
return naturalKeyCache != null;
|
||||
return cacheControl.isNaturalKeyCaching();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there is bean or query caching on this type.
|
||||
*/
|
||||
boolean isCaching() {
|
||||
return beanCache != null || queryCache != null;
|
||||
return cacheControl.isCaching();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,7 +218,7 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
/**
|
||||
* Add query cache clear to the changeSet.
|
||||
*/
|
||||
void queryCacheClear(CacheChangeSet changeSet) {
|
||||
private void queryCacheClear(CacheChangeSet changeSet) {
|
||||
if (queryCache != null) {
|
||||
changeSet.addClearQuery(desc);
|
||||
}
|
||||
@@ -292,8 +304,8 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object ownerBean = bc.getOwnerBean();
|
||||
EntityBeanIntercept ebi = ((EntityBean) ownerBean)._ebean_getIntercept();
|
||||
EntityBean ownerBean = bc.getOwnerBean();
|
||||
EntityBeanIntercept ebi = ownerBean._ebean_getIntercept();
|
||||
PersistenceContext persistenceContext = ebi.getPersistenceContext();
|
||||
|
||||
BeanDescriptor<?> targetDescriptor = many.getTargetDescriptor();
|
||||
@@ -362,6 +374,31 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
return new CachedManyIds(idList);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hit the bean cache with the given ids returning the hits.
|
||||
*/
|
||||
BeanCacheResult<T> cacheIdLookup(PersistenceContext context, Collection<?> ids) {
|
||||
|
||||
Set<Object> keys = new HashSet<>(ids.size());
|
||||
for (Object id : ids) {
|
||||
keys.add(desc.cacheKey(id));
|
||||
}
|
||||
|
||||
Map<Object, Object> beanDataMap = beanCache.getAll(keys);
|
||||
if (beanLog.isTraceEnabled()) {
|
||||
beanLog.trace(" GET MANY {}({}) - hits:{}", cacheName, ids, beanDataMap.keySet());
|
||||
}
|
||||
|
||||
BeanCacheResult<T> result = new BeanCacheResult<>();
|
||||
for (Map.Entry<Object, Object> entry : beanDataMap.entrySet()) {
|
||||
CachedBeanData cachedBeanData = (CachedBeanData) entry.getValue();
|
||||
T bean = convertToBean(entry.getKey(), false, context, cachedBeanData);
|
||||
result.add(bean, desc.getBeanId(bean));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use natural keys to hit the bean cache and return resulting hits.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebeaninternal.api.SpiCacheControl;
|
||||
import io.ebeaninternal.api.SpiCacheRegion;
|
||||
|
||||
/**
|
||||
* Default implementation of SpiCacheControl.
|
||||
*/
|
||||
class DCacheControl implements SpiCacheControl {
|
||||
|
||||
private final SpiCacheRegion region;
|
||||
private final boolean bean;
|
||||
private final boolean naturalKey;
|
||||
private final boolean query;
|
||||
|
||||
DCacheControl(SpiCacheRegion region, boolean bean, boolean naturalKey, boolean query) {
|
||||
this.region = region;
|
||||
this.bean = bean;
|
||||
this.naturalKey = naturalKey;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCaching() {
|
||||
return (bean || query) && region.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBeanCaching() {
|
||||
return bean && region.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNaturalKeyCaching() {
|
||||
return naturalKey && region.isEnabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isQueryCaching() {
|
||||
return query && region.isEnabled();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebeaninternal.api.SpiCacheControl;
|
||||
|
||||
/**
|
||||
* Cache control used when no caching is on the bean type.
|
||||
*/
|
||||
class DCacheControlNone implements SpiCacheControl {
|
||||
|
||||
static final SpiCacheControl INSTANCE = new DCacheControlNone();
|
||||
|
||||
@Override
|
||||
public boolean isCaching() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBeanCaching() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNaturalKeyCaching() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isQueryCaching() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebeaninternal.api.SpiCacheRegion;
|
||||
|
||||
public class DCacheRegion implements SpiCacheRegion {
|
||||
|
||||
private boolean enabled = true;
|
||||
|
||||
private final String name;
|
||||
|
||||
public DCacheRegion(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebeaninternal.api.SpiCacheRegion;
|
||||
|
||||
class DCacheRegionNone implements SpiCacheRegion {
|
||||
|
||||
static SpiCacheRegion INSTANCE = new DCacheRegionNone();
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "<none>";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
throw new IllegalStateException("Not expected");
|
||||
}
|
||||
}
|
||||
@@ -473,8 +473,8 @@ public class DeployBeanDescriptor<T> {
|
||||
/**
|
||||
* Set that this type invalidates query caches.
|
||||
*/
|
||||
public void setInvalidateQueryCache() {
|
||||
this.cacheOptions = CacheOptions.INVALIDATE_QUERY_CACHE;
|
||||
public void setInvalidateQueryCache(String region) {
|
||||
this.cacheOptions = CacheOptions.invalidateQueryCache(region);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -202,13 +202,12 @@ public class AnnotationClass extends AnnotationParser {
|
||||
} else {
|
||||
InvalidateQueryCache invalidateQueryCache = AnnotationUtil.findAnnotationRecursive(cls, InvalidateQueryCache.class);
|
||||
if (invalidateQueryCache != null) {
|
||||
descriptor.setInvalidateQueryCache();
|
||||
descriptor.setInvalidateQueryCache(invalidateQueryCache.region());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<NamedQuery> namedQueries = AnnotationUtil.findAnnotationsRecursive(cls, NamedQuery.class);
|
||||
for (NamedQuery namedQuery : namedQueries) {
|
||||
for (NamedQuery namedQuery : AnnotationUtil.findAnnotationsRecursive(cls, NamedQuery.class)) {
|
||||
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,20 +9,38 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* In a collection of Id values.
|
||||
*/
|
||||
public class IdInExpression extends NonPrepareExpression {
|
||||
|
||||
private final Collection<?> idCollection;
|
||||
private final List<Object> idCollection;
|
||||
|
||||
private boolean multiValueIdSupported;
|
||||
|
||||
public IdInExpression(Collection<?> idCollection) {
|
||||
this.idCollection = idCollection;
|
||||
this.idCollection = new ArrayList<>(idCollection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ids this expression is looking to fetch.
|
||||
*/
|
||||
public Collection<Object> idValues() {
|
||||
return idCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove Ids that where obtained from l2 cache. Don't fetch these from DB.
|
||||
*/
|
||||
public int removeIds(Set<Object> hitIds) {
|
||||
idCollection.removeAll(hitIds);
|
||||
return idCollection.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -63,23 +63,25 @@ class InExpression extends AbstractExpression {
|
||||
return vals;
|
||||
}
|
||||
|
||||
private List<Object> initBindValues() {
|
||||
if (bindValues == null) {
|
||||
bindValues = values();
|
||||
}
|
||||
return bindValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean naturalKey(NaturalKeyQueryData<?> data) {
|
||||
// can't use naturalKey cache for NOT IN or when "empty"
|
||||
if (not || empty) {
|
||||
return false;
|
||||
}
|
||||
List<Object> copy = data.matchIn(propName, bindValues);
|
||||
if (copy == null) {
|
||||
return false;
|
||||
}
|
||||
bindValues = copy;
|
||||
return true;
|
||||
return data.matchIn(propName, initBindValues());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
bindValues = values();
|
||||
initBindValues();
|
||||
if (bindValues.size() > 0) {
|
||||
multiValueSupported = request.isMultiValueSupported((bindValues.get(0)).getClass());
|
||||
}
|
||||
@@ -146,9 +148,7 @@ class InExpression extends AbstractExpression {
|
||||
|
||||
if (prop != null) {
|
||||
request.append(prop.getAssocIdInExpr(propName));
|
||||
String inClause = prop.getAssocIdInValueExpr(not, bindValues.size());
|
||||
request.append(inClause);
|
||||
|
||||
request.append(prop.getAssocIdInValueExpr(not, bindValues.size()));
|
||||
} else {
|
||||
request.append(propName);
|
||||
request.appendInExpression(not, bindValues);
|
||||
|
||||
@@ -54,7 +54,6 @@ class InPairsExpression extends AbstractExpression {
|
||||
|
||||
@Override
|
||||
public void prepareExpression(BeanQueryRequest<?> request) {
|
||||
|
||||
// at this stage translating pairs into varchar via DB concat
|
||||
multiValueSupported = request.isMultiValueSupported(String.class);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ class CQueryBindCapture {
|
||||
|
||||
DQueryPlanOutput queryPlan = planLogger.logQueryPlan(request.getConnection(), cQueryPlan, last);
|
||||
if (queryPlan != null) {
|
||||
queryPlan.with(queryTimeMicros, captureCount, cQueryPlan.getPlanKey().toString());
|
||||
queryPlan.with(queryTimeMicros, captureCount, cQueryPlan.getSqlHash());
|
||||
request.process(queryPlan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.ProfileLocation;
|
||||
import io.ebean.meta.MetaQueryPlan;
|
||||
|
||||
/**
|
||||
@@ -9,29 +10,28 @@ class DQueryPlanOutput implements MetaQueryPlan {
|
||||
|
||||
private final Class<?> beanType;
|
||||
private final String label;
|
||||
|
||||
private final ProfileLocation profileLocation;
|
||||
|
||||
private final String sql;
|
||||
|
||||
private final String bind;
|
||||
|
||||
private final String plan;
|
||||
|
||||
private String planHash;
|
||||
private String sqlHash;
|
||||
private long queryTimeMicros;
|
||||
private long captureCount;
|
||||
|
||||
DQueryPlanOutput(Class<?> beanType, String label, String sql, String bind, String plan) {
|
||||
DQueryPlanOutput(Class<?> beanType, String label, String sql, String bind, String plan, ProfileLocation profileLocation) {
|
||||
this.beanType = beanType;
|
||||
this.label = label;
|
||||
this.sql = sql;
|
||||
this.bind = bind;
|
||||
this.plan = plan;
|
||||
this.profileLocation = profileLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryPlanHash() {
|
||||
return planHash;
|
||||
return sqlHash;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,15 +94,15 @@ class DQueryPlanOutput implements MetaQueryPlan {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + planHash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan;
|
||||
return " BeanType:" + ((beanType == null) ? "" : beanType.getSimpleName()) + " planHash:" + sqlHash + " label:" + label + " queryTimeMicros:" + queryTimeMicros + " captureCount:" + captureCount + "\n SQL:" + sql + "\nBIND:" + bind + "\nPLAN:" + plan;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additionally set the query execution time and the number of bind captures.
|
||||
*/
|
||||
void with(long queryTimeMicros, long captureCount, String planHash) {
|
||||
void with(long queryTimeMicros, long captureCount, String sqlHash) {
|
||||
this.queryTimeMicros = queryTimeMicros;
|
||||
this.captureCount = captureCount;
|
||||
this.planHash = planHash;
|
||||
this.sqlHash = sqlHash;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public abstract class QueryPlanLogger {
|
||||
}
|
||||
|
||||
protected DQueryPlanOutput createPlan(CQueryPlan plan, String bind, String planString) {
|
||||
return new DQueryPlanOutput(plan.getBeanType(), plan.getName(), plan.getSql(), bind, planString);
|
||||
return new DQueryPlanOutput(plan.getBeanType(), plan.getName(), plan.getSql(), bind, planString, plan.getProfileLocation());
|
||||
}
|
||||
|
||||
DQueryPlanOutput readQueryPlanBasic(CQueryPlan plan, BindCapture bind, ResultSet rset) throws SQLException {
|
||||
|
||||
@@ -33,6 +33,7 @@ import io.ebean.event.readaudit.ReadEvent;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebeaninternal.api.BindParams;
|
||||
import io.ebeaninternal.api.CQueryPlanKey;
|
||||
import io.ebeaninternal.api.CacheIdLookup;
|
||||
import io.ebeaninternal.api.HashQuery;
|
||||
import io.ebeaninternal.api.ManyWhereJoins;
|
||||
import io.ebeaninternal.api.NaturalKeyQueryData;
|
||||
@@ -50,6 +51,7 @@ import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.expression.DefaultExpressionList;
|
||||
import io.ebeaninternal.server.expression.IdInExpression;
|
||||
import io.ebeaninternal.server.expression.SimpleExpression;
|
||||
import io.ebeaninternal.server.query.CancelableQuery;
|
||||
import io.ebeaninternal.server.query.NativeSqlQueryPlanKey;
|
||||
@@ -733,6 +735,21 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return !manualId && !distinct && !singleAttribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheIdLookup<T> cacheIdLookup() {
|
||||
if (whereExpressions == null) {
|
||||
return null;
|
||||
}
|
||||
List<SpiExpression> underlyingList = whereExpressions.getUnderlyingList();
|
||||
if (underlyingList.size() == 1) {
|
||||
SpiExpression singleExpression = underlyingList.get(0);
|
||||
if (singleExpression instanceof IdInExpression) {
|
||||
return new CacheIdLookup<>((IdInExpression)singleExpression);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalKeyQueryData<T> naturalKey() {
|
||||
|
||||
@@ -1848,8 +1865,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
@Override
|
||||
public void checkNamedParameters() {
|
||||
if (namedParams != null) {
|
||||
Collection<ONamedParam> values = namedParams.values();
|
||||
for (ONamedParam value : values) {
|
||||
for (ONamedParam value : namedParams.values()) {
|
||||
value.checkValueSet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public abstract class BaseTestCase {
|
||||
DB_CLOCK_DELTA = 100;
|
||||
}
|
||||
logger.debug("... preStart");
|
||||
if (!AgentLoader.loadAgentFromClasspath("ebean-agent", "debug=1")) {
|
||||
if (!AgentLoader.loadAgentByMainClass("io.ebean.enhance.Transformer", "debug=1")) {
|
||||
logger.info("avaje-ebeanorm-agent not found in classpath - not dynamically loaded");
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package io.ebean.cache;
|
||||
|
||||
import io.ebean.DB;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ServerCacheManagerTest {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
ServerCacheManager cacheManager = DB.getDefault().getServerCacheManager();
|
||||
|
||||
for (ServerCacheRegion region : cacheManager.allRegions()) {
|
||||
assertTrue(region.isEnabled());
|
||||
}
|
||||
|
||||
cacheManager.setEnabledRegions("r0,doesNotExist");
|
||||
assertThat(cacheManager.getRegion("email").isEnabled()).isFalse();
|
||||
assertThat(cacheManager.getRegion("r0").isEnabled()).isTrue();
|
||||
|
||||
cacheManager.setEnabledRegions("r0");
|
||||
assertThat(cacheManager.getRegion("email").isEnabled()).isFalse();
|
||||
assertThat(cacheManager.getRegion("r0").isEnabled()).isTrue();
|
||||
|
||||
cacheManager.setEnabledRegions(null);
|
||||
assertThat(cacheManager.getRegion("email").isEnabled()).isFalse();
|
||||
assertThat(cacheManager.getRegion("r0").isEnabled()).isTrue();
|
||||
|
||||
cacheManager.setEnabledRegions("email");
|
||||
assertThat(cacheManager.getRegion("email").isEnabled()).isTrue();
|
||||
assertThat(cacheManager.getRegion("r0").isEnabled()).isFalse();
|
||||
|
||||
cacheManager.setAllRegionsEnabled(false);
|
||||
for (ServerCacheRegion region : cacheManager.allRegions()) {
|
||||
assertFalse(region.isEnabled());
|
||||
}
|
||||
|
||||
cacheManager.setAllRegionsEnabled(true);
|
||||
for (ServerCacheRegion region : cacheManager.allRegions()) {
|
||||
assertTrue(region.isEnabled());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,7 @@ public class ServerConfigTest {
|
||||
props.setProperty("mappingLocations", "classpath:/foo;bar");
|
||||
props.setProperty("namingConvention", "io.ebean.config.MatchingNamingConvention");
|
||||
props.setProperty("idGeneratorAutomatic", "true");
|
||||
props.setProperty("enabledL2Regions", "r0,users,orgs");
|
||||
|
||||
|
||||
serverConfig.loadFromProperties(props);
|
||||
@@ -85,6 +86,7 @@ public class ServerConfigTest {
|
||||
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade());
|
||||
assertEquals(PlatformConfig.DbUuid.BINARY, serverConfig.getPlatformConfig().getDbUuid());
|
||||
assertEquals(JsonConfig.DateTime.ISO8601, serverConfig.getJsonDateTime());
|
||||
assertEquals("r0,users,orgs", serverConfig.getEnabledL2Regions());
|
||||
|
||||
assertEquals(42, serverConfig.getJdbcFetchSizeFindEach());
|
||||
assertEquals(43, serverConfig.getJdbcFetchSizeFindList());
|
||||
@@ -111,6 +113,9 @@ public class ServerConfigTest {
|
||||
|
||||
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatch());
|
||||
assertEquals(PersistBatch.ALL, serverConfig.getPersistBatchOnCascade());
|
||||
|
||||
serverConfig.setEnabledL2Regions("r0,orgs");
|
||||
assertEquals("r0,orgs", serverConfig.getEnabledL2Regions());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+107
@@ -1,13 +1,21 @@
|
||||
package org.tests.cache;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.cache.ServerCache;
|
||||
import io.ebean.cache.ServerCacheStatistics;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.tests.model.basic.Country;
|
||||
import org.tests.model.basic.OCachedBean;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
@@ -17,6 +25,10 @@ import static org.junit.Assert.assertNotNull;
|
||||
*/
|
||||
public class TestBeanCache extends BaseTestCase {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TestBeanCache.class);
|
||||
|
||||
private ServerCache beanCache = DB.getDefault().getServerCacheManager().getBeanCache(OCachedBean.class);
|
||||
|
||||
@Test
|
||||
public void findById_when_idTypeConverted() {
|
||||
|
||||
@@ -40,7 +52,102 @@ public class TestBeanCache extends BaseTestCase {
|
||||
sql = LoggedSqlCollector.stop();
|
||||
assertNotNull(bean2);
|
||||
assertThat(sql).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void idsInExpression() {
|
||||
|
||||
List<OCachedBean> beans = createBeans();
|
||||
List<Long> ids = beans.stream().map(OCachedBean::getId).collect(Collectors.toList());
|
||||
|
||||
beanCache.clear();
|
||||
beanCache.getStatistics(true);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
log.info("All misses (0 of 3) ...");
|
||||
List<OCachedBean> list = DB.find(OCachedBean.class)
|
||||
.where().idIn(ids)
|
||||
.setUseCache(true)
|
||||
.findList();
|
||||
|
||||
assertThat(list).hasSize(3);
|
||||
assertBeanCacheHitMiss(0, 3);
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
if (isH2()) {
|
||||
assertThat(sql.get(0)).contains("from o_cached_bean t0 where t0.id in (?, ?, ? )");
|
||||
}
|
||||
|
||||
log.info("All hits (3 of 3) ...");
|
||||
list = DB.find(OCachedBean.class)
|
||||
.where().idIn(ids)
|
||||
.setUseCache(true)
|
||||
.findList();
|
||||
|
||||
assertBeanCacheHitMiss(3, 0);
|
||||
assertThat(list).hasSize(3);
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(0); // no misses
|
||||
|
||||
// remove a bean so that we get a "partial" hit (2 out of 3 in cache)
|
||||
beanCache.remove(beans.get(0).getId().toString());
|
||||
|
||||
log.info("Partial hits (2 of 3) ...");
|
||||
list = DB.find(OCachedBean.class)
|
||||
.where().idIn(ids)
|
||||
.setUseCache(true)
|
||||
.findList();
|
||||
|
||||
assertBeanCacheHitMiss(2, 1);
|
||||
assertThat(list).hasSize(3);
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
if (isH2()) {
|
||||
// fetch the miss from DB
|
||||
assertThat(sql.get(0)).contains("from o_cached_bean t0 where t0.id in (? )");
|
||||
}
|
||||
|
||||
// remove beans so that we get a "partial" hit (1 out of 3 in cache)
|
||||
beanCache.remove(beans.get(1).getId().toString());
|
||||
beanCache.remove(beans.get(2).getId().toString());
|
||||
|
||||
log.info("Partial hits (1 of 3) ...");
|
||||
list = DB.find(OCachedBean.class)
|
||||
.where().idIn(ids)
|
||||
.setUseCache(true)
|
||||
.findList();
|
||||
|
||||
assertBeanCacheHitMiss(1, 2);
|
||||
assertThat(list).hasSize(3);
|
||||
sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
if (isH2()) {
|
||||
// fetch the misses from DB
|
||||
assertThat(sql.get(0)).contains("from o_cached_bean t0 where t0.id in (?, ? )");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertBeanCacheHitMiss(int hitCount, int missCount) {
|
||||
ServerCacheStatistics statistics = beanCache.getStatistics(true);
|
||||
assertThat(statistics.getHitCount()).isEqualTo(hitCount);
|
||||
assertThat(statistics.getMissCount()).isEqualTo(missCount);
|
||||
}
|
||||
|
||||
private List<OCachedBean> createBeans() {
|
||||
|
||||
List<String> names = Arrays.asList("z0", "z1", "z2");
|
||||
|
||||
List<OCachedBean> beans = new ArrayList<>();
|
||||
for (String name : names) {
|
||||
OCachedBean bean = new OCachedBean();
|
||||
bean.setName(name);
|
||||
beans.add(bean);
|
||||
}
|
||||
|
||||
DB.saveAll(beans);
|
||||
|
||||
return beans;
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -8,7 +8,7 @@ import javax.persistence.ManyToOne;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Entity
|
||||
@Cache
|
||||
@Cache(region = "email")
|
||||
public class PersonCacheEmail {
|
||||
|
||||
@Id
|
||||
|
||||
@@ -7,7 +7,7 @@ import javax.persistence.Id;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Entity
|
||||
@Cache
|
||||
@Cache(region = "email")
|
||||
public class PersonCacheInfo {
|
||||
|
||||
@Id
|
||||
|
||||
+64
-15
@@ -1,17 +1,31 @@
|
||||
package org.tests.cache.personinfo;
|
||||
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.DB;
|
||||
import io.ebean.cache.ServerCache;
|
||||
import io.ebean.cache.ServerCacheRegion;
|
||||
import io.ebean.cache.ServerCacheStatistics;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PersonCacheTests {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(PersonCacheTests.class);
|
||||
|
||||
private final List<Object> ids = Arrays.asList("E001", "E002", "E003");
|
||||
|
||||
private ServerCache beanCacheInfo = DB.getDefault().getServerCacheManager().getBeanCache(PersonCacheInfo.class);
|
||||
private ServerCache beanCacheEmail = DB.getDefault().getServerCacheManager().getBeanCache(PersonCacheEmail.class);
|
||||
|
||||
private ServerCacheRegion region = DB.getDefault().getServerCacheManager().getRegion("email");
|
||||
|
||||
private void insert(int id, String email) {
|
||||
|
||||
PersonCacheInfo person = new PersonCacheInfo("P00" + id, "P00" + id);
|
||||
@@ -19,8 +33,8 @@ public class PersonCacheTests {
|
||||
PersonCacheEmail personEmail = new PersonCacheEmail("E00" + id, email);
|
||||
personEmail.setPersonInfo(person);
|
||||
|
||||
Ebean.save(person);
|
||||
Ebean.save(personEmail);
|
||||
DB.save(person);
|
||||
DB.save(personEmail);
|
||||
}
|
||||
|
||||
private void addTestData() {
|
||||
@@ -37,18 +51,15 @@ public class PersonCacheTests {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
Ebean.find(PersonCacheInfo.class)
|
||||
DB.find(PersonCacheInfo.class)
|
||||
.select("personId") // do not fetch name
|
||||
.setUseCache(true)
|
||||
.findList();
|
||||
|
||||
ServerCache beanCache = Ebean.getDefaultServer().getServerCacheManager().getBeanCache(PersonCacheInfo.class);
|
||||
beanCache.getStatistics(true);
|
||||
|
||||
List<Object> ids = Arrays.asList(new String[]{"E001", "E002", "E003"});
|
||||
beanCacheInfo.getStatistics(true);
|
||||
|
||||
List<PersonCacheEmail> emailList =
|
||||
Ebean.find(PersonCacheEmail.class)
|
||||
DB.find(PersonCacheEmail.class)
|
||||
.where().idIn(ids)
|
||||
.setUseCache(true)
|
||||
.findList();
|
||||
@@ -57,23 +68,23 @@ public class PersonCacheTests {
|
||||
for (PersonCacheEmail email : emailList) {
|
||||
// get property that isn't in the cached data
|
||||
assertThat(email.getPersonInfo().getName()).isNotNull();
|
||||
System.out.println(email.getPersonInfo().getName());
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(3);
|
||||
|
||||
assertThat(beanCache.getStatistics(true).getHitCount()).isEqualTo(3);
|
||||
assertThat(beanCacheInfo.getStatistics(true).getHitCount()).isEqualTo(3);
|
||||
|
||||
System.out.println("Fetch again ...");
|
||||
// force cache misses on PersonCacheEmail
|
||||
beanCacheEmail.clear();
|
||||
log.info("Fetch again - cache missing on PersonCacheEmail and cache hits on PersonCacheInfo ...");
|
||||
|
||||
emailList =
|
||||
Ebean.find(PersonCacheEmail.class)
|
||||
DB.find(PersonCacheEmail.class)
|
||||
.where().idIn(ids)
|
||||
.setUseCache(true)
|
||||
.findList();
|
||||
|
||||
|
||||
for (PersonCacheEmail email : emailList) {
|
||||
// get property but it is in the cache data now
|
||||
System.out.println(email.getPersonInfo().getName());
|
||||
@@ -83,7 +94,45 @@ public class PersonCacheTests {
|
||||
sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
|
||||
assertThat(beanCache.getStatistics(true).getHitCount()).isEqualTo(3);
|
||||
assertThat(beanCacheInfo.getStatistics(true).getHitCount()).isEqualTo(3);
|
||||
|
||||
turnOffRegion_expect_noCacheUse();
|
||||
|
||||
}
|
||||
|
||||
private void turnOffRegion_expect_noCacheUse() {
|
||||
|
||||
assertTrue(region.isEnabled());
|
||||
beanCacheEmail.getStatistics(true);
|
||||
|
||||
log.info("Disabled region ...");
|
||||
region.setEnabled(false);
|
||||
|
||||
for (Object id : ids) {
|
||||
DB.find(PersonCacheEmail.class)
|
||||
.where().idEq(id)
|
||||
.setUseCache(true)
|
||||
.findOne();
|
||||
}
|
||||
|
||||
// assert that we didn't hit the cache
|
||||
ServerCacheStatistics statistics = beanCacheEmail.getStatistics(true);
|
||||
assertThat(statistics.getHitCount()).isEqualTo(0);
|
||||
assertThat(statistics.getMissCount()).isEqualTo(0);
|
||||
|
||||
log.info("Enabled region ...");
|
||||
region.setEnabled(true);
|
||||
|
||||
for (Object id : ids) {
|
||||
DB.find(PersonCacheEmail.class)
|
||||
.where().idEq(id)
|
||||
.setUseCache(true)
|
||||
.findOne();
|
||||
}
|
||||
|
||||
// assert that we DID hit the cache
|
||||
statistics = beanCacheEmail.getStatistics(true);
|
||||
assertThat(statistics.getHitCount()).isEqualTo(3);
|
||||
assertThat(statistics.getMissCount()).isEqualTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
+46
-8
@@ -8,6 +8,8 @@ import io.ebean.cache.ServerCacheManager;
|
||||
import io.ebean.cache.ServerCacheStatistics;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -17,6 +19,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestCacheViaComplexNaturalKey extends BaseTestCase {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(TestCacheViaComplexNaturalKey.class);
|
||||
|
||||
private ServerCache beanCache = cacheManager().getBeanCache(OCachedNatKeyBean.class);
|
||||
private ServerCache natKeyCache = cacheManager().getNaturalKeyCache(OCachedNatKeyBean.class);
|
||||
|
||||
@@ -29,11 +33,8 @@ public class TestCacheViaComplexNaturalKey extends BaseTestCase {
|
||||
private static synchronized void insertSome() {
|
||||
if (!loadOnce) {
|
||||
Ebean.find(OCachedNatKeyBean.class).delete();
|
||||
|
||||
List<String> stores = new ArrayList<>(Arrays.asList("abc", "def"));
|
||||
for (String store : stores) {
|
||||
List<String> skus = new ArrayList<>(Arrays.asList("1", "2", "3", "4"));
|
||||
for (String sku : skus) {
|
||||
for (String store : Arrays.asList("abc", "def")) {
|
||||
for (String sku : Arrays.asList("1", "2", "3", "4")) {
|
||||
saveBean(store, sku);
|
||||
}
|
||||
}
|
||||
@@ -92,10 +93,11 @@ public class TestCacheViaComplexNaturalKey extends BaseTestCase {
|
||||
loadSomeIntoCache();
|
||||
|
||||
String storeId = "abc";
|
||||
List<String> skus = new ArrayList<>(Arrays.asList("1", "2", "3"));
|
||||
List<String> skus = Arrays.asList("1", "2", "3");
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
log.info("Partial (2 of 3) ...");
|
||||
List<OCachedNatKeyBean> list = Ebean.find(OCachedNatKeyBean.class)
|
||||
.where()
|
||||
.eq("store", storeId)
|
||||
@@ -104,7 +106,7 @@ public class TestCacheViaComplexNaturalKey extends BaseTestCase {
|
||||
.orderBy("sku desc")
|
||||
.findList();
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
if (isH2()) {
|
||||
// in clause with only 1 bind param - (sku=3 ... we got hits on sku 1 and 2)
|
||||
@@ -112,9 +114,45 @@ public class TestCacheViaComplexNaturalKey extends BaseTestCase {
|
||||
}
|
||||
|
||||
assertThat(list).hasSize(3);
|
||||
|
||||
assertNaturalKeyHitMiss(2, 1);
|
||||
assertBeanCacheHitMiss(2, 0);
|
||||
|
||||
list = Ebean.find(OCachedNatKeyBean.class)
|
||||
.where()
|
||||
.eq("store", storeId)
|
||||
.in("sku", skus)
|
||||
.setUseCache(true)
|
||||
.orderBy("sku desc")
|
||||
.findList();
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
|
||||
assertThat(list).hasSize(3);
|
||||
assertThat(sql).hasSize(0);
|
||||
assertNaturalKeyHitMiss(3, 0);
|
||||
assertBeanCacheHitMiss(3, 0);
|
||||
|
||||
natKeyCache.remove("abc;1;");
|
||||
natKeyCache.remove("abc;3;");
|
||||
|
||||
log.info("Partial (1 of 3) ...");
|
||||
list = Ebean.find(OCachedNatKeyBean.class)
|
||||
.where()
|
||||
.eq("store", storeId)
|
||||
.in("sku", skus)
|
||||
.setUseCache(true)
|
||||
.orderBy("sku desc")
|
||||
.findList();
|
||||
|
||||
sql = LoggedSqlCollector.stop();
|
||||
|
||||
assertThat(list).hasSize(3);
|
||||
assertThat(sql).hasSize(1);
|
||||
if (isH2()) {
|
||||
assertThat(sql.get(0)).contains("where t0.store = ? and t0.sku in (?, ? ) order by t0.sku desc; --bind(abc,Array[2]={1,3})");
|
||||
}
|
||||
assertNaturalKeyHitMiss(1, 2);
|
||||
assertBeanCacheHitMiss(1, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -86,12 +86,13 @@
|
||||
<logger name="io.ebean.TXN" level="TRACE"/>
|
||||
<logger name="io.ebean.SUM" level="TRACE"/>
|
||||
|
||||
<!--<logger name="io.ebean.cache.TABLEMOD" level="TRACE"/>-->
|
||||
<!-- <logger name="io.ebean.cache.REGION" level="TRACE"/>-->
|
||||
<!-- <logger name="io.ebean.cache.TABLEMOD" level="TRACE"/>-->
|
||||
|
||||
<!--<logger name="io.ebean.cache.QUERY" level="TRACE"/>-->
|
||||
<!--<logger name="io.ebean.cache.BEAN" level="TRACE"/>-->
|
||||
<!--<logger name="io.ebean.cache.NATKEY" level="TRACE"/>-->
|
||||
<!--<logger name="io.ebean.cache.COLL" level="TRACE"/>-->
|
||||
<!-- <logger name="io.ebean.cache.QUERY" level="TRACE"/>-->
|
||||
<!-- <logger name="io.ebean.cache.BEAN" level="TRACE"/>-->
|
||||
<!-- <logger name="io.ebean.cache.NATKEY" level="TRACE"/>-->
|
||||
<!-- <logger name="io.ebean.cache.COLL" level="TRACE"/>-->
|
||||
|
||||
<!--<logger name="io.ebeaninternal.server.deploy" level="TRACE"/>-->
|
||||
|
||||
|
||||
Reference in New Issue
Block a user