
io.ebeaninternal.server.query.CQueryEngine Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ebean Show documentation
Show all versions of ebean Show documentation
composite of common runtime dependencies for all platforms
package io.ebeaninternal.server.query;
import io.ebean.QueryIterator;
import io.ebean.ValuePair;
import io.ebean.Version;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.ObjectGraphNode;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.core.DiffHelp;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.lib.util.Str;
import io.ebeaninternal.server.persist.Binder;
import io.ebeaninternal.server.transaction.TransactionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Handles the Object Relational fetching.
*/
public class CQueryEngine {
private static final Logger logger = LoggerFactory.getLogger(CQueryEngine.class);
private static final int defaultSecondaryQueryBatchSize = 100;
private static final String T0 = "t0";
private final int defaultFetchSizeFindList;
private final int defaultFetchSizeFindEach;
private final boolean forwardOnlyHintOnFindIterate;
private final CQueryBuilder queryBuilder;
private final CQueryHistorySupport historySupport;
public CQueryEngine(ServerConfig serverConfig, DatabasePlatform dbPlatform, Binder binder, Map asOfTableMapping, Map draftTableMap) {
this.defaultFetchSizeFindEach = serverConfig.getJdbcFetchSizeFindEach();
this.defaultFetchSizeFindList = serverConfig.getJdbcFetchSizeFindList();
this.forwardOnlyHintOnFindIterate = dbPlatform.isForwardOnlyHintOnFindIterate();
this.historySupport = new CQueryHistorySupport(dbPlatform.getHistorySupport(), asOfTableMapping, serverConfig.getAsOfSysPeriod());
this.queryBuilder = new CQueryBuilder(dbPlatform, binder, historySupport, new CQueryDraftSupport(draftTableMap));
}
public CQuery buildQuery(OrmQueryRequest request) {
return queryBuilder.buildQuery(request);
}
public int delete(OrmQueryRequest request) {
CQueryUpdate query = queryBuilder.buildUpdateQuery("Delete", request);
return executeUpdate(request, query);
}
public int update(OrmQueryRequest request) {
CQueryUpdate query = queryBuilder.buildUpdateQuery("Update", request);
return executeUpdate(request, query);
}
private int executeUpdate(OrmQueryRequest request, CQueryUpdate query) {
try {
int rows = query.execute();
if (request.isLogSql()) {
String logSql = query.getGeneratedSql();
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql = Str.add(logSql, "; --bind(", query.getBindLog(), ") rows:", String.valueOf(rows));
}
request.logSql(logSql);
}
return rows;
} catch (SQLException e) {
throw CQuery.createPersistenceException(e, request.getTransaction(), query.getBindLog(), query.getGeneratedSql());
}
}
/**
* Build and execute the findSingleAttributeList query.
*/
public List findSingleAttributeList(OrmQueryRequest> request) {
CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchAttributeQuery(request);
return findAttributeList(request, rcQuery);
}
@SuppressWarnings("unchecked")
private List findAttributeList(OrmQueryRequest> request, CQueryFetchSingleAttribute rcQuery) {
try {
List list = (List) rcQuery.findList();
if (request.isLogSql()) {
logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog());
}
if (request.isLogSummary()) {
request.getTransaction().logSummary(rcQuery.getSummary());
}
return list;
} catch (SQLException e) {
throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql());
}
}
/**
* Build and execute the find Id's query.
*/
public List findIds(OrmQueryRequest> request) {
CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchIdsQuery(request);
return findAttributeList(request, rcQuery);
}
private void logGeneratedSql(OrmQueryRequest request, String sql, String bindLog) {
String logSql = sql;
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql = Str.add(logSql, "; --bind(", bindLog, ")");
}
request.logSql(logSql);
}
/**
* Build and execute the row count query.
*/
public int findCount(OrmQueryRequest request) {
CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request);
try {
int count = rcQuery.findCount();
if (request.isLogSql()) {
logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog());
}
if (request.isLogSummary()) {
request.getTransaction().logSummary(rcQuery.getSummary());
}
if (request.getQuery().isFutureFetch()) {
request.getTransaction().end();
}
return count;
} catch (SQLException e) {
throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql());
}
}
/**
* Read many beans using an iterator (except you need to close() the iterator
* when you have finished).
*/
public QueryIterator findIterate(OrmQueryRequest request) {
CQuery cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
if (defaultFetchSizeFindEach > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindEach);
}
if (!cquery.prepareBindExecuteQueryForwardOnly(forwardOnlyHintOnFindIterate)) {
// query has been cancelled already
logger.trace("Future fetch already cancelled");
return null;
}
if (request.isLogSql()) {
logSql(cquery);
}
// first check batch sizes set on query joins
int iterateBufferSize = request.getSecondaryQueriesMinBatchSize(defaultSecondaryQueryBatchSize);
if (iterateBufferSize < 1) {
// not set on query joins so check if batch size set on query itself
int queryBatch = request.getQuery().getLazyLoadBatchSize();
if (queryBatch > 0) {
iterateBufferSize = queryBatch;
}
}
QueryIterator readIterate = cquery.readIterate(iterateBufferSize, request);
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
if (request.isAuditReads()) {
// indicates we need to audit as the iterator progresses
cquery.auditFindIterate();
}
return readIterate;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
}
}
/**
* Execute the find versions query returning version beans.
*/
public List> findVersions(OrmQueryRequest request) {
SpiQuery query = request.getQuery();
String sysPeriodLower = getSysPeriodLower(query);
if (query.isVersionsBetween() && !historySupport.isStandardsBased()) {
// just add as normal predicates using the lower bound
query.where().gt(sysPeriodLower, query.getVersionStart());
query.where().lt(sysPeriodLower, query.getVersionEnd());
}
// order by id asc, lower sys period desc
query.orderBy().asc(request.getBeanDescriptor().getIdProperty().getName());
query.orderBy().desc(sysPeriodLower);
CQuery cquery = queryBuilder.buildQuery(request);
try {
cquery.prepareBindExecuteQuery();
if (request.isLogSql()) {
logSql(cquery);
}
List> versions = cquery.readVersions();
// just order in memory rather than use NULLS LAST as that
// is not universally supported, not expect huge list here
Collections.sort(versions, OrderVersionDesc.INSTANCE);
deriveVersionDiffs(versions, request);
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
if (request.isAuditReads()) {
cquery.auditFindMany();
}
return versions;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
if (cquery != null) {
cquery.close();
}
}
}
private void deriveVersionDiffs(List> versions, OrmQueryRequest request) {
BeanDescriptor descriptor = request.getBeanDescriptor();
if (!versions.isEmpty()) {
Version current = versions.get(0);
if (versions.size() > 1) {
for (int i = 1; i < versions.size(); i++) {
Version next = versions.get(i);
deriveVersionDiff(current, next, descriptor);
current = next;
}
}
// put an empty map into the last one
current.setDiff(new LinkedHashMap<>());
}
}
private void deriveVersionDiff(Version current, Version prior, BeanDescriptor descriptor) {
Map diff = DiffHelp.diff(current.getBean(), prior.getBean(), descriptor);
current.setDiff(diff);
}
/**
* Return the lower sys_period given the table alias of the query or default.
*/
private String getSysPeriodLower(SpiQuery query) {
String rootTableAlias = query.getAlias();
if (rootTableAlias == null) {
rootTableAlias = T0;
}
return historySupport.getSysPeriodLower(rootTableAlias);
}
/**
* Find a list/map/set of beans.
*/
BeanCollection findMany(OrmQueryRequest request) {
SpiQuery query = request.getQuery();
if (!query.isDistinct() && (query.getMaxRows() > 1 || query.getFirstRow() > 0)) {
// deemed to be a be a paging query - check that the order by contains
// the id property to ensure unique row ordering for predicable paging
// but only in case, this is not a distinct query
request.getBeanDescriptor().appendOrderById(query);
}
CQuery cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
if (defaultFetchSizeFindList > 0) {
request.setDefaultFetchBuffer(defaultFetchSizeFindList);
}
if (!cquery.prepareBindExecuteQuery()) {
// query has been cancelled already
logger.trace("Future fetch already cancelled");
return null;
}
if (request.isLogSql()) {
logSql(cquery);
}
BeanCollection beanCollection = cquery.readCollection();
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
if (request.isAuditReads()) {
cquery.auditFindMany();
}
request.executeSecondaryQueries(false);
return beanCollection;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
if (cquery != null) {
cquery.close();
}
if (query.isFutureFetch()) {
// end the transaction for futureFindIds
// as it had it's own transaction
logger.debug("Future fetch completed!");
request.getTransaction().end();
}
}
}
/**
* Find and return a single bean using its unique id.
*/
@SuppressWarnings("unchecked")
public T find(OrmQueryRequest request) {
EntityBean bean = null;
CQuery cquery = queryBuilder.buildQuery(request);
try {
cquery.prepareBindExecuteQuery();
if (request.isLogSql()) {
logSql(cquery);
}
if (cquery.readBean()) {
bean = cquery.next();
}
if (request.isLogSummary()) {
logFindBeanSummary(cquery);
}
if (request.isAuditReads()) {
cquery.auditFind(bean);
}
request.executeSecondaryQueries(false);
return (T) bean;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
cquery.close();
}
}
/**
* Log the generated SQL to the transaction log.
*/
private void logSql(CQuery> query) {
String sql = query.getGeneratedSql();
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
sql = Str.add(sql, "; --bind(", query.getBindLog(), ")");
}
query.getTransaction().logSql(sql);
}
/**
* Log the FindById summary to the transaction log.
*/
private void logFindBeanSummary(CQuery> q) {
SpiQuery> query = q.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
String lazyLoadProp = query.getLazyLoadProperty();
ObjectGraphNode node = query.getParentNode();
String originKey;
if (node == null || node.getOriginQueryPoint() == null) {
originKey = null;
} else {
originKey = node.getOriginQueryPoint().getKey();
}
StringBuilder msg = new StringBuilder(200);
msg.append("FindBean ");
if (loadMode != null) {
msg.append("mode[").append(loadMode).append("] ");
}
msg.append("type[").append(q.getBeanName()).append("] ");
if (query.isAutoTuned()) {
msg.append("tuned[true] ");
}
if (query.isAsDraft()) {
msg.append(" draft[true] ");
}
if (originKey != null) {
msg.append("origin[").append(originKey).append("] ");
}
if (lazyLoadProp != null) {
msg.append("lazyLoadProp[").append(lazyLoadProp).append("] ");
}
if (loadDesc != null) {
msg.append("load[").append(loadDesc).append("] ");
}
msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros());
msg.append("] rows[").append(q.getLoadedRowDetail());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logSummary(msg.toString());
}
/**
* Log the FindMany to the transaction log.
*/
private void logFindManySummary(CQuery> q) {
SpiQuery> query = q.getQueryRequest().getQuery();
String loadMode = query.getLoadMode();
String loadDesc = query.getLoadDescription();
String lazyLoadProp = query.getLazyLoadProperty();
ObjectGraphNode node = query.getParentNode();
String originKey;
if (node == null || node.getOriginQueryPoint() == null) {
originKey = null;
} else {
originKey = node.getOriginQueryPoint().getKey();
}
StringBuilder msg = new StringBuilder(200);
msg.append("FindMany ");
if (loadMode != null) {
msg.append("mode[").append(loadMode).append("] ");
}
msg.append("type[").append(q.getBeanName()).append("] ");
if (query.isAutoTuned()) {
msg.append("tuned[true] ");
}
if (query.isAsDraft()) {
msg.append(" draft[true] ");
}
if (originKey != null) {
msg.append("origin[").append(originKey).append("] ");
}
if (lazyLoadProp != null) {
msg.append("lazyLoadProp[").append(lazyLoadProp).append("] ");
}
if (loadDesc != null) {
msg.append("load[").append(loadDesc).append("] ");
}
msg.append("exeMicros[").append(q.getQueryExecutionTimeMicros());
msg.append("] rows[").append(q.getLoadedRowDetail());
msg.append("] predicates[").append(q.getLogWhereSql());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logSummary(msg.toString());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy