package io.ebeaninternal.server.loadcontext;
import io.ebean.CacheMode;
import io.ebean.ProfileLocation;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.CallOrigin;
import io.ebean.bean.EntityBeanIntercept;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.bean.ObjectGraphOrigin;
import io.ebean.bean.PersistenceContext;
import io.ebeaninternal.api.LoadContext;
import io.ebeaninternal.api.LoadSecondaryQuery;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.SpiQuerySecondary;
import io.ebeaninternal.server.autotune.ProfilingListener;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class DLoadContext implements LoadContext {
private final SpiEbeanServer ebeanServer;
private final BeanDescriptor<?> rootDescriptor;
private final Map<String, DLoadBeanContext> beanMap = new HashMap<>();
private final Map<String, DLoadManyContext> manyMap = new HashMap<>();
private final DLoadBeanContext rootBeanContext;
private final boolean asDraft;
private final Timestamp asOf;
private final Boolean readOnly;
private final CacheMode useBeanCache;
private final int defaultBatchSize;
private final boolean disableLazyLoading;
private final boolean disableReadAudit;
private final boolean includeSoftDeletes;
final boolean useDocStore;
private final String relativePath;
private final ObjectGraphOrigin origin;
private final String planLabel;
private final ProfileLocation profileLocation;
private final ProfilingListener profilingListener;
private final Map<String, ObjectGraphNode> nodePathMap = new HashMap<>();
private PersistenceContext persistenceContext;
private List<OrmQueryProperties> secQuery;
private Object tenantId;
public DLoadContext(BeanDescriptor<?> rootDescriptor, PersistenceContext persistenceContext) {
this.useDocStore = true;
this.rootDescriptor = rootDescriptor;
this.ebeanServer = rootDescriptor.getEbeanServer();
this.persistenceContext = persistenceContext;
this.origin = initOrigin();
this.defaultBatchSize = 100;
this.useBeanCache = CacheMode.OFF;
this.asDraft = false;
this.asOf = null;
this.readOnly = false;
this.disableLazyLoading = false;
this.disableReadAudit = false;
this.includeSoftDeletes = false;
this.relativePath = null;
this.planLabel = null;
this.profileLocation = null;
this.profilingListener = null;
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
}
private ObjectGraphOrigin initOrigin() {
CallOrigin callOrigin = ebeanServer.createCallOrigin();
return new ObjectGraphOrigin(0, callOrigin, rootDescriptor.getFullName());
}
public DLoadContext(OrmQueryRequest<?> request, SpiQuerySecondary secondaryQueries) {
this.tenantId = request.getTenantId();
this.persistenceContext = request.getPersistenceContext();
this.ebeanServer = request.getServer();
this.defaultBatchSize = request.getLazyLoadBatchSize();
this.rootDescriptor = request.getBeanDescriptor();
SpiQuery<?> query = request.getQuery();
this.useDocStore = query.isUseDocStore();
this.asOf = query.getAsOf();
this.asDraft = query.isAsDraft();
this.includeSoftDeletes = query.isIncludeSoftDeletes();
this.readOnly = query.isReadOnly();
this.disableReadAudit = query.isDisableReadAudit();
this.disableLazyLoading = query.isDisableLazyLoading();
this.useBeanCache = query.getUseBeanCache();
this.profilingListener = query.getProfilingListener();
this.planLabel = query.getPlanLabel();
this.profileLocation = query.getProfileLocation();
ObjectGraphNode parentNode = query.getParentNode();
if (parentNode != null) {
this.origin = parentNode.getOriginQueryPoint();
this.relativePath = parentNode.getPath();
} else {
this.origin = null;
this.relativePath = null;
}
this.rootBeanContext = new DLoadBeanContext(this, rootDescriptor, null, defaultBatchSize, null);
registerSecondaryQueries(secondaryQueries);
}
String getPlanLabel() {
return planLabel;
}
public ProfileLocation getProfileLocation() {
return profileLocation;
}
private void registerSecondaryQueries(SpiQuerySecondary secondaryQueries) {
this.secQuery = secondaryQueries.getQueryJoins();
if (secQuery != null) {
for (OrmQueryProperties pathProperties : secQuery) {
registerSecondaryQuery(pathProperties);
}
}
List<OrmQueryProperties> lazyJoins = secondaryQueries.getLazyJoins();
if (lazyJoins != null) {
for (OrmQueryProperties lazyJoin : lazyJoins) {
registerSecondaryQuery(lazyJoin);
}
}
}
private void registerSecondaryQuery(OrmQueryProperties props) {
ElPropertyValue elGetValue = rootDescriptor.getElGetValue(props.getPath());
boolean many = elGetValue.getBeanProperty().containsMany();
registerSecondaryNode(many, props);
}
boolean isBeanCacheGet() {
return useBeanCache.isGet();
}
@Override
public int getSecondaryQueriesMinBatchSize(int defaultQueryBatch) {
if (secQuery == null) {
return -1;
}
int maxBatch = 0;
for (OrmQueryProperties aSecQuery : secQuery) {
int batchSize = aSecQuery.getQueryFetchBatch();
if (batchSize == 0) {
batchSize = defaultQueryBatch;
}
maxBatch = Math.max(maxBatch, batchSize);
}
return maxBatch;
}
@Override
public void executeSecondaryQueries(OrmQueryRequest<?> parentRequest, boolean forEach) {
if (secQuery != null) {
for (OrmQueryProperties aSecQuery : secQuery) {
LoadSecondaryQuery load = getLoadSecondaryQuery(aSecQuery.getPath());
load.loadSecondaryQuery(parentRequest, forEach);
}
}
}
private LoadSecondaryQuery getLoadSecondaryQuery(String path) {
LoadSecondaryQuery beanLoad = beanMap.get(path);
return beanLoad == null ? manyMap.get(path) : beanLoad;
}
@Override
public ObjectGraphNode getObjectGraphNode(String path) {
return nodePathMap.computeIfAbsent(path, this::createObjectGraphNode);
}
private ObjectGraphNode createObjectGraphNode(String path) {
if (relativePath != null) {
if (path == null) {
path = relativePath;
} else {
path = relativePath + "." + path;
}
}
return new ObjectGraphNode(origin, path);
}
String getFullPath(String path) {
if (relativePath == null) {
return path;
} else {
return relativePath + "." + path;
}
}
protected SpiEbeanServer getEbeanServer() {
return ebeanServer;
}
protected Boolean isReadOnly() {
return readOnly;
}
@Override
public PersistenceContext getPersistenceContext() {
return persistenceContext;
}
@Override
public void resetPersistenceContext(PersistenceContext persistenceContext) {
this.persistenceContext = persistenceContext;
for (DLoadBeanContext beanContext : beanMap.values()) {
beanContext.clear();
}
for (DLoadManyContext manyContext : manyMap.values()) {
manyContext.clear();
}
this.rootBeanContext.clear();
}
@Override
public void register(String path, EntityBeanIntercept ebi) {
getBeanContext(path).register(ebi);
}
@Override
public void register(String path, EntityBeanIntercept ebi, BeanPropertyAssocOne<?> property) {
getBeanContextWithInherit(path, property).register(ebi);
}
@Override
public void register(String path, BeanPropertyAssocMany<?> many, BeanCollection<?> bc) {
getManyContext(path, many).register(bc);
}
DLoadBeanContext getBeanContext(String path) {
if (path == null) {
return rootBeanContext;
}
return beanMap.computeIfAbsent(path, p -> createBeanContext(p, defaultBatchSize, null));
}
DLoadBeanContext getBeanContextWithInherit(String path, BeanPropertyAssocOne<?> property) {
String key = path + ":" + property.getTargetDescriptor().getName();
return beanMap.computeIfAbsent(key, p -> createBeanContext(property, path, defaultBatchSize, null));
}
private void registerSecondaryNode(boolean many, OrmQueryProperties props) {
int batchSize;
if (props.isQueryFetch()) {
batchSize = 100;
} else {
int lazyJoinBatch = props.getLazyFetchBatch();
batchSize = lazyJoinBatch > 0 ? lazyJoinBatch : defaultBatchSize;
}
String path = props.getPath();
if (many) {
manyMap.put(path, createManyContext(path, batchSize, props));
} else {
beanMap.put(path, createBeanContext(path, batchSize, props));
}
}
DLoadManyContext getManyContext(String path, BeanPropertyAssocMany<?> many) {
return manyMap.computeIfAbsent(path, p -> createManyContext(p, many, defaultBatchSize));
}
private DLoadManyContext createManyContext(String path, BeanPropertyAssocMany<?> many, int batchSize) {
return new DLoadManyContext(this, many, path, batchSize, null);
}
private DLoadManyContext createManyContext(String path, int batchSize, OrmQueryProperties queryProps) {
BeanPropertyAssocMany<?> p = (BeanPropertyAssocMany<?>) getBeanProperty(rootDescriptor, path);
return new DLoadManyContext(this, p, path, batchSize, queryProps);
}
private DLoadBeanContext createBeanContext(String path, int batchSize, OrmQueryProperties queryProps) {
BeanPropertyAssoc<?> p = (BeanPropertyAssoc<?>) getBeanProperty(rootDescriptor, path);
return new DLoadBeanContext(this, p.getTargetDescriptor(), path, batchSize, queryProps);
}
private DLoadBeanContext createBeanContext(BeanPropertyAssoc<?> property, String path, int batchSize, OrmQueryProperties queryProps) {
return new DLoadBeanContext(this, property.getTargetDescriptor(), path, batchSize, queryProps);
}
private BeanProperty getBeanProperty(BeanDescriptor<?> desc, String path) {
return desc.findPropertyFromPath(path);
}
void propagateQueryState(SpiQuery<?> query, boolean docStoreMapped) {
if (useDocStore && docStoreMapped) {
query.setUseDocStore(true);
}
if (readOnly != null) {
query.setReadOnly(readOnly);
}
query.setDisableLazyLoading(disableLazyLoading);
query.asOf(asOf);
if (asDraft) {
query.asDraft();
}
if (includeSoftDeletes) {
query.setIncludeSoftDeletes();
}
if (disableReadAudit) {
query.setDisableReadAuditing();
}
if (profilingListener != null) {
query.setProfilingListener(profilingListener);
}
if (tenantId != null) {
query.setTenantId(tenantId);
}
}
}