mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-27 00:02:42 +00:00
#1796 - ENH: add query.fetchCache(path) ... for explicitly building part of graph from L2 cache
This commit is contained in:
@@ -148,6 +148,8 @@ public class FetchConfig implements Serializable {
|
||||
|
||||
private boolean queryAll;
|
||||
|
||||
private boolean cache;
|
||||
|
||||
/**
|
||||
* Construct the fetch configuration object.
|
||||
*/
|
||||
@@ -188,6 +190,17 @@ public class FetchConfig implements Serializable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eagerly fetch the beans fetching the beans from the L2 bean cache
|
||||
* and using the DB for beans not in the cache.
|
||||
*/
|
||||
public FetchConfig cache() {
|
||||
this.cache = true;
|
||||
this.queryBatchSize = 0;
|
||||
this.queryAll = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eagerly fetch the beans in this path as a separate query (rather than as
|
||||
* part of the main query).
|
||||
@@ -247,6 +260,13 @@ public class FetchConfig implements Serializable {
|
||||
return queryAll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this uses L2 bean cache.
|
||||
*/
|
||||
public boolean isCache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
@@ -255,6 +275,7 @@ public class FetchConfig implements Serializable {
|
||||
FetchConfig that = (FetchConfig) o;
|
||||
if (lazyBatchSize != that.lazyBatchSize) return false;
|
||||
if (queryBatchSize != that.queryBatchSize) return false;
|
||||
if (cache != that.cache) return false;
|
||||
return queryAll == that.queryAll;
|
||||
}
|
||||
|
||||
@@ -263,6 +284,7 @@ public class FetchConfig implements Serializable {
|
||||
int result = lazyBatchSize;
|
||||
result = 92821 * result + queryBatchSize;
|
||||
result = 92821 * result + (queryAll ? 1 : 0);
|
||||
result = 92821 * result + (cache ? 1 : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,12 +67,26 @@ public interface FetchGroupBuilder<T> {
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetchQuery(String path);
|
||||
|
||||
/**
|
||||
* Fetch the path including all its properties using L2 cache.
|
||||
* Cache misses fallback to fetchQuery().
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetchCache(String path);
|
||||
|
||||
/**
|
||||
* Fetch the path including specified properties using a query join.
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetchQuery(String path, String properties);
|
||||
|
||||
/**
|
||||
* Fetch the path including specified properties using L2 cache.
|
||||
* Cache misses fallback to fetchQuery().
|
||||
*/
|
||||
@Nonnull
|
||||
FetchGroupBuilder<T> fetchCache(String path, String properties);
|
||||
|
||||
/**
|
||||
* Fetch the path including all its properties lazily.
|
||||
*/
|
||||
|
||||
@@ -501,6 +501,14 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> fetchQuery(String path, String fetchProperties);
|
||||
|
||||
/**
|
||||
* Fetch the path and properties using L2 bean cache.
|
||||
*
|
||||
* @param path The path of the beans we are fetching from L2 cache.
|
||||
* @param fetchProperties The properties that should be loaded.
|
||||
*/
|
||||
Query<T> fetchCache(String path, String fetchProperties);
|
||||
|
||||
/**
|
||||
* Fetch the path and properties lazily (via batch lazy loading).
|
||||
* <p>
|
||||
@@ -587,6 +595,11 @@ public interface Query<T> {
|
||||
*/
|
||||
Query<T> fetchQuery(String path);
|
||||
|
||||
/**
|
||||
* Fetch the path eagerly using L2 cache.
|
||||
*/
|
||||
Query<T> fetchCache(String path);
|
||||
|
||||
/**
|
||||
* Fetch the path lazily (via batch lazy loading).
|
||||
* <p>
|
||||
|
||||
@@ -95,13 +95,10 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
private final byte[] flags;
|
||||
|
||||
private boolean fullyLoadedBean;
|
||||
|
||||
private boolean loadedFromCache;
|
||||
private Object[] origValues;
|
||||
|
||||
private Exception[] loadErrors;
|
||||
|
||||
private int lazyLoadProperty = -1;
|
||||
|
||||
private Object ownerId;
|
||||
private int sortOrder;
|
||||
|
||||
@@ -311,6 +308,22 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set true when the bean has been loaded from L2 bean cache.
|
||||
* The effect of this is that we should skip the cache if there
|
||||
* is subsequent lazy loading (bean cache partially populated).
|
||||
*/
|
||||
public void setLoadedFromCache(boolean loadedFromCache) {
|
||||
this.loadedFromCache = loadedFromCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this bean was loaded from L2 bean cache.
|
||||
*/
|
||||
public boolean isLoadedFromCache() {
|
||||
return loadedFromCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the bean should be treated as readOnly. If a setter method
|
||||
* is called when it is readOnly an Exception is thrown.
|
||||
@@ -780,7 +793,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
}
|
||||
|
||||
public boolean[] getLoaded() {
|
||||
boolean[] ret= new boolean[flags.length];
|
||||
boolean[] ret = new boolean[flags.length];
|
||||
for (int i = 0; i < ret.length; i++) {
|
||||
ret[i] = (flags[i] & FLAG_LOADED_PROP) != 0;
|
||||
}
|
||||
|
||||
@@ -23,11 +23,14 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
|
||||
private final boolean loadCache;
|
||||
|
||||
private boolean loadedFromCache;
|
||||
|
||||
/**
|
||||
* Construct for lazy load request.
|
||||
*/
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, String lazyLoadProperty, boolean loadCache) {
|
||||
this(LoadBuffer, null, true, lazyLoadProperty, loadCache);
|
||||
public LoadBeanRequest(LoadBeanBuffer LoadBuffer, EntityBeanIntercept ebi, boolean loadCache) {
|
||||
this(LoadBuffer, null, true, ebi.getLazyLoadProperty(), loadCache);
|
||||
this.loadedFromCache = ebi.isLoadedFromCache();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,11 +55,17 @@ public class LoadBeanRequest extends LoadRequest {
|
||||
return loadBuffer.getBeanDescriptor().getBeanType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the beans invoking lazy loading were previously loaded from cache.
|
||||
*/
|
||||
public boolean isLoadedFromCache() {
|
||||
return loadedFromCache;
|
||||
}
|
||||
|
||||
private boolean isLoadCache() {
|
||||
return loadCache;
|
||||
}
|
||||
|
||||
|
||||
public String getDescription() {
|
||||
return "path:" + loadBuffer.getFullPath() + " batch:" + batch.size();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ public class CachedBeanDataToBean {
|
||||
public static void load(BeanDescriptor<?> desc, EntityBean bean, CachedBeanData cacheBeanData, PersistenceContext context) {
|
||||
|
||||
EntityBeanIntercept ebi = bean._ebean_getIntercept();
|
||||
// any future lazy loading skips L2 bean cache
|
||||
ebi.setLoadedFromCache(true);
|
||||
|
||||
BeanProperty idProperty = desc.getIdProperty();
|
||||
if (desc.getInheritInfo() != null) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.core;
|
||||
|
||||
import io.ebean.CacheMode;
|
||||
import io.ebean.ExpressionList;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
@@ -38,10 +39,6 @@ class DefaultBeanLoader {
|
||||
this.onIterateUseExtraTxn = server.getDatabasePlatform().useExtraTransactionOnIterateSecondaryQueries();
|
||||
}
|
||||
|
||||
void refreshMany(EntityBean parentBean, String propertyName) {
|
||||
refreshMany(parentBean, propertyName, null);
|
||||
}
|
||||
|
||||
void loadMany(LoadManyRequest loadRequest) {
|
||||
|
||||
SpiQuery<?> query = loadRequest.createQuery(server);
|
||||
@@ -57,8 +54,8 @@ class DefaultBeanLoader {
|
||||
loadManyInternal(parentBean, propertyName, null, false, onlyIds);
|
||||
}
|
||||
|
||||
private void refreshMany(EntityBean parentBean, String propertyName, Transaction t) {
|
||||
loadManyInternal(parentBean, propertyName, t, true, false);
|
||||
void refreshMany(EntityBean parentBean, String propertyName) {
|
||||
loadManyInternal(parentBean, propertyName, null, true, false);
|
||||
}
|
||||
|
||||
private void loadManyInternal(EntityBean parentBean, String propertyName, Transaction t, boolean refresh, boolean onlyIds) {
|
||||
@@ -159,6 +156,9 @@ class DefaultBeanLoader {
|
||||
|
||||
SpiQuery<?> query = server.createQuery(loadRequest.getBeanType());
|
||||
loadRequest.configureQuery(query, idList);
|
||||
if (loadRequest.isLoadedFromCache()) {
|
||||
query.setBeanCacheMode(CacheMode.PUT);
|
||||
}
|
||||
|
||||
List<?> list = executeQuery(loadRequest, query);
|
||||
loadRequest.postLoad(list);
|
||||
|
||||
@@ -570,37 +570,31 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
|
||||
@Override
|
||||
public void refreshMany(Object parentBean, String propertyName) {
|
||||
|
||||
beanLoader.refreshMany(checkEntityBean(parentBean), propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMany(LoadManyRequest loadRequest) {
|
||||
|
||||
beanLoader.loadMany(loadRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadMany(BeanCollection<?> bc, boolean onlyIds) {
|
||||
|
||||
beanLoader.loadMany(bc, onlyIds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh(Object bean) {
|
||||
|
||||
beanLoader.refresh(checkEntityBean(bean));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadBean(LoadBeanRequest loadRequest) {
|
||||
|
||||
beanLoader.loadBean(loadRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadBean(EntityBeanIntercept ebi) {
|
||||
|
||||
beanLoader.loadBean(ebi);
|
||||
}
|
||||
|
||||
|
||||
@@ -97,6 +97,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
this.queryEngine = queryEngine;
|
||||
this.query = query;
|
||||
this.readOnly = query.isReadOnly();
|
||||
this.persistenceContext = query.getPersistenceContext();
|
||||
}
|
||||
|
||||
public PersistenceException translate(String bindLog, String sql, SQLException e) {
|
||||
|
||||
@@ -2316,6 +2316,13 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
return idBinder.convertSetId(idValue, bean);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Id value to the bean (without type conversion).
|
||||
*/
|
||||
public void setId(Object idValue, EntityBean bean) {
|
||||
idProperty.setValueIntercept(bean, idValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property getProperty(String propName) {
|
||||
return findProperty(propName);
|
||||
|
||||
@@ -675,25 +675,30 @@ final class BeanDescriptorCacheHelp<T> {
|
||||
*/
|
||||
EntityBean loadBeanDirect(Object id, Boolean readOnly, CachedBeanData data, PersistenceContext context) {
|
||||
|
||||
id = desc.convertId(id);
|
||||
EntityBean bean = null;
|
||||
if (context == null) {
|
||||
context = new DefaultPersistenceContext();
|
||||
} else {
|
||||
bean = (EntityBean)desc.contextGet(context, id);
|
||||
}
|
||||
|
||||
if (bean == null) {
|
||||
bean = desc.createEntityBean();
|
||||
desc.setId(id, bean);
|
||||
desc.contextPut(context, id, bean);
|
||||
|
||||
EntityBeanIntercept ebi = bean._ebean_getIntercept();
|
||||
// Not using loadContext here so no batch lazy loading for these beans
|
||||
ebi.setBeanLoader(desc.getEbeanServer());
|
||||
if (Boolean.TRUE.equals(readOnly)) {
|
||||
ebi.setReadOnly(true);
|
||||
}
|
||||
ebi.setPersistenceContext(context);
|
||||
}
|
||||
|
||||
EntityBean bean = desc.createEntityBean();
|
||||
id = desc.convertSetId(id, bean);
|
||||
CachedBeanDataToBean.load(desc, bean, data, context);
|
||||
|
||||
EntityBeanIntercept ebi = bean._ebean_getIntercept();
|
||||
|
||||
// Not using a loadContext for beans coming out of L2 cache
|
||||
// so that means no batch lazy loading for these beans
|
||||
ebi.setBeanLoader(desc.getEbeanServer());
|
||||
if (Boolean.TRUE.equals(readOnly)) {
|
||||
ebi.setReadOnly(true);
|
||||
}
|
||||
ebi.setPersistenceContext(context);
|
||||
desc.contextPut(context, id, bean);
|
||||
|
||||
if (desc.isReadAuditing()) {
|
||||
desc.readAuditBean("l2", "", bean);
|
||||
}
|
||||
|
||||
@@ -24,11 +24,9 @@ public class IdBinderFactory {
|
||||
* Create the IdConvertSet for the given type of Id properties.
|
||||
*/
|
||||
public IdBinder createIdBinder(BeanProperty id) {
|
||||
|
||||
if (id == null) {
|
||||
// for report type beans that don't need an id
|
||||
return EMPTY;
|
||||
|
||||
}
|
||||
if (id.isEmbedded()) {
|
||||
return new IdBinderEmbedded(idInExpandedForm, (BeanPropertyAssocOne<?>) id);
|
||||
|
||||
@@ -191,7 +191,7 @@ class DLoadBeanContext extends DLoadBaseContext implements LoadBeanContext {
|
||||
}
|
||||
}
|
||||
|
||||
LoadBeanRequest req = new LoadBeanRequest(this, ebi.getLazyLoadProperty(), context.hitCache);
|
||||
LoadBeanRequest req = new LoadBeanRequest(this, ebi, context.hitCache);
|
||||
context.desc.getEbeanServer().loadBean(req);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ import io.ebeaninternal.server.querydefn.SpiFetchGroup;
|
||||
*/
|
||||
class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
|
||||
|
||||
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
|
||||
|
||||
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
|
||||
|
||||
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
|
||||
@@ -61,6 +63,12 @@ class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetchCache(String path) {
|
||||
detail.fetch(path, null, FETCH_CACHE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetchLazy(String path) {
|
||||
detail.fetch(path, null, FETCH_LAZY);
|
||||
@@ -79,6 +87,12 @@ class DFetchGroupBuilder<T> implements FetchGroupBuilder<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetchCache(String path, String properties) {
|
||||
detail.fetch(path, properties, FETCH_CACHE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchGroupBuilder<T> fetchLazy(String path, String properties) {
|
||||
detail.fetch(path, properties, FETCH_LAZY);
|
||||
|
||||
@@ -43,6 +43,8 @@ import java.util.function.Predicate;
|
||||
*/
|
||||
class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
|
||||
|
||||
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
|
||||
|
||||
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
|
||||
|
||||
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
|
||||
@@ -76,6 +78,11 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
|
||||
return fetch(property, null, FETCH_QUERY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchCache(String property) {
|
||||
return fetch(property, null, FETCH_CACHE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchLazy(String property) {
|
||||
return fetch(property, null, FETCH_LAZY);
|
||||
@@ -96,6 +103,11 @@ class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T> {
|
||||
return fetch(property, columns, FETCH_QUERY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchCache(String property, String columns) {
|
||||
return fetch(property, columns, FETCH_CACHE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchLazy(String property, String columns) {
|
||||
return fetch(property, columns, FETCH_LAZY);
|
||||
|
||||
@@ -79,6 +79,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
|
||||
private static final String DEFAULT_QUERY_NAME = "default";
|
||||
|
||||
private static final FetchConfig FETCH_CACHE = new FetchConfig().cache();
|
||||
|
||||
private static final FetchConfig FETCH_QUERY = new FetchConfig().query();
|
||||
|
||||
private static final FetchConfig FETCH_LAZY = new FetchConfig().lazy();
|
||||
@@ -1405,6 +1407,10 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return fetch(property, null, FETCH_QUERY);
|
||||
}
|
||||
|
||||
public Query<T> fetchCache(String property) {
|
||||
return fetch(property, null, FETCH_CACHE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchLazy(String property) {
|
||||
return fetch(property, null, FETCH_LAZY);
|
||||
@@ -1425,6 +1431,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return fetch(property, columns, FETCH_QUERY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchCache(String property, String columns) {
|
||||
return fetch(property, columns, FETCH_CACHE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> fetchLazy(String property, String columns) {
|
||||
return fetch(property, columns, FETCH_LAZY);
|
||||
|
||||
@@ -107,6 +107,9 @@ public class OrmQueryProperties implements Serializable {
|
||||
this.readOnly = response.readOnly;
|
||||
if (fetchConfig != null) {
|
||||
this.fetchConfig = fetchConfig;
|
||||
if (fetchConfig.isCache()) {
|
||||
this.cache = true;
|
||||
}
|
||||
} else {
|
||||
this.fetchConfig = response.fetchConfig;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user