No effective change - change newline char

This commit is contained in:
rbygrave
2015-05-09 01:10:56 +12:00
parent 3358ab16a7
commit e3cccfa9a3
34 changed files with 6041 additions and 6041 deletions
@@ -1,46 +1,46 @@
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A base object for query Future objects.
*
* @author rbygrave
*
* @param <T> the entity bean type
*/
public abstract class BaseFuture<T> implements Future<T> {
protected final FutureTask<T> futureTask;
public BaseFuture(FutureTask<T> futureTask) {
this.futureTask = futureTask;
}
public boolean cancel(boolean mayInterruptIfRunning) {
return futureTask.cancel(mayInterruptIfRunning);
}
public T get() throws InterruptedException, ExecutionException {
return futureTask.get();
}
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return futureTask.get(timeout, unit);
}
public boolean isCancelled() {
return futureTask.isCancelled();
}
public boolean isDone() {
return futureTask.isDone();
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A base object for query Future objects.
*
* @author rbygrave
*
* @param <T> the entity bean type
*/
public abstract class BaseFuture<T> implements Future<T> {
protected final FutureTask<T> futureTask;
public BaseFuture(FutureTask<T> futureTask) {
this.futureTask = futureTask;
}
public boolean cancel(boolean mayInterruptIfRunning) {
return futureTask.cancel(mayInterruptIfRunning);
}
public T get() throws InterruptedException, ExecutionException {
return futureTask.get();
}
public T get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return futureTask.get(timeout, unit);
}
public boolean isCancelled() {
return futureTask.isCancelled();
}
public boolean isDone() {
return futureTask.isDone();
}
}
@@ -1,202 +1,202 @@
package com.avaje.ebeaninternal.server.query;
import java.util.Collection;
import java.util.Map;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.util.BeanCollectionFactory;
import com.avaje.ebeaninternal.server.util.BeanCollectionParams;
/**
* Wraps a BeanCollection with helper methods to add beans.
* <p>
* Helps adding the bean to the underlying set list or map.
* </p>
*/
public final class BeanCollectionWrapper {
/**
* Flag set if this builds a Map rather than a Collection.
*/
private final boolean isMap;
/**
* The type.
*/
private final SpiQuery.Type queryType;
/**
* A property name used as key for a Map.
*/
private final String mapKey;
/**
* The actual BeanCollection.
*/
private final BeanCollection<?> beanCollection;
/**
* Collection type of BeanCollection.
*/
private final Collection<Object> collection;
/**
* Map type of BeanCollection.
*/
private final Map<Object,Object> map;
/**
* The associated BeanDescriptor.
*/
private final BeanDescriptor<?> desc;
/**
* The number of rows added.
*/
private int rowCount;
public BeanCollectionWrapper(RelationalQueryRequest request) {
this.desc = null;
this.queryType = request.getQueryType();
this.mapKey = request.getQuery().getMapKey();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
/**
* Create based on a Find.
*/
public BeanCollectionWrapper(OrmQueryRequest<?> request) {
this.desc = request.getBeanDescriptor();
this.queryType = request.getQueryType();
this.mapKey = request.getQuery().getMapKey();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
/**
* Create based on a ManyType and mapKey. Note the mapKey is only used if
* the manyType is a Map.
* <p>
* modifyListening is set to true if this is a collection used to hold
* ManyToMany associated objects.
* </p>
*/
public BeanCollectionWrapper(BeanPropertyAssocMany<?> manyProp) {
this.queryType = manyProp.getManyType().getQueryType();
this.mapKey = manyProp.getMapKey();
this.desc = manyProp.getTargetDescriptor();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Map<Object,Object> getMap(boolean isMap) {
return isMap ? (Map)beanCollection : null;
}
@SuppressWarnings("unchecked")
private Collection<Object> getCollection(boolean isMap) {
return isMap ? null : (Collection<Object>)beanCollection ;
}
/**
* Return the underlying BeanCollection.
*/
public BeanCollection<?> getBeanCollection() {
return beanCollection;
}
/**
* Create a BeanCollection of the correct type.
*/
private BeanCollection<?> createBeanCollection(SpiQuery.Type manyType) {
BeanCollectionParams p = new BeanCollectionParams(manyType);
return BeanCollectionFactory.create(p);
}
/**
* Return true if this wraps a Map rather than a set or list.
*/
public boolean isMap() {
return isMap;
}
/**
* Return the number of rows added to this wrapper.
*/
public int size() {
return rowCount;
}
/**
* Add the bean to the collection held in this wrapper.
*/
public void add(EntityBean bean) {
add(bean, beanCollection);
}
/**
* Add the bean to the collection passed.
*
* @param bean
* the bean to add
* @param collection
* the collection or map to add the bean to
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void add(EntityBean bean, Object collection) {
if (bean == null) {
return;
}
rowCount++;
if (isMap) {
Object keyValue = null;
if (mapKey != null) {
// use the value for the property
keyValue = desc.getValue(bean, mapKey);
} else {
// use the uniqueId for this
keyValue = desc.getId(bean);
}
Map mapColl = (Map) collection;
mapColl.put(keyValue, bean);
} else {
((Collection) collection).add(bean);
}
}
/**
* Specifically add to a Collection.
*/
public void addToCollection(Object bean) {
collection.add(bean);
}
/**
* Specifically add to this as a Map with a known key.
*/
public void addToMap(Object bean, Object key) {
map.put(key, bean);
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.Collection;
import java.util.Map;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.util.BeanCollectionFactory;
import com.avaje.ebeaninternal.server.util.BeanCollectionParams;
/**
* Wraps a BeanCollection with helper methods to add beans.
* <p>
* Helps adding the bean to the underlying set list or map.
* </p>
*/
public final class BeanCollectionWrapper {
/**
* Flag set if this builds a Map rather than a Collection.
*/
private final boolean isMap;
/**
* The type.
*/
private final SpiQuery.Type queryType;
/**
* A property name used as key for a Map.
*/
private final String mapKey;
/**
* The actual BeanCollection.
*/
private final BeanCollection<?> beanCollection;
/**
* Collection type of BeanCollection.
*/
private final Collection<Object> collection;
/**
* Map type of BeanCollection.
*/
private final Map<Object,Object> map;
/**
* The associated BeanDescriptor.
*/
private final BeanDescriptor<?> desc;
/**
* The number of rows added.
*/
private int rowCount;
public BeanCollectionWrapper(RelationalQueryRequest request) {
this.desc = null;
this.queryType = request.getQueryType();
this.mapKey = request.getQuery().getMapKey();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
/**
* Create based on a Find.
*/
public BeanCollectionWrapper(OrmQueryRequest<?> request) {
this.desc = request.getBeanDescriptor();
this.queryType = request.getQueryType();
this.mapKey = request.getQuery().getMapKey();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
/**
* Create based on a ManyType and mapKey. Note the mapKey is only used if
* the manyType is a Map.
* <p>
* modifyListening is set to true if this is a collection used to hold
* ManyToMany associated objects.
* </p>
*/
public BeanCollectionWrapper(BeanPropertyAssocMany<?> manyProp) {
this.queryType = manyProp.getManyType().getQueryType();
this.mapKey = manyProp.getMapKey();
this.desc = manyProp.getTargetDescriptor();
this.isMap = SpiQuery.Type.MAP.equals(queryType);
this.beanCollection = createBeanCollection(queryType);
this.collection = getCollection(isMap);
this.map = getMap(isMap);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Map<Object,Object> getMap(boolean isMap) {
return isMap ? (Map)beanCollection : null;
}
@SuppressWarnings("unchecked")
private Collection<Object> getCollection(boolean isMap) {
return isMap ? null : (Collection<Object>)beanCollection ;
}
/**
* Return the underlying BeanCollection.
*/
public BeanCollection<?> getBeanCollection() {
return beanCollection;
}
/**
* Create a BeanCollection of the correct type.
*/
private BeanCollection<?> createBeanCollection(SpiQuery.Type manyType) {
BeanCollectionParams p = new BeanCollectionParams(manyType);
return BeanCollectionFactory.create(p);
}
/**
* Return true if this wraps a Map rather than a set or list.
*/
public boolean isMap() {
return isMap;
}
/**
* Return the number of rows added to this wrapper.
*/
public int size() {
return rowCount;
}
/**
* Add the bean to the collection held in this wrapper.
*/
public void add(EntityBean bean) {
add(bean, beanCollection);
}
/**
* Add the bean to the collection passed.
*
* @param bean
* the bean to add
* @param collection
* the collection or map to add the bean to
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void add(EntityBean bean, Object collection) {
if (bean == null) {
return;
}
rowCount++;
if (isMap) {
Object keyValue = null;
if (mapKey != null) {
// use the value for the property
keyValue = desc.getValue(bean, mapKey);
} else {
// use the uniqueId for this
keyValue = desc.getId(bean);
}
Map mapColl = (Map) collection;
mapColl.put(keyValue, bean);
} else {
((Collection) collection).add(bean);
}
}
/**
* Specifically add to a Collection.
*/
public void addToCollection(Object bean) {
collection.add(bean);
}
/**
* Specifically add to this as a Map with a known key.
*/
public void addToMap(Object bean, Object key) {
map.put(key, bean);
}
}
File diff suppressed because it is too large Load Diff
@@ -1,411 +1,411 @@
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.RawSql.ColumnMapping.Column;
import com.avaje.ebean.RawSqlBuilder;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitRequest;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
import javax.persistence.PersistenceException;
import java.util.Iterator;
import java.util.Set;
/**
* Generates the SQL SELECT statements taking into account the physical
* deployment properties.
*/
public class CQueryBuilder implements Constants {
private final String tableAliasPlaceHolder;
private final String columnAliasPrefix;
private final SqlLimiter sqlLimiter;
private final RawSqlSelectClauseBuilder sqlSelectBuilder;
private final CQueryBuilderRawSql rawSqlHandler;
private final Binder binder;
private final boolean selectCountWithAlias;
private DatabasePlatform dbPlatform;
/**
* Create the SqlGenSelect.
*/
public CQueryBuilder(DatabasePlatform dbPlatform, Binder binder) {
this.binder = binder;
this.tableAliasPlaceHolder = dbPlatform.getTableAliasPlaceHolder();
this.columnAliasPrefix = dbPlatform.getColumnAliasPrefix();
this.sqlSelectBuilder = new RawSqlSelectClauseBuilder(dbPlatform, binder);
this.sqlLimiter = dbPlatform.getSqlLimiter();
this.rawSqlHandler = new CQueryBuilderRawSql(sqlLimiter, dbPlatform);
this.selectCountWithAlias = dbPlatform.isSelectCountWithAlias();
this.dbPlatform = dbPlatform;
}
/**
* split the order by claus on the field delimiter and prefix each field with
* the relation name
*/
public static String prefixOrderByFields(String name, String orderBy) {
StringBuilder sb = new StringBuilder();
for (String token : orderBy.split(",")) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(name);
sb.append(".");
sb.append(token.trim());
}
return sb.toString();
}
/**
* Build the row count query.
*/
public <T> CQueryFetchIds buildFetchIdsQuery(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
query.setSelectId();
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// skip building the SqlTree and Sql string
predicates.prepare(false);
String sql = queryPlan.getSql();
return new CQueryFetchIds(request, predicates, sql);
}
// use RawSql or generated Sql
predicates.prepare(true);
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse s = buildSql(null, request, predicates, sqlTree);
String sql = s.getSql();
// cache the query plan
queryPlan = new CQueryPlan(request, sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryFetchIds(request, predicates, sql);
}
/**
* Build the row count query.
*/
public <T> CQueryRowCount buildRowCountQuery(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
// always set the order by to null for row count query
query.setOrder(null);
query.setFirstRow(0);
query.setMaxRows(0);
ManyWhereJoins manyWhereJoins = query.getManyWhereJoins();
boolean hasMany = manyWhereJoins.isHasMany();
if (manyWhereJoins.isSelectId()) {
// just select the id property
query.setSelectId();
} else {
// select the id and the required formula properties
query.select(manyWhereJoins.getFormulaProperties());
}
String sqlSelect = "select count(*)";
if (hasMany) {
// need to count distinct id's ...
query.setSqlDistinct(true);
sqlSelect = null;
}
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// skip building the SqlTree and Sql string
predicates.prepare(false);
String sql = queryPlan.getSql();
return new CQueryRowCount(request, predicates, sql);
}
predicates.prepare(true);
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse s = buildSql(sqlSelect, request, predicates, sqlTree);
String sql = s.getSql();
if (hasMany || query.isRawSql()) {
sql = "select count(*) from ( " + sql + ")";
if (selectCountWithAlias) {
sql += " as c";
}
}
// cache the query plan
queryPlan = new CQueryPlan(request, sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryRowCount(request, predicates, sql);
}
/**
* Return the SQL Select statement as a String. Converts logical property
* names to physical deployment column names.
*/
public <T> CQuery<T> buildQuery(OrmQueryRequest<T> request) {
if (request.isSqlSelect()) {
return sqlSelectBuilder.build(request);
}
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// Reuse the query plan so skip generating SqlTree and SQL.
// We do prepare and bind the new parameters
predicates.prepare(false);
return new CQuery<T>(request, predicates, queryPlan);
}
// RawSql or Generated Sql query
// Prepare the where, having and order by clauses.
// This also parses them from logical property names to
// database columns and determines 'includes'.
// We need to check these 'includes' for extra joins
// that are not included via select
predicates.prepare(true);
// Build the tree structure that represents the query.
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse res = buildSql(null, request, predicates, sqlTree);
boolean rawSql = request.isRawSql();
if (rawSql) {
queryPlan = new CQueryPlanRawSql(request, res, sqlTree, predicates.getLogWhereSql());
} else {
queryPlan = new CQueryPlan(request, res, sqlTree, rawSql, predicates.getLogWhereSql());
}
// cache the query plan because we can reuse it and also
// gather query performance statistics based on it.
request.putQueryPlan(queryPlan);
return new CQuery<T>(request, predicates, queryPlan);
}
/**
* Build the SqlTree.
* <p>
* The SqlTree is immutable after construction and so is safe to use by
* concurrent threads.
* </p>
* <p>
* The predicates is used to add additional joins that come from the where or
* order by clauses that are not already included for the select clause.
* </p>
*/
private SqlTree createSqlTree(OrmQueryRequest<?> request, CQueryPredicates predicates) {
if (request.isRawSql()) {
return createRawSqlSqlTree(request, predicates);
}
return new SqlTreeBuilder(tableAliasPlaceHolder, columnAliasPrefix, request, predicates).build();
}
private SqlTree createRawSqlSqlTree(OrmQueryRequest<?> request, CQueryPredicates predicates) {
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping();
PathProperties pathProps = new PathProperties();
// convert list of columns into (tree like) PathProperties
Iterator<Column> it = columnMapping.getColumns();
while (it.hasNext()) {
RawSql.ColumnMapping.Column column = it.next();
String propertyName = column.getPropertyName();
if (!RawSqlBuilder.IGNORE_COLUMN.equals(propertyName)) {
ElPropertyValue el = descriptor.getElGetValue(propertyName);
if (el == null) {
throw new PersistenceException("Property [" + propertyName + "] not found on " + descriptor.getFullName());
} else {
BeanProperty beanProperty = el.getBeanProperty();
if (beanProperty.isId() || beanProperty.isDiscriminator()) {
// For @Id properties we chop off the last part of the path
propertyName = SplitName.parent(propertyName);
} else if (beanProperty instanceof BeanPropertyAssocOne<?>) {
String msg = "Column [" + column.getDbColumn() + "] mapped to complex Property[" + propertyName + "]";
msg += ". It should be mapped to a simple property (proably the Id property). ";
throw new PersistenceException(msg);
}
if (propertyName != null) {
String[] pathProp = SplitName.split(propertyName);
pathProps.addToPath(pathProp[0], pathProp[1]);
}
}
}
}
OrmQueryDetail detail = new OrmQueryDetail();
// transfer PathProperties into OrmQueryDetail
for (String path : pathProps.getPaths()) {
Set<String> props = pathProps.get(path);
detail.getChunk(path, true).setDefaultProperties(null, props);
}
// build SqlTree based on OrmQueryDetail of the RawSql
return new SqlTreeBuilder(request, predicates, detail).build();
}
private SqlLimitResponse buildSql(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select) {
SpiQuery<?> query = request.getQuery();
RawSql rawSql = query.getRawSql();
if (rawSql != null) {
return rawSqlHandler.buildSql(request, predicates, rawSql.getSql());
}
BeanPropertyAssocMany<?> manyProp = select.getManyProperty();
boolean useSqlLimiter = false;
StringBuilder sb = new StringBuilder(500);
String dbOrderBy = predicates.getDbOrderBy();
if (selectClause != null) {
sb.append(selectClause);
} else {
useSqlLimiter = (query.hasMaxRowsOrFirstRow() && manyProp == null);
if (!useSqlLimiter) {
sb.append("select ");
if (query.isDistinctQuery()) {
sb.append("distinct ");
}
}
sb.append(select.getSelectSql());
if (query.isDistinctQuery() && dbOrderBy != null) {
// add the orderby columns to the select clause (due to distinct)
sb.append(", ").append(convertDbOrderByForSelect(dbOrderBy));
}
}
sb.append(" from ");
// build the from clause potentially with joins
// required only for the predicates
sb.append(select.getFromSql());
String inheritanceWhere = select.getInheritanceWhereSql();
boolean hasWhere = false;
if (inheritanceWhere.length() > 0) {
sb.append(" where");
sb.append(inheritanceWhere);
hasWhere = true;
}
if (request.isFindById() || query.getId() != null) {
if (hasWhere) {
sb.append(" and ");
} else {
sb.append(" where ");
}
BeanDescriptor<?> desc = request.getBeanDescriptor();
String idSql = desc.getIdBinderIdSql();
if (idSql.isEmpty()) {
throw new IllegalStateException("Executing FindById query on entity bean " + desc.getName()
+ " that doesn't have an @Id property??");
}
sb.append(idSql).append(" ");
hasWhere = true;
}
String dbWhere = predicates.getDbWhere();
if (!isEmpty(dbWhere)) {
if (!hasWhere) {
hasWhere = true;
sb.append(" where ");
} else {
sb.append(" and ");
}
sb.append(dbWhere);
}
String dbFilterMany = predicates.getDbFilterMany();
if (!isEmpty(dbFilterMany)) {
if (!hasWhere) {
sb.append(" where ");
} else {
sb.append("and ");
}
sb.append(dbFilterMany);
}
if (dbOrderBy != null) {
sb.append(" order by ").append(dbOrderBy);
}
if (useSqlLimiter) {
// use LIMIT/OFFSET, ROW_NUMBER() or rownum type SQL query limitation
SqlLimitRequest r = new OrmQueryLimitRequest(sb.toString(), dbOrderBy, query, dbPlatform);
return sqlLimiter.limit(r);
} else {
return new SqlLimitResponse(dbPlatform.completeSql(sb.toString(), query), false);
}
}
/**
* Convert the dbOrderBy clause to be safe for adding to select. This is done when 'distinct' is
* used.
*/
private String convertDbOrderByForSelect(String dbOrderBy) {
// just remove the ASC and DESC keywords
return dbOrderBy.replaceAll("(?i)\\b asc\\b|\\b desc\\b", "");
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
}
}
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.RawSql.ColumnMapping.Column;
import com.avaje.ebean.RawSqlBuilder;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitRequest;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
import javax.persistence.PersistenceException;
import java.util.Iterator;
import java.util.Set;
/**
* Generates the SQL SELECT statements taking into account the physical
* deployment properties.
*/
public class CQueryBuilder implements Constants {
private final String tableAliasPlaceHolder;
private final String columnAliasPrefix;
private final SqlLimiter sqlLimiter;
private final RawSqlSelectClauseBuilder sqlSelectBuilder;
private final CQueryBuilderRawSql rawSqlHandler;
private final Binder binder;
private final boolean selectCountWithAlias;
private DatabasePlatform dbPlatform;
/**
* Create the SqlGenSelect.
*/
public CQueryBuilder(DatabasePlatform dbPlatform, Binder binder) {
this.binder = binder;
this.tableAliasPlaceHolder = dbPlatform.getTableAliasPlaceHolder();
this.columnAliasPrefix = dbPlatform.getColumnAliasPrefix();
this.sqlSelectBuilder = new RawSqlSelectClauseBuilder(dbPlatform, binder);
this.sqlLimiter = dbPlatform.getSqlLimiter();
this.rawSqlHandler = new CQueryBuilderRawSql(sqlLimiter, dbPlatform);
this.selectCountWithAlias = dbPlatform.isSelectCountWithAlias();
this.dbPlatform = dbPlatform;
}
/**
* split the order by claus on the field delimiter and prefix each field with
* the relation name
*/
public static String prefixOrderByFields(String name, String orderBy) {
StringBuilder sb = new StringBuilder();
for (String token : orderBy.split(",")) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(name);
sb.append(".");
sb.append(token.trim());
}
return sb.toString();
}
/**
* Build the row count query.
*/
public <T> CQueryFetchIds buildFetchIdsQuery(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
query.setSelectId();
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// skip building the SqlTree and Sql string
predicates.prepare(false);
String sql = queryPlan.getSql();
return new CQueryFetchIds(request, predicates, sql);
}
// use RawSql or generated Sql
predicates.prepare(true);
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse s = buildSql(null, request, predicates, sqlTree);
String sql = s.getSql();
// cache the query plan
queryPlan = new CQueryPlan(request, sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryFetchIds(request, predicates, sql);
}
/**
* Build the row count query.
*/
public <T> CQueryRowCount buildRowCountQuery(OrmQueryRequest<T> request) {
SpiQuery<T> query = request.getQuery();
// always set the order by to null for row count query
query.setOrder(null);
query.setFirstRow(0);
query.setMaxRows(0);
ManyWhereJoins manyWhereJoins = query.getManyWhereJoins();
boolean hasMany = manyWhereJoins.isHasMany();
if (manyWhereJoins.isSelectId()) {
// just select the id property
query.setSelectId();
} else {
// select the id and the required formula properties
query.select(manyWhereJoins.getFormulaProperties());
}
String sqlSelect = "select count(*)";
if (hasMany) {
// need to count distinct id's ...
query.setSqlDistinct(true);
sqlSelect = null;
}
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// skip building the SqlTree and Sql string
predicates.prepare(false);
String sql = queryPlan.getSql();
return new CQueryRowCount(request, predicates, sql);
}
predicates.prepare(true);
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse s = buildSql(sqlSelect, request, predicates, sqlTree);
String sql = s.getSql();
if (hasMany || query.isRawSql()) {
sql = "select count(*) from ( " + sql + ")";
if (selectCountWithAlias) {
sql += " as c";
}
}
// cache the query plan
queryPlan = new CQueryPlan(request, sql, sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
request.putQueryPlan(queryPlan);
return new CQueryRowCount(request, predicates, sql);
}
/**
* Return the SQL Select statement as a String. Converts logical property
* names to physical deployment column names.
*/
public <T> CQuery<T> buildQuery(OrmQueryRequest<T> request) {
if (request.isSqlSelect()) {
return sqlSelectBuilder.build(request);
}
CQueryPredicates predicates = new CQueryPredicates(binder, request);
CQueryPlan queryPlan = request.getQueryPlan();
if (queryPlan != null) {
// Reuse the query plan so skip generating SqlTree and SQL.
// We do prepare and bind the new parameters
predicates.prepare(false);
return new CQuery<T>(request, predicates, queryPlan);
}
// RawSql or Generated Sql query
// Prepare the where, having and order by clauses.
// This also parses them from logical property names to
// database columns and determines 'includes'.
// We need to check these 'includes' for extra joins
// that are not included via select
predicates.prepare(true);
// Build the tree structure that represents the query.
SqlTree sqlTree = createSqlTree(request, predicates);
SqlLimitResponse res = buildSql(null, request, predicates, sqlTree);
boolean rawSql = request.isRawSql();
if (rawSql) {
queryPlan = new CQueryPlanRawSql(request, res, sqlTree, predicates.getLogWhereSql());
} else {
queryPlan = new CQueryPlan(request, res, sqlTree, rawSql, predicates.getLogWhereSql());
}
// cache the query plan because we can reuse it and also
// gather query performance statistics based on it.
request.putQueryPlan(queryPlan);
return new CQuery<T>(request, predicates, queryPlan);
}
/**
* Build the SqlTree.
* <p>
* The SqlTree is immutable after construction and so is safe to use by
* concurrent threads.
* </p>
* <p>
* The predicates is used to add additional joins that come from the where or
* order by clauses that are not already included for the select clause.
* </p>
*/
private SqlTree createSqlTree(OrmQueryRequest<?> request, CQueryPredicates predicates) {
if (request.isRawSql()) {
return createRawSqlSqlTree(request, predicates);
}
return new SqlTreeBuilder(tableAliasPlaceHolder, columnAliasPrefix, request, predicates).build();
}
private SqlTree createRawSqlSqlTree(OrmQueryRequest<?> request, CQueryPredicates predicates) {
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping();
PathProperties pathProps = new PathProperties();
// convert list of columns into (tree like) PathProperties
Iterator<Column> it = columnMapping.getColumns();
while (it.hasNext()) {
RawSql.ColumnMapping.Column column = it.next();
String propertyName = column.getPropertyName();
if (!RawSqlBuilder.IGNORE_COLUMN.equals(propertyName)) {
ElPropertyValue el = descriptor.getElGetValue(propertyName);
if (el == null) {
throw new PersistenceException("Property [" + propertyName + "] not found on " + descriptor.getFullName());
} else {
BeanProperty beanProperty = el.getBeanProperty();
if (beanProperty.isId() || beanProperty.isDiscriminator()) {
// For @Id properties we chop off the last part of the path
propertyName = SplitName.parent(propertyName);
} else if (beanProperty instanceof BeanPropertyAssocOne<?>) {
String msg = "Column [" + column.getDbColumn() + "] mapped to complex Property[" + propertyName + "]";
msg += ". It should be mapped to a simple property (proably the Id property). ";
throw new PersistenceException(msg);
}
if (propertyName != null) {
String[] pathProp = SplitName.split(propertyName);
pathProps.addToPath(pathProp[0], pathProp[1]);
}
}
}
}
OrmQueryDetail detail = new OrmQueryDetail();
// transfer PathProperties into OrmQueryDetail
for (String path : pathProps.getPaths()) {
Set<String> props = pathProps.get(path);
detail.getChunk(path, true).setDefaultProperties(null, props);
}
// build SqlTree based on OrmQueryDetail of the RawSql
return new SqlTreeBuilder(request, predicates, detail).build();
}
private SqlLimitResponse buildSql(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select) {
SpiQuery<?> query = request.getQuery();
RawSql rawSql = query.getRawSql();
if (rawSql != null) {
return rawSqlHandler.buildSql(request, predicates, rawSql.getSql());
}
BeanPropertyAssocMany<?> manyProp = select.getManyProperty();
boolean useSqlLimiter = false;
StringBuilder sb = new StringBuilder(500);
String dbOrderBy = predicates.getDbOrderBy();
if (selectClause != null) {
sb.append(selectClause);
} else {
useSqlLimiter = (query.hasMaxRowsOrFirstRow() && manyProp == null);
if (!useSqlLimiter) {
sb.append("select ");
if (query.isDistinctQuery()) {
sb.append("distinct ");
}
}
sb.append(select.getSelectSql());
if (query.isDistinctQuery() && dbOrderBy != null) {
// add the orderby columns to the select clause (due to distinct)
sb.append(", ").append(convertDbOrderByForSelect(dbOrderBy));
}
}
sb.append(" from ");
// build the from clause potentially with joins
// required only for the predicates
sb.append(select.getFromSql());
String inheritanceWhere = select.getInheritanceWhereSql();
boolean hasWhere = false;
if (inheritanceWhere.length() > 0) {
sb.append(" where");
sb.append(inheritanceWhere);
hasWhere = true;
}
if (request.isFindById() || query.getId() != null) {
if (hasWhere) {
sb.append(" and ");
} else {
sb.append(" where ");
}
BeanDescriptor<?> desc = request.getBeanDescriptor();
String idSql = desc.getIdBinderIdSql();
if (idSql.isEmpty()) {
throw new IllegalStateException("Executing FindById query on entity bean " + desc.getName()
+ " that doesn't have an @Id property??");
}
sb.append(idSql).append(" ");
hasWhere = true;
}
String dbWhere = predicates.getDbWhere();
if (!isEmpty(dbWhere)) {
if (!hasWhere) {
hasWhere = true;
sb.append(" where ");
} else {
sb.append(" and ");
}
sb.append(dbWhere);
}
String dbFilterMany = predicates.getDbFilterMany();
if (!isEmpty(dbFilterMany)) {
if (!hasWhere) {
sb.append(" where ");
} else {
sb.append("and ");
}
sb.append(dbFilterMany);
}
if (dbOrderBy != null) {
sb.append(" order by ").append(dbOrderBy);
}
if (useSqlLimiter) {
// use LIMIT/OFFSET, ROW_NUMBER() or rownum type SQL query limitation
SqlLimitRequest r = new OrmQueryLimitRequest(sb.toString(), dbOrderBy, query, dbPlatform);
return sqlLimiter.limit(r);
} else {
return new SqlLimitResponse(dbPlatform.completeSql(sb.toString(), query), false);
}
}
/**
* Convert the dbOrderBy clause to be safe for adding to select. This is done when 'distinct' is
* used.
*/
private String convertDbOrderByForSelect(String dbOrderBy) {
// just remove the ASC and DESC keywords
return dbOrderBy.replaceAll("(?i)\\b asc\\b|\\b desc\\b", "");
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
}
}
@@ -1,148 +1,148 @@
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
public class CQueryBuilderRawSql implements Constants {
private final SqlLimiter sqlLimiter;
private final DatabasePlatform dbPlatform;
CQueryBuilderRawSql(SqlLimiter sqlLimiter, DatabasePlatform dbPlatform) {
this.sqlLimiter = sqlLimiter;
this.dbPlatform = dbPlatform;
}
/**
* Build the full SQL Select statement for the request.
*/
public SqlLimitResponse buildSql(OrmQueryRequest<?> request, CQueryPredicates predicates, RawSql.Sql rsql) {
if (rsql == null) {
// this is a ResultSet based RawSql query - just use some placeholder for the SQL
return new SqlLimitResponse("--ResultSetBasedRawSql", false);
}
if (!rsql.isParsed()){
String sql = rsql.getUnparsedSql();
BindParams bindParams = request.getQuery().getBindParams();
if (bindParams != null && bindParams.requiresNamedParamsPrepare()){
// convert named parameters into positioned parameters
sql = BindParamsParser.parse(bindParams, sql);
}
return new SqlLimitResponse(sql, false);
}
String orderBy = getOrderBy(predicates, rsql);
// build the actual sql String
String sql = buildMainQuery(orderBy, request, predicates, rsql);
SpiQuery<?> query = request.getQuery();
if (query.hasMaxRowsOrFirstRow() && sqlLimiter != null) {
// wrap with a limit offset or ROW_NUMBER() etc
return sqlLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform));
} else {
// add back select keyword (it was removed to support sqlQueryLimiter)
String prefix = "select "+ (rsql.isDistinct() ? "distinct " : "");
sql = prefix + sql;
return new SqlLimitResponse(sql, false);
}
}
private String buildMainQuery(String orderBy, OrmQueryRequest<?> request, CQueryPredicates predicates, RawSql.Sql sql) {
StringBuilder sb = new StringBuilder();
sb.append(sql.getPreFrom());
sb.append(" ");
String s = sql.getPreWhere();
BindParams bindParams = request.getQuery().getBindParams();
if (bindParams != null && bindParams.requiresNamedParamsPrepare()){
// convert named parameters into positioned parameters
// Named Parameters only allowed prior to dynamic where
// clause (so not allowed in having etc - use unparsed)
s = BindParamsParser.parse(bindParams, s);
}
sb.append(s);
sb.append(" ");
String dynamicWhere = null;
if (request.getQuery().getId() != null) {
// need to convert this as well. This avoids the
// assumption that id has its proper dbColumn assigned
// which may change if using multiple raw sql statements
// against the same bean.
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
//FIXME: I think this is broken... needs to be logical
// and then parsed for RawSqlSelect...
dynamicWhere = descriptor.getIdBinderIdSql();
}
String dbWhere = predicates.getDbWhere();
if (!isEmpty(dbWhere)) {
if (dynamicWhere == null) {
dynamicWhere = dbWhere;
} else {
dynamicWhere += " and " + dbWhere;
}
}
if (!isEmpty(dynamicWhere)) {
if (sql.isAndWhereExpr()) {
sb.append(" and ");
} else {
sb.append(" where ");
}
sb.append(dynamicWhere);
sb.append(" ");
}
String preHaving = sql.getPreHaving();
if (!isEmpty(preHaving)) {
sb.append(preHaving);
sb.append(" ");
}
String dbHaving = predicates.getDbHaving();
if (!isEmpty(dbHaving)) {
sb.append(" ");
if (sql.isAndHavingExpr()) {
sb.append("and ");
} else {
sb.append("having ");
}
sb.append(dbHaving);
sb.append(" ");
}
if (!isEmpty(orderBy)) {
sb.append(" ").append(sql.getOrderByPrefix()).append(" ").append(orderBy);
}
return sb.toString().trim();
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
}
private String getOrderBy(CQueryPredicates predicates, RawSql.Sql sql) {
String orderBy = predicates.getDbOrderBy();
if (orderBy != null) {
return orderBy;
} else {
return sql.getOrderBy();
}
}
}
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
public class CQueryBuilderRawSql implements Constants {
private final SqlLimiter sqlLimiter;
private final DatabasePlatform dbPlatform;
CQueryBuilderRawSql(SqlLimiter sqlLimiter, DatabasePlatform dbPlatform) {
this.sqlLimiter = sqlLimiter;
this.dbPlatform = dbPlatform;
}
/**
* Build the full SQL Select statement for the request.
*/
public SqlLimitResponse buildSql(OrmQueryRequest<?> request, CQueryPredicates predicates, RawSql.Sql rsql) {
if (rsql == null) {
// this is a ResultSet based RawSql query - just use some placeholder for the SQL
return new SqlLimitResponse("--ResultSetBasedRawSql", false);
}
if (!rsql.isParsed()){
String sql = rsql.getUnparsedSql();
BindParams bindParams = request.getQuery().getBindParams();
if (bindParams != null && bindParams.requiresNamedParamsPrepare()){
// convert named parameters into positioned parameters
sql = BindParamsParser.parse(bindParams, sql);
}
return new SqlLimitResponse(sql, false);
}
String orderBy = getOrderBy(predicates, rsql);
// build the actual sql String
String sql = buildMainQuery(orderBy, request, predicates, rsql);
SpiQuery<?> query = request.getQuery();
if (query.hasMaxRowsOrFirstRow() && sqlLimiter != null) {
// wrap with a limit offset or ROW_NUMBER() etc
return sqlLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform));
} else {
// add back select keyword (it was removed to support sqlQueryLimiter)
String prefix = "select "+ (rsql.isDistinct() ? "distinct " : "");
sql = prefix + sql;
return new SqlLimitResponse(sql, false);
}
}
private String buildMainQuery(String orderBy, OrmQueryRequest<?> request, CQueryPredicates predicates, RawSql.Sql sql) {
StringBuilder sb = new StringBuilder();
sb.append(sql.getPreFrom());
sb.append(" ");
String s = sql.getPreWhere();
BindParams bindParams = request.getQuery().getBindParams();
if (bindParams != null && bindParams.requiresNamedParamsPrepare()){
// convert named parameters into positioned parameters
// Named Parameters only allowed prior to dynamic where
// clause (so not allowed in having etc - use unparsed)
s = BindParamsParser.parse(bindParams, s);
}
sb.append(s);
sb.append(" ");
String dynamicWhere = null;
if (request.getQuery().getId() != null) {
// need to convert this as well. This avoids the
// assumption that id has its proper dbColumn assigned
// which may change if using multiple raw sql statements
// against the same bean.
BeanDescriptor<?> descriptor = request.getBeanDescriptor();
//FIXME: I think this is broken... needs to be logical
// and then parsed for RawSqlSelect...
dynamicWhere = descriptor.getIdBinderIdSql();
}
String dbWhere = predicates.getDbWhere();
if (!isEmpty(dbWhere)) {
if (dynamicWhere == null) {
dynamicWhere = dbWhere;
} else {
dynamicWhere += " and " + dbWhere;
}
}
if (!isEmpty(dynamicWhere)) {
if (sql.isAndWhereExpr()) {
sb.append(" and ");
} else {
sb.append(" where ");
}
sb.append(dynamicWhere);
sb.append(" ");
}
String preHaving = sql.getPreHaving();
if (!isEmpty(preHaving)) {
sb.append(preHaving);
sb.append(" ");
}
String dbHaving = predicates.getDbHaving();
if (!isEmpty(dbHaving)) {
sb.append(" ");
if (sql.isAndHavingExpr()) {
sb.append("and ");
} else {
sb.append("having ");
}
sb.append(dbHaving);
sb.append(" ");
}
if (!isEmpty(orderBy)) {
sb.append(" ").append(sql.getOrderByPrefix()).append(" ").append(orderBy);
}
return sb.toString().trim();
}
private boolean isEmpty(String s) {
return s == null || s.length() == 0;
}
private String getOrderBy(CQueryPredicates predicates, RawSql.Sql sql) {
String orderBy = predicates.getDbOrderBy();
if (orderBy != null) {
return orderBy;
} else {
return sql.getOrderBy();
}
}
}
@@ -1,344 +1,344 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.lib.util.Str;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
/**
* Handles the Object Relational fetching.
*/
public class CQueryEngine {
private static final Logger logger = LoggerFactory.getLogger(CQueryEngine.class);
private static final int defaultSecondaryQueryBatchSize = 100;
private final boolean forwardOnlyHintOnFindIterate;
private final CQueryBuilder queryBuilder;
public CQueryEngine(DatabasePlatform dbPlatform, Binder binder) {
this.forwardOnlyHintOnFindIterate = dbPlatform.isForwardOnlyHintOnFindIterate();
this.queryBuilder = new CQueryBuilder(dbPlatform, binder);
}
public <T> CQuery<T> buildQuery(OrmQueryRequest<T> request) {
return queryBuilder.buildQuery(request);
}
/**
* Build and execute the find Id's query.
*/
public <T> BeanIdList findIds(OrmQueryRequest<T> request) {
CQueryFetchIds rcQuery = queryBuilder.buildFetchIdsQuery(request);
try {
BeanIdList list = rcQuery.findIds();
if (request.isLogSql()) {
String logSql = rcQuery.getGeneratedSql();
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql = Str.add(logSql, "; --bind(", rcQuery.getBindLog(), ")");
}
request.logSql(logSql);
}
if (request.isLogSummary()) {
request.getTransaction().logSummary(rcQuery.getSummary());
}
if (!list.isFetchingInBackground() && request.getQuery().isFutureFetch()) {
// end the transaction for futureFindIds (it had it's own one)
logger.debug("Future findIds completed!");
request.getTransaction().end();
}
return list;
} catch (SQLException e) {
throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql());
}
}
/**
* Build and execute the row count query.
*/
public <T> int findRowCount(OrmQueryRequest<T> request) {
CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request);
try {
int rowCount = rcQuery.findRowCount();
if (request.isLogSql()) {
String logSql = rcQuery.getGeneratedSql();
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql= Str.add(logSql, "; --bind(", rcQuery.getBindLog(), ")");
}
request.logSql(logSql);
}
if (request.isLogSummary()) {
request.getTransaction().logSummary(rcQuery.getSummary());
}
if (request.getQuery().isFutureFetch()) {
logger.debug("Future findRowCount completed!");
request.getTransaction().end();
}
return rowCount;
} 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 <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
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<T> readIterate = cquery.readIterate(iterateBufferSize, request);
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
return readIterate;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
}
}
/**
* Find a list/map/set of beans.
*/
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
if (!cquery.prepareBindExecuteQuery()) {
// query has been cancelled already
logger.trace("Future fetch already cancelled");
return null;
}
if (request.isLogSql()) {
logSql(cquery);
}
BeanCollection<T> beanCollection = cquery.readCollection();
BeanCollectionTouched collectionTouched = request.getQuery().getBeanCollectionTouched();
if (collectionTouched != null) {
// register a listener that wants to be notified when the
// bean collection is first used
beanCollection.setBeanCollectionTouched(collectionTouched);
}
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
request.executeSecondaryQueries();
return beanCollection;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
if (cquery != null) {
cquery.close();
}
if (request.getQuery().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> T find(OrmQueryRequest<T> request) {
EntityBean bean = null;
CQuery<T> cquery = queryBuilder.buildQuery(request);
try {
cquery.prepareBindExecuteQuery();
if (request.isLogSql()) {
logSql(cquery);
}
if (cquery.readBean()) {
bean = cquery.getLoadedBean();
}
if (request.isLogSummary()) {
logFindBeanSummary(cquery);
}
request.executeSecondaryQueries();
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.isAutofetchTuned()) {
msg.append("tuned[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.isAutofetchTuned()) {
msg.append("tuned[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("] name[").append(q.getName());
msg.append("] predicates[").append(q.getLogWhereSql());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logSummary(msg.toString());
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.BeanCollectionTouched;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.ObjectGraphNode;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.lib.util.Str;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
/**
* Handles the Object Relational fetching.
*/
public class CQueryEngine {
private static final Logger logger = LoggerFactory.getLogger(CQueryEngine.class);
private static final int defaultSecondaryQueryBatchSize = 100;
private final boolean forwardOnlyHintOnFindIterate;
private final CQueryBuilder queryBuilder;
public CQueryEngine(DatabasePlatform dbPlatform, Binder binder) {
this.forwardOnlyHintOnFindIterate = dbPlatform.isForwardOnlyHintOnFindIterate();
this.queryBuilder = new CQueryBuilder(dbPlatform, binder);
}
public <T> CQuery<T> buildQuery(OrmQueryRequest<T> request) {
return queryBuilder.buildQuery(request);
}
/**
* Build and execute the find Id's query.
*/
public <T> BeanIdList findIds(OrmQueryRequest<T> request) {
CQueryFetchIds rcQuery = queryBuilder.buildFetchIdsQuery(request);
try {
BeanIdList list = rcQuery.findIds();
if (request.isLogSql()) {
String logSql = rcQuery.getGeneratedSql();
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql = Str.add(logSql, "; --bind(", rcQuery.getBindLog(), ")");
}
request.logSql(logSql);
}
if (request.isLogSummary()) {
request.getTransaction().logSummary(rcQuery.getSummary());
}
if (!list.isFetchingInBackground() && request.getQuery().isFutureFetch()) {
// end the transaction for futureFindIds (it had it's own one)
logger.debug("Future findIds completed!");
request.getTransaction().end();
}
return list;
} catch (SQLException e) {
throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql());
}
}
/**
* Build and execute the row count query.
*/
public <T> int findRowCount(OrmQueryRequest<T> request) {
CQueryRowCount rcQuery = queryBuilder.buildRowCountQuery(request);
try {
int rowCount = rcQuery.findRowCount();
if (request.isLogSql()) {
String logSql = rcQuery.getGeneratedSql();
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql= Str.add(logSql, "; --bind(", rcQuery.getBindLog(), ")");
}
request.logSql(logSql);
}
if (request.isLogSummary()) {
request.getTransaction().logSummary(rcQuery.getSummary());
}
if (request.getQuery().isFutureFetch()) {
logger.debug("Future findRowCount completed!");
request.getTransaction().end();
}
return rowCount;
} 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 <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
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<T> readIterate = cquery.readIterate(iterateBufferSize, request);
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
return readIterate;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
}
}
/**
* Find a list/map/set of beans.
*/
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
CQuery<T> cquery = queryBuilder.buildQuery(request);
request.setCancelableQuery(cquery);
try {
if (!cquery.prepareBindExecuteQuery()) {
// query has been cancelled already
logger.trace("Future fetch already cancelled");
return null;
}
if (request.isLogSql()) {
logSql(cquery);
}
BeanCollection<T> beanCollection = cquery.readCollection();
BeanCollectionTouched collectionTouched = request.getQuery().getBeanCollectionTouched();
if (collectionTouched != null) {
// register a listener that wants to be notified when the
// bean collection is first used
beanCollection.setBeanCollectionTouched(collectionTouched);
}
if (request.isLogSummary()) {
logFindManySummary(cquery);
}
request.executeSecondaryQueries();
return beanCollection;
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
} finally {
if (cquery != null) {
cquery.close();
}
if (request.getQuery().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> T find(OrmQueryRequest<T> request) {
EntityBean bean = null;
CQuery<T> cquery = queryBuilder.buildQuery(request);
try {
cquery.prepareBindExecuteQuery();
if (request.isLogSql()) {
logSql(cquery);
}
if (cquery.readBean()) {
bean = cquery.getLoadedBean();
}
if (request.isLogSummary()) {
logFindBeanSummary(cquery);
}
request.executeSecondaryQueries();
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.isAutofetchTuned()) {
msg.append("tuned[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.isAutofetchTuned()) {
msg.append("tuned[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("] name[").append(q.getName());
msg.append("] predicates[").append(q.getLogWhereSql());
msg.append("] bind[").append(q.getBindLog()).append("]");
q.getTransaction().logSummary(msg.toString());
}
}
@@ -1,283 +1,283 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.DataReader;
import com.avaje.ebeaninternal.server.type.RsetDataReader;
/**
* Executes the select row count query.
*/
public class CQueryFetchIds {
private static final Logger logger = LoggerFactory.getLogger(CQueryFetchIds.class);
/**
* The overall find request wrapper object.
*/
private final OrmQueryRequest<?> request;
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Where clause predicates.
*/
private final CQueryPredicates predicates;
/**
* The final sql that is generated.
*/
private final String sql;
private RsetDataReader dataReader;
/**
* The statement used to create the resultSet.
*/
private PreparedStatement pstmt;
private String bindLog;
private long startNano;
private int executionTimeMicros;
private int rowCount;
private final int maxRows;
/**
* Create the Sql select based on the request.
*/
public CQueryFetchIds(OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
this.request = request;
this.query = request.getQuery();
this.sql = sql;
this.maxRows = query.getMaxRows();
query.setGeneratedSql(sql);
this.desc = request.getBeanDescriptor();
this.predicates = predicates;
}
/**
* Return a summary description of this query.
*/
public String getSummary() {
StringBuilder sb = new StringBuilder();
sb.append("FindIds exeMicros[").append(executionTimeMicros)
.append("] rows[").append(rowCount)
.append("] type[").append(desc.getName())
.append("] predicates[").append(predicates.getLogWhereSql())
.append("] bind[").append(bindLog).append("]");
return sb.toString();
}
/**
* Return the bind log.
*/
public String getBindLog() {
return bindLog;
}
/**
* Return the generated sql.
*/
public String getGeneratedSql() {
return sql;
}
public SpiOrmQueryRequest<?> getQueryRequest() {
return request;
}
/**
* Execute the query returning the row count.
*/
public BeanIdList findIds() throws SQLException {
startNano = System.nanoTime();
try {
// get the list that we are going to put the id's into.
// This was already set so that it is available to be
// read by other threads (it is a synchronised list)
List<Object> idList = query.getIdList();
if (idList == null){
// running in foreground thread (not FutureIds query)
idList = Collections.synchronizedList(new ArrayList<Object>());
query.setIdList(idList);
}
BeanIdList result = new BeanIdList(idList);
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
if (query.getBufferFetchSizeHint() > 0){
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(new DataBind(pstmt));
ResultSet rset = pstmt.executeQuery();
dataReader = new RsetDataReader(rset);
boolean hitMaxRows = false;
boolean hasMoreRows = false;
rowCount = 0;
DbReadContext ctx = new DbContext();
while (rset.next()){
Object idValue = desc.getIdBinder().read(ctx);
idList.add(idValue);
// reset back to 0
dataReader.resetColumnPosition();
rowCount++;
if (maxRows > 0 && rowCount == maxRows) {
hitMaxRows = true;
hasMoreRows = rset.next();
break;
}
}
if (hitMaxRows){
result.setHasMore(hasMoreRows);
}
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = (int)exeNano/1000;
return result;
} finally {
close();
}
}
/**
* Close the resources.
* <p>
* The jdbc resultSet and statement need to be closed. Its important that
* this method is called.
* </p>
*/
private void close() {
try {
if (dataReader != null) {
dataReader.close();
dataReader = null;
}
} catch (SQLException e) {
logger.error(null, e);
}
try {
if (pstmt != null) {
pstmt.close();
pstmt = null;
}
} catch (SQLException e) {
logger.error(null, e);
}
}
class DbContext implements DbReadContext {
public void propagateState(Object e) {
throw new RuntimeException("Not Called");
}
public Mode getQueryMode() {
return Mode.NORMAL;
}
public DataReader getDataReader() {
return dataReader;
}
public Boolean isReadOnly() {
return Boolean.FALSE;
}
public boolean isRawSql() {
return false;
}
public void register(String path, EntityBeanIntercept ebi){
}
public void register(String path, BeanCollection<?> bc){
}
public BeanPropertyAssocMany<?> getManyProperty() {
// always null
return null;
}
public PersistenceContext getPersistenceContext() {
// always null
return null;
}
public boolean isAutoFetchProfiling() {
return false;
}
public void profileBean(EntityBeanIntercept ebi, String prefix) {
// no-op
}
public void setCurrentPrefix(String currentPrefix,Map<String, String> pathMap) {
// no-op
}
public void setLoadedBean(EntityBean loadedBean, Object id, Object lazyLoadParentId) {
// no-op
}
public void setLoadedManyBean(EntityBean loadedBean) {
// no-op
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.type.DataReader;
import com.avaje.ebeaninternal.server.type.RsetDataReader;
/**
* Executes the select row count query.
*/
public class CQueryFetchIds {
private static final Logger logger = LoggerFactory.getLogger(CQueryFetchIds.class);
/**
* The overall find request wrapper object.
*/
private final OrmQueryRequest<?> request;
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Where clause predicates.
*/
private final CQueryPredicates predicates;
/**
* The final sql that is generated.
*/
private final String sql;
private RsetDataReader dataReader;
/**
* The statement used to create the resultSet.
*/
private PreparedStatement pstmt;
private String bindLog;
private long startNano;
private int executionTimeMicros;
private int rowCount;
private final int maxRows;
/**
* Create the Sql select based on the request.
*/
public CQueryFetchIds(OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
this.request = request;
this.query = request.getQuery();
this.sql = sql;
this.maxRows = query.getMaxRows();
query.setGeneratedSql(sql);
this.desc = request.getBeanDescriptor();
this.predicates = predicates;
}
/**
* Return a summary description of this query.
*/
public String getSummary() {
StringBuilder sb = new StringBuilder();
sb.append("FindIds exeMicros[").append(executionTimeMicros)
.append("] rows[").append(rowCount)
.append("] type[").append(desc.getName())
.append("] predicates[").append(predicates.getLogWhereSql())
.append("] bind[").append(bindLog).append("]");
return sb.toString();
}
/**
* Return the bind log.
*/
public String getBindLog() {
return bindLog;
}
/**
* Return the generated sql.
*/
public String getGeneratedSql() {
return sql;
}
public SpiOrmQueryRequest<?> getQueryRequest() {
return request;
}
/**
* Execute the query returning the row count.
*/
public BeanIdList findIds() throws SQLException {
startNano = System.nanoTime();
try {
// get the list that we are going to put the id's into.
// This was already set so that it is available to be
// read by other threads (it is a synchronised list)
List<Object> idList = query.getIdList();
if (idList == null){
// running in foreground thread (not FutureIds query)
idList = Collections.synchronizedList(new ArrayList<Object>());
query.setIdList(idList);
}
BeanIdList result = new BeanIdList(idList);
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
if (query.getBufferFetchSizeHint() > 0){
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(new DataBind(pstmt));
ResultSet rset = pstmt.executeQuery();
dataReader = new RsetDataReader(rset);
boolean hitMaxRows = false;
boolean hasMoreRows = false;
rowCount = 0;
DbReadContext ctx = new DbContext();
while (rset.next()){
Object idValue = desc.getIdBinder().read(ctx);
idList.add(idValue);
// reset back to 0
dataReader.resetColumnPosition();
rowCount++;
if (maxRows > 0 && rowCount == maxRows) {
hitMaxRows = true;
hasMoreRows = rset.next();
break;
}
}
if (hitMaxRows){
result.setHasMore(hasMoreRows);
}
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = (int)exeNano/1000;
return result;
} finally {
close();
}
}
/**
* Close the resources.
* <p>
* The jdbc resultSet and statement need to be closed. Its important that
* this method is called.
* </p>
*/
private void close() {
try {
if (dataReader != null) {
dataReader.close();
dataReader = null;
}
} catch (SQLException e) {
logger.error(null, e);
}
try {
if (pstmt != null) {
pstmt.close();
pstmt = null;
}
} catch (SQLException e) {
logger.error(null, e);
}
}
class DbContext implements DbReadContext {
public void propagateState(Object e) {
throw new RuntimeException("Not Called");
}
public Mode getQueryMode() {
return Mode.NORMAL;
}
public DataReader getDataReader() {
return dataReader;
}
public Boolean isReadOnly() {
return Boolean.FALSE;
}
public boolean isRawSql() {
return false;
}
public void register(String path, EntityBeanIntercept ebi){
}
public void register(String path, BeanCollection<?> bc){
}
public BeanPropertyAssocMany<?> getManyProperty() {
// always null
return null;
}
public PersistenceContext getPersistenceContext() {
// always null
return null;
}
public boolean isAutoFetchProfiling() {
return false;
}
public void profileBean(EntityBeanIntercept ebi, String prefix) {
// no-op
}
public void setCurrentPrefix(String currentPrefix,Map<String, String> pathMap) {
// no-op
}
public void setLoadedBean(EntityBean loadedBean, Object id, Object lazyLoadParentId) {
// no-op
}
public void setLoadedManyBean(EntityBean loadedBean) {
// no-op
}
}
}
@@ -1,46 +1,46 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import javax.persistence.PersistenceException;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* QueryIterator that does not require a buffer for secondary queries.
*/
class CQueryIteratorSimple<T> implements QueryIterator<T> {
private final CQuery<T> cquery;
private final OrmQueryRequest<T> request;
CQueryIteratorSimple(CQuery<T> cquery, OrmQueryRequest<T> request) {
this.cquery = cquery;
this.request = request;
}
public boolean hasNext() {
try {
request.flushPersistenceContextOnIterate();
return cquery.hasNextBean();
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
}
}
@SuppressWarnings("unchecked")
public T next() {
return (T)cquery.getLoadedBean();
}
public void close() {
cquery.updateExecutionStatistics();
cquery.close();
request.endTransIfRequired();
}
public void remove() {
throw new PersistenceException("Remove not allowed");
}
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import javax.persistence.PersistenceException;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* QueryIterator that does not require a buffer for secondary queries.
*/
class CQueryIteratorSimple<T> implements QueryIterator<T> {
private final CQuery<T> cquery;
private final OrmQueryRequest<T> request;
CQueryIteratorSimple(CQuery<T> cquery, OrmQueryRequest<T> request) {
this.cquery = cquery;
this.request = request;
}
public boolean hasNext() {
try {
request.flushPersistenceContextOnIterate();
return cquery.hasNextBean();
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
}
}
@SuppressWarnings("unchecked")
public T next() {
return (T)cquery.getLoadedBean();
}
public void close() {
cquery.updateExecutionStatistics();
cquery.close();
request.endTransIfRequired();
}
public void remove() {
throw new PersistenceException("Remove not allowed");
}
}
@@ -1,68 +1,68 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.persistence.PersistenceException;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* A QueryIterator that uses a buffer to execute secondary queries periodically.
*/
class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
private final CQuery<T> cquery;
private final int bufferSize;
private final OrmQueryRequest<T> request;
private final ArrayList<T> buffer;
private boolean moreToLoad = true;
CQueryIteratorWithBuffer(CQuery<T> cquery, OrmQueryRequest<T> request, int bufferSize) {
this.cquery = cquery;
this.request = request;
this.bufferSize = bufferSize;
this.buffer = new ArrayList<T>(bufferSize);
}
@SuppressWarnings("unchecked")
public boolean hasNext() {
try {
if (buffer.isEmpty() && moreToLoad) {
// load buffer
request.flushPersistenceContextOnIterate();
int i = -1;
while (moreToLoad && ++i < bufferSize) {
if (cquery.hasNextBean()) {
buffer.add((T)cquery.getLoadedBean());
} else {
moreToLoad = false;
}
}
// execute secondary queries
request.executeSecondaryQueries();
}
return !buffer.isEmpty();
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
}
}
public T next() {
return buffer.remove(0);
}
public void close() {
cquery.updateExecutionStatistics();
cquery.close();
request.endTransIfRequired();
}
public void remove() {
throw new PersistenceException("Remove not allowed");
}
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.persistence.PersistenceException;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
/**
* A QueryIterator that uses a buffer to execute secondary queries periodically.
*/
class CQueryIteratorWithBuffer<T> implements QueryIterator<T> {
private final CQuery<T> cquery;
private final int bufferSize;
private final OrmQueryRequest<T> request;
private final ArrayList<T> buffer;
private boolean moreToLoad = true;
CQueryIteratorWithBuffer(CQuery<T> cquery, OrmQueryRequest<T> request, int bufferSize) {
this.cquery = cquery;
this.request = request;
this.bufferSize = bufferSize;
this.buffer = new ArrayList<T>(bufferSize);
}
@SuppressWarnings("unchecked")
public boolean hasNext() {
try {
if (buffer.isEmpty() && moreToLoad) {
// load buffer
request.flushPersistenceContextOnIterate();
int i = -1;
while (moreToLoad && ++i < bufferSize) {
if (cquery.hasNextBean()) {
buffer.add((T)cquery.getLoadedBean());
} else {
moreToLoad = false;
}
}
// execute secondary queries
request.executeSecondaryQueries();
}
return !buffer.isEmpty();
} catch (SQLException e) {
throw cquery.createPersistenceException(e);
}
}
public T next() {
return buffer.remove(0);
}
public void close() {
cquery.updateExecutionStatistics();
cquery.close();
request.endTransIfRequired();
}
public void remove() {
throw new PersistenceException("Remove not allowed");
}
}
@@ -1,77 +1,77 @@
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.OrderBy.Property;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
/**
* Creates the order by expression clause.
*/
public class CQueryOrderBy {
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Create the logical order by clause.
*/
public static String parse(BeanDescriptor<?> desc, SpiQuery<?> query) {
return new CQueryOrderBy(desc, query).parseInternal();
}
private CQueryOrderBy(BeanDescriptor<?> desc, SpiQuery<?> query) {
this.desc = desc;
this.query = query;
}
private String parseInternal() {
OrderBy<?> orderBy = query.getOrderBy();
if (orderBy == null){
return null;
}
StringBuilder sb = new StringBuilder();
List<Property> properties = orderBy.getProperties();
if (properties.isEmpty()){
// order by clause removed by filterMany()
return null;
}
for (int i = 0; i < properties.size(); i++) {
if (i > 0){
sb.append(", ");
}
Property p = properties.get(i);
String expression = parseProperty(p);
sb.append(expression);
}
return sb.toString();
}
private String parseProperty(Property p) {
String propName = p.getProperty();
ElPropertyValue el = desc.getElGetValue(propName);
if (el == null){
return p.toStringFormat();
}
BeanProperty beanProperty = el.getBeanProperty();
if (beanProperty instanceof BeanPropertyAssoc<?>){
BeanPropertyAssoc<?> ap = (BeanPropertyAssoc<?>)beanProperty;
IdBinder idBinder = ap.getTargetDescriptor().getIdBinder();
return idBinder.getOrderBy(el.getElName(), p.isAscending());
}
return p.toStringFormat();
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import com.avaje.ebean.OrderBy;
import com.avaje.ebean.OrderBy.Property;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
/**
* Creates the order by expression clause.
*/
public class CQueryOrderBy {
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Create the logical order by clause.
*/
public static String parse(BeanDescriptor<?> desc, SpiQuery<?> query) {
return new CQueryOrderBy(desc, query).parseInternal();
}
private CQueryOrderBy(BeanDescriptor<?> desc, SpiQuery<?> query) {
this.desc = desc;
this.query = query;
}
private String parseInternal() {
OrderBy<?> orderBy = query.getOrderBy();
if (orderBy == null){
return null;
}
StringBuilder sb = new StringBuilder();
List<Property> properties = orderBy.getProperties();
if (properties.isEmpty()){
// order by clause removed by filterMany()
return null;
}
for (int i = 0; i < properties.size(); i++) {
if (i > 0){
sb.append(", ");
}
Property p = properties.get(i);
String expression = parseProperty(p);
sb.append(expression);
}
return sb.toString();
}
private String parseProperty(Property p) {
String propName = p.getProperty();
ElPropertyValue el = desc.getElGetValue(propName);
if (el == null){
return p.toStringFormat();
}
BeanProperty beanProperty = el.getBeanProperty();
if (beanProperty instanceof BeanPropertyAssoc<?>){
BeanPropertyAssoc<?> ap = (BeanPropertyAssoc<?>)beanProperty;
IdBinder idBinder = ap.getTargetDescriptor().getIdBinder();
return idBinder.getOrderBy(el.getElName(), p.isAscending());
}
return p.toStringFormat();
}
}
@@ -1,54 +1,54 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.ResultSet;
import java.util.List;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.type.DataReader;
import com.avaje.ebeaninternal.server.type.RsetDataReaderIndexed;
/**
* RawSql based query plan.
*/
public class CQueryPlanRawSql extends CQueryPlan {
private final int[] rsetIndexPositions;
public CQueryPlanRawSql(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) {
super(request, sqlRes, sqlTree, true, logWhereSql);
this.rsetIndexPositions = createIndexPositions(request, sqlTree);
}
public DataReader createDataReader(ResultSet rset) {
return new RsetDataReaderIndexed(rset, rsetIndexPositions, isRowNumberIncluded());
}
private int[] createIndexPositions(OrmQueryRequest<?> request, SqlTree sqlTree) {
List<String> chain = sqlTree.buildSelectExpressionChain();
ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping();
InheritInfo inheritInfo = request.getBeanDescriptor().getInheritInfo();
boolean addDiscriminator = inheritInfo != null;
int offset = addDiscriminator ? 1 : 0;
int[] indexPositions = new int[chain.size() + offset];
if (addDiscriminator) {
// discriminator column must always be first in the query
indexPositions[0] = 1;
}
for (int i = 0; i < chain.size(); i++) {
String expr = chain.get(i);
int indexPos = 1 + columnMapping.getIndexPosition(expr);
indexPositions[i + offset] = indexPos;
}
return indexPositions;
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.ResultSet;
import java.util.List;
import com.avaje.ebean.RawSql.ColumnMapping;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.type.DataReader;
import com.avaje.ebeaninternal.server.type.RsetDataReaderIndexed;
/**
* RawSql based query plan.
*/
public class CQueryPlanRawSql extends CQueryPlan {
private final int[] rsetIndexPositions;
public CQueryPlanRawSql(OrmQueryRequest<?> request, SqlLimitResponse sqlRes, SqlTree sqlTree, String logWhereSql) {
super(request, sqlRes, sqlTree, true, logWhereSql);
this.rsetIndexPositions = createIndexPositions(request, sqlTree);
}
public DataReader createDataReader(ResultSet rset) {
return new RsetDataReaderIndexed(rset, rsetIndexPositions, isRowNumberIncluded());
}
private int[] createIndexPositions(OrmQueryRequest<?> request, SqlTree sqlTree) {
List<String> chain = sqlTree.buildSelectExpressionChain();
ColumnMapping columnMapping = request.getQuery().getRawSql().getColumnMapping();
InheritInfo inheritInfo = request.getBeanDescriptor().getInheritInfo();
boolean addDiscriminator = inheritInfo != null;
int offset = addDiscriminator ? 1 : 0;
int[] indexPositions = new int[chain.size() + offset];
if (addDiscriminator) {
// discriminator column must always be first in the query
indexPositions[0] = 1;
}
for (int i = 0; i < chain.size(); i++) {
String expr = chain.get(i);
int indexPos = 1 + columnMapping.getIndexPosition(expr);
indexPositions[i + offset] = indexPos;
}
return indexPositions;
}
}
File diff suppressed because it is too large Load Diff
@@ -1,173 +1,173 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.type.DataBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Executes the select row count query.
*/
public class CQueryRowCount {
private static final Logger logger = LoggerFactory.getLogger(CQueryRowCount.class);
/**
* The overall find request wrapper object.
*/
private final OrmQueryRequest<?> request;
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Where clause predicates.
*/
private final CQueryPredicates predicates;
/**
* The final sql that is generated.
*/
private final String sql;
/**
* The resultSet that is read and converted to objects.
*/
private ResultSet rset;
/**
* The statement used to create the resultSet.
*/
private PreparedStatement pstmt;
private String bindLog;
private long startNano;
private int executionTimeMicros;
private int rowCount;
/**
* Create the Sql select based on the request.
*/
public CQueryRowCount(OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
this.request = request;
this.query = request.getQuery();
this.sql = sql;
query.setGeneratedSql(sql);
this.desc = request.getBeanDescriptor();
this.predicates = predicates;
}
/**
* Return a summary description of this query.
*/
public String getSummary() {
StringBuilder sb = new StringBuilder();
sb.append("FindRowCount exeMicros[").append(executionTimeMicros)
.append("] rows[").append(rowCount)
.append("] type[").append(desc.getFullName())
.append("] predicates[").append(predicates.getLogWhereSql())
.append("] bind[").append(bindLog).append("]");
return sb.toString();
}
/**
* Return the bind log.
*/
public String getBindLog() {
return bindLog;
}
/**
* Return the generated sql.
*/
public String getGeneratedSql() {
return sql;
}
public SpiOrmQueryRequest<?> getQueryRequest() {
return request;
}
/**
* Execute the query returning the row count.
*/
public int findRowCount() throws SQLException {
startNano = System.nanoTime();
try {
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(new DataBind(pstmt));
rset = pstmt.executeQuery();
if (!rset.next()){
throw new PersistenceException("Expecting 1 row but got none?");
}
rowCount = rset.getInt(1);
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = (int)exeNano/1000;
return rowCount;
} finally {
close();
}
}
/**
* Close the resources.
* <p>
* The jdbc resultSet and statement need to be closed. Its important that
* this method is called.
* </p>
*/
private void close() {
try {
if (rset != null) {
rset.close();
rset = null;
}
} catch (SQLException e) {
logger.error(null, e);
}
try {
if (pstmt != null) {
pstmt.close();
pstmt = null;
}
} catch (SQLException e) {
logger.error(null, e);
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.persistence.PersistenceException;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.core.SpiOrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.type.DataBind;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Executes the select row count query.
*/
public class CQueryRowCount {
private static final Logger logger = LoggerFactory.getLogger(CQueryRowCount.class);
/**
* The overall find request wrapper object.
*/
private final OrmQueryRequest<?> request;
private final BeanDescriptor<?> desc;
private final SpiQuery<?> query;
/**
* Where clause predicates.
*/
private final CQueryPredicates predicates;
/**
* The final sql that is generated.
*/
private final String sql;
/**
* The resultSet that is read and converted to objects.
*/
private ResultSet rset;
/**
* The statement used to create the resultSet.
*/
private PreparedStatement pstmt;
private String bindLog;
private long startNano;
private int executionTimeMicros;
private int rowCount;
/**
* Create the Sql select based on the request.
*/
public CQueryRowCount(OrmQueryRequest<?> request, CQueryPredicates predicates, String sql) {
this.request = request;
this.query = request.getQuery();
this.sql = sql;
query.setGeneratedSql(sql);
this.desc = request.getBeanDescriptor();
this.predicates = predicates;
}
/**
* Return a summary description of this query.
*/
public String getSummary() {
StringBuilder sb = new StringBuilder();
sb.append("FindRowCount exeMicros[").append(executionTimeMicros)
.append("] rows[").append(rowCount)
.append("] type[").append(desc.getFullName())
.append("] predicates[").append(predicates.getLogWhereSql())
.append("] bind[").append(bindLog).append("]");
return sb.toString();
}
/**
* Return the bind log.
*/
public String getBindLog() {
return bindLog;
}
/**
* Return the generated sql.
*/
public String getGeneratedSql() {
return sql;
}
public SpiOrmQueryRequest<?> getQueryRequest() {
return request;
}
/**
* Execute the query returning the row count.
*/
public int findRowCount() throws SQLException {
startNano = System.nanoTime();
try {
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
bindLog = predicates.bind(new DataBind(pstmt));
rset = pstmt.executeQuery();
if (!rset.next()){
throw new PersistenceException("Expecting 1 row but got none?");
}
rowCount = rset.getInt(1);
long exeNano = System.nanoTime() - startNano;
executionTimeMicros = (int)exeNano/1000;
return rowCount;
} finally {
close();
}
}
/**
* Close the resources.
* <p>
* The jdbc resultSet and statement need to be closed. Its important that
* this method is called.
* </p>
*/
private void close() {
try {
if (rset != null) {
rset.close();
rset = null;
}
} catch (SQLException e) {
logger.error(null, e);
}
try {
if (pstmt != null) {
pstmt.close();
pstmt = null;
}
} catch (SQLException e) {
logger.error(null, e);
}
}
}
@@ -1,36 +1,36 @@
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Base object for making query execution into Callable's.
*
* @author rbygrave
*
* @param <T> the entity bean type
*/
public abstract class CallableQuery<T> {
protected final SpiQuery<T> query;
protected final SpiEbeanServer server;
protected final Transaction transaction;
public CallableQuery(SpiEbeanServer server, SpiQuery<T> query, Transaction t) {
this.server = server;
this.query = query;
this.transaction = t;
}
public SpiQuery<T> getQuery() {
return query;
}
public Transaction getTransaction() {
return transaction;
}
}
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Base object for making query execution into Callable's.
*
* @author rbygrave
*
* @param <T> the entity bean type
*/
public abstract class CallableQuery<T> {
protected final SpiQuery<T> query;
protected final SpiEbeanServer server;
protected final Transaction transaction;
public CallableQuery(SpiEbeanServer server, SpiQuery<T> query, Transaction t) {
this.server = server;
this.query = query;
this.transaction = t;
}
public SpiQuery<T> getQuery() {
return query;
}
public Transaction getTransaction() {
return transaction;
}
}
@@ -1,36 +1,36 @@
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Represent the fetch Id's query as a Callable.
*
* @param <T> the entity bean type
*/
public class CallableQueryIds<T> extends CallableQuery<T> implements Callable<List<Object>> {
public CallableQueryIds(SpiEbeanServer server, SpiQuery<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the find Id's query returning the list of Id's.
*/
public List<Object> call() throws Exception {
// we have already made a copy of the query
// this way the same query instance is available to the
// QueryFutureIds (as so has access to the List before it is done)
try {
return server.findIdsWithCopy(query, transaction);
} finally {
transaction.end();
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Represent the fetch Id's query as a Callable.
*
* @param <T> the entity bean type
*/
public class CallableQueryIds<T> extends CallableQuery<T> implements Callable<List<Object>> {
public CallableQueryIds(SpiEbeanServer server, SpiQuery<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the find Id's query returning the list of Id's.
*/
public List<Object> call() throws Exception {
// we have already made a copy of the query
// this way the same query instance is available to the
// QueryFutureIds (as so has access to the List before it is done)
try {
return server.findIdsWithCopy(query, transaction);
} finally {
transaction.end();
}
}
}
@@ -1,36 +1,36 @@
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Represent the findList query as a Callable.
*
* @param <T> the entity bean type
*/
public class CallableQueryList<T> extends CallableQuery<T> implements Callable<List<T>> {
public CallableQueryList(SpiEbeanServer server, SpiQuery<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the query returning the resulting List.
*/
public List<T> call() throws Exception {
try {
return server.findList(query, transaction);
} finally {
// cleanup the underlying connection
transaction.end();
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Represent the findList query as a Callable.
*
* @param <T> the entity bean type
*/
public class CallableQueryList<T> extends CallableQuery<T> implements Callable<List<T>> {
public CallableQueryList(SpiEbeanServer server, SpiQuery<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the query returning the resulting List.
*/
public List<T> call() throws Exception {
try {
return server.findList(query, transaction);
} finally {
// cleanup the underlying connection
transaction.end();
}
}
}
@@ -1,37 +1,37 @@
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.Callable;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Represent the findRowCount query as a Callable.
*
* @param <T>
* the entity bean type
*/
public class CallableQueryRowCount<T> extends CallableQuery<T> implements Callable<Integer> {
/**
* Note that the transaction passed in is always a new transaction solely to
* find the row count so it must be cleaned up by this CallableQueryRowCount.
*/
public CallableQueryRowCount(SpiEbeanServer server, SpiQuery<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the query returning the row count.
*/
public Integer call() throws Exception {
try {
return server.findRowCountWithCopy(query, transaction);
} finally {
// cleanup the underlying connection
transaction.end();
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.Callable;
import com.avaje.ebean.Transaction;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* Represent the findRowCount query as a Callable.
*
* @param <T>
* the entity bean type
*/
public class CallableQueryRowCount<T> extends CallableQuery<T> implements Callable<Integer> {
/**
* Note that the transaction passed in is always a new transaction solely to
* find the row count so it must be cleaned up by this CallableQueryRowCount.
*/
public CallableQueryRowCount(SpiEbeanServer server, SpiQuery<T> query, Transaction t) {
super(server, query, t);
}
/**
* Execute the query returning the row count.
*/
public Integer call() throws Exception {
try {
return server.findRowCountWithCopy(query, transaction);
} finally {
// cleanup the underlying connection
transaction.end();
}
}
}
@@ -1,41 +1,41 @@
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.Transaction;
/**
* Represent the SQL query findList as a Callable.
*/
public class CallableSqlQueryList implements Callable<List<SqlRow>> {
private final SqlQuery query;
private final EbeanServer server;
private final Transaction transaction;
public CallableSqlQueryList(EbeanServer server, SqlQuery query, Transaction t) {
this.server = server;
this.query = query;
this.transaction = t;
}
/**
* Execute the query returning the resulting list.
*/
public List<SqlRow> call() throws Exception {
try {
return server.findList(query, transaction);
} finally {
transaction.end();
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Callable;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.Transaction;
/**
* Represent the SQL query findList as a Callable.
*/
public class CallableSqlQueryList implements Callable<List<SqlRow>> {
private final SqlQuery query;
private final EbeanServer server;
private final Transaction transaction;
public CallableSqlQueryList(EbeanServer server, SqlQuery query, Transaction t) {
this.server = server;
this.query = query;
this.transaction = t;
}
/**
* Execute the query returning the resulting list.
*/
public List<SqlRow> call() throws Exception {
try {
return server.findList(query, transaction);
} finally {
transaction.end();
}
}
}
@@ -1,19 +1,19 @@
package com.avaje.ebeaninternal.server.query;
/**
* Defines a cancelable query.
* <p>
* Typically holds a representation of the PreparedStatement to perform the
* actual cancel.
* </p>
*/
public interface CancelableQuery {
/**
* Cancel the query.
* <p>
* For JDBC this translates to calling cancel on the PreparedStatement.
* </p>
*/
public void cancel();
}
package com.avaje.ebeaninternal.server.query;
/**
* Defines a cancelable query.
* <p>
* Typically holds a representation of the PreparedStatement to perform the
* actual cancel.
* </p>
*/
public interface CancelableQuery {
/**
* Cancel the query.
* <p>
* For JDBC this translates to calling cancel on the PreparedStatement.
* </p>
*/
public void cancel();
}
@@ -1,27 +1,27 @@
package com.avaje.ebeaninternal.server.query;
/**
* Constants used in find processing.
*/
public interface Constants {
/**
* literal used for SQL LIMIT in MySql and Postgres.
*/
public static final String LIMIT = "limit";
/**
* Literal used for SQL LIMIT OFFSET clause in MySql and Postgres.
*/
public static final String OFFSET = "offset";
/**
* ROW_NUMBER() OVER (ORDER BY
*/
public static final String ROW_NUMBER_OVER = "row_number() over (order by ";
/**
* ) as rn,
*/
public static final String ROW_NUMBER_AS = ") as rn, ";
}
package com.avaje.ebeaninternal.server.query;
/**
* Constants used in find processing.
*/
public interface Constants {
/**
* literal used for SQL LIMIT in MySql and Postgres.
*/
public static final String LIMIT = "limit";
/**
* Literal used for SQL LIMIT OFFSET clause in MySql and Postgres.
*/
public static final String OFFSET = "offset";
/**
* ROW_NUMBER() OVER (ORDER BY
*/
public static final String ROW_NUMBER_OVER = "row_number() over (order by ";
/**
* ) as rn,
*/
public static final String ROW_NUMBER_AS = ") as rn, ";
}
@@ -1,125 +1,125 @@
package com.avaje.ebeaninternal.server.query;
import java.util.Collection;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryEngine;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
/**
* Main Finder implementation.
*/
public class DefaultOrmQueryEngine implements OrmQueryEngine {
/**
* Find using predicates
*/
private final CQueryEngine queryEngine;
/**
* Create the Finder.
*/
public DefaultOrmQueryEngine(BeanDescriptorManager descMgr, CQueryEngine queryEngine) {
this.queryEngine = queryEngine;
}
/**
* Flushes the jdbc batch by default unless explicitly turned off on the transaction.
*/
private <T> void flushJdbcBatchOnQuery(OrmQueryRequest<T> request) {
SpiTransaction t = request.getTransaction();
if (t.isBatchFlushOnQuery()) {
// before we perform a query, we need to flush any
// previous persist requests that are queued/batched.
// The query may read data affected by those requests.
t.flushBatch();
}
}
public <T> int findRowCount(OrmQueryRequest<T> request) {
flushJdbcBatchOnQuery(request);
return queryEngine.findRowCount(request);
}
public <T> BeanIdList findIds(OrmQueryRequest<T> request) {
flushJdbcBatchOnQuery(request);
return queryEngine.findIds(request);
}
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
// LIMITATION: You can not use QueryIterator to load bean cache
flushJdbcBatchOnQuery(request);
return queryEngine.findIterate(request);
}
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
flushJdbcBatchOnQuery(request);
BeanFinder<T> finder = request.getBeanFinder();
BeanCollection<T> result;
if (finder != null) {
// this bean type has its own specific finder
result = finder.findMany(request);
} else {
result = queryEngine.findMany(request);
}
SpiQuery<T> query = request.getQuery();
if (query.isLoadBeanCache()) {
// load the individual beans into the bean cache
BeanDescriptor<T> descriptor = request.getBeanDescriptor();
Collection<T> c = result.getActualDetails();
for (T bean : c) {
descriptor.cacheBeanPutData((EntityBean) bean);
}
}
if (!result.isEmpty() && query.isUseQueryCache()) {
// load the query result into the query cache
request.putToQueryCache(result);
}
return result;
}
/**
* Find a single bean using its unique id.
*/
public <T> T findId(OrmQueryRequest<T> request) {
flushJdbcBatchOnQuery(request);
BeanFinder<T> finder = request.getBeanFinder();
T result;
if (finder != null) {
result = finder.find(request);
} else {
result = queryEngine.find(request);
}
if (result != null && request.isUseBeanCache()) {
request.getBeanDescriptor().cacheBeanPutData((EntityBean) result);
}
return result;
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.Collection;
import com.avaje.ebean.QueryIterator;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.event.BeanFinder;
import com.avaje.ebeaninternal.api.BeanIdList;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.OrmQueryEngine;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptorManager;
/**
* Main Finder implementation.
*/
public class DefaultOrmQueryEngine implements OrmQueryEngine {
/**
* Find using predicates
*/
private final CQueryEngine queryEngine;
/**
* Create the Finder.
*/
public DefaultOrmQueryEngine(BeanDescriptorManager descMgr, CQueryEngine queryEngine) {
this.queryEngine = queryEngine;
}
/**
* Flushes the jdbc batch by default unless explicitly turned off on the transaction.
*/
private <T> void flushJdbcBatchOnQuery(OrmQueryRequest<T> request) {
SpiTransaction t = request.getTransaction();
if (t.isBatchFlushOnQuery()) {
// before we perform a query, we need to flush any
// previous persist requests that are queued/batched.
// The query may read data affected by those requests.
t.flushBatch();
}
}
public <T> int findRowCount(OrmQueryRequest<T> request) {
flushJdbcBatchOnQuery(request);
return queryEngine.findRowCount(request);
}
public <T> BeanIdList findIds(OrmQueryRequest<T> request) {
flushJdbcBatchOnQuery(request);
return queryEngine.findIds(request);
}
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
// LIMITATION: You can not use QueryIterator to load bean cache
flushJdbcBatchOnQuery(request);
return queryEngine.findIterate(request);
}
public <T> BeanCollection<T> findMany(OrmQueryRequest<T> request) {
flushJdbcBatchOnQuery(request);
BeanFinder<T> finder = request.getBeanFinder();
BeanCollection<T> result;
if (finder != null) {
// this bean type has its own specific finder
result = finder.findMany(request);
} else {
result = queryEngine.findMany(request);
}
SpiQuery<T> query = request.getQuery();
if (query.isLoadBeanCache()) {
// load the individual beans into the bean cache
BeanDescriptor<T> descriptor = request.getBeanDescriptor();
Collection<T> c = result.getActualDetails();
for (T bean : c) {
descriptor.cacheBeanPutData((EntityBean) bean);
}
}
if (!result.isEmpty() && query.isUseQueryCache()) {
// load the query result into the query cache
request.putToQueryCache(result);
}
return result;
}
/**
* Find a single bean using its unique id.
*/
public <T> T findId(OrmQueryRequest<T> request) {
flushJdbcBatchOnQuery(request);
BeanFinder<T> finder = request.getBeanFinder();
T result;
if (finder != null) {
result = finder.find(request);
} else {
result = queryEngine.find(request);
}
if (result != null && request.isUseBeanCache()) {
request.getBeanDescriptor().cacheBeanPutData((EntityBean) result);
}
return result;
}
}
@@ -1,231 +1,231 @@
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.SqlQueryListener;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.api.SpiSqlQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.RelationalQueryEngine;
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
import com.avaje.ebeaninternal.server.lib.util.Str;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.sql.*;
import java.util.ArrayList;
/**
* Perform native sql fetches.
*/
public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
private static final Logger logger = LoggerFactory.getLogger(DefaultRelationalQueryEngine.class);
private static final int GLOBAL_ROW_LIMIT = Integer.valueOf(System.getProperty("ebean.query.globallimit","1000000"));
private final Binder binder;
private final String dbTrueValue;
public DefaultRelationalQueryEngine(Binder binder, String dbTrueValue) {
this.binder = binder;
this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue;
}
public Object findMany(RelationalQueryRequest request) {
SpiSqlQuery query = request.getQuery();
long startTime = System.currentTimeMillis();
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
ResultSet rset = null;
PreparedStatement pstmt = null;
String sql = query.getQuery();
BindParams bindParams = query.getBindParams();
if (!bindParams.isEmpty()) {
// convert any named parameters if required
sql = BindParamsParser.parse(bindParams, sql);
}
try {
String bindLog = "";
String[] propNames;
synchronized (query) {
if (query.isCancelled()){
logger.trace("Query already cancelled");
return null;
}
// synchronise for query.cancel() support
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
if (query.getBufferFetchSizeHint() > 0){
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
if (!bindParams.isEmpty()) {
bindLog = binder.bind(bindParams, new DataBind(pstmt));
}
if (request.isLogSql()) {
String logSql = sql;
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql = Str.add(logSql, "; --bind(", bindLog, ")");
}
t.logSql(logSql);
}
rset = pstmt.executeQuery();
propNames = getPropertyNames(rset);
}
// calculate the initialCapacity of the Map to reduce
// rehashing for queries with 12+ columns
float initCap = (propNames.length) / 0.7f;
int estimateCapacity = (int) initCap + 1;
// determine the maxRows limit
int maxRows = GLOBAL_ROW_LIMIT;
if (query.getMaxRows() >= 1) {
maxRows = query.getMaxRows();
}
int loadRowCount = 0;
SqlQueryListener listener = query.getListener();
BeanCollectionWrapper wrapper = new BeanCollectionWrapper(request);
boolean isMap = wrapper.isMap();
String mapKey = query.getMapKey();
SqlRow bean = null;
while (rset.next()) {
synchronized (query) {
// synchronise for query.cancel() support
if (!query.isCancelled()){
bean = readRow(rset, propNames, estimateCapacity);
}
}
if (bean != null){
// bean can be null if query cancelled
if (listener != null) {
listener.process(bean);
} else {
if (isMap) {
Object keyValue = bean.get(mapKey);
wrapper.addToMap(bean, keyValue);
} else {
wrapper.addToCollection(bean);
}
}
loadRowCount++;
if (loadRowCount == maxRows) {
// break, as we have hit the max rows to fetch...
break;
}
}
}
BeanCollection<?> beanColl = wrapper.getBeanCollection();
if (request.isLogSummary()) {
long exeTime = System.currentTimeMillis() - startTime;
String msg = "SqlQuery rows[" + loadRowCount + "] time[" + exeTime + "] bind[" + bindLog + "]";
t.logSummary(msg);
}
if (query.isCancelled()){
logger.debug("Query was cancelled during execution rows:"+loadRowCount);
}
return beanColl;
} catch (Exception e) {
String m = Message.msg("fetch.error", e.getMessage(), sql);
throw new PersistenceException(m, e);
} finally {
try {
if (rset != null) {
rset.close();
}
} catch (SQLException e) {
logger.error(null, e);
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
logger.error(null, e);
}
}
}
/**
* Build the list of property names.
*/
protected String[] getPropertyNames(ResultSet rset) throws SQLException {
ArrayList<String> propNames = new ArrayList<String>();
ResultSetMetaData rsmd = rset.getMetaData();
int columnsPlusOne = rsmd.getColumnCount()+1;
for (int i = 1; i < columnsPlusOne; i++) {
String columnName = rsmd.getColumnLabel(i);
// will convert columnName to lower case
propNames.add(columnName);
}
return propNames.toArray(new String[propNames.size()]);
}
/**
* Read the row from the ResultSet and return as a MapBean.
*/
protected SqlRow readRow(ResultSet rset, String[] propNames, int initialCapacity) throws SQLException {
// by default a map will rehash on the 12th entry
// it will be pretty common to have 12 or more entries so
// to reduce rehashing I am trying to estimate a good
// initial capacity for the MapBean to use.
SqlRow bean = new DefaultSqlRow(initialCapacity, 0.75f, dbTrueValue);
int index = 0;
for (int i = 0; i < propNames.length; i++) {
index++;
Object value = rset.getObject(index);
bean.set(propNames[i], value);
}
return bean;
}
}
package com.avaje.ebeaninternal.server.query;
import com.avaje.ebean.SqlQueryListener;
import com.avaje.ebean.SqlRow;
import com.avaje.ebean.bean.BeanCollection;
import com.avaje.ebeaninternal.api.BindParams;
import com.avaje.ebeaninternal.api.SpiSqlQuery;
import com.avaje.ebeaninternal.api.SpiTransaction;
import com.avaje.ebeaninternal.server.core.Message;
import com.avaje.ebeaninternal.server.core.RelationalQueryEngine;
import com.avaje.ebeaninternal.server.core.RelationalQueryRequest;
import com.avaje.ebeaninternal.server.lib.util.Str;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.transaction.TransactionManager;
import com.avaje.ebeaninternal.server.type.DataBind;
import com.avaje.ebeaninternal.server.util.BindParamsParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.PersistenceException;
import java.sql.*;
import java.util.ArrayList;
/**
* Perform native sql fetches.
*/
public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
private static final Logger logger = LoggerFactory.getLogger(DefaultRelationalQueryEngine.class);
private static final int GLOBAL_ROW_LIMIT = Integer.valueOf(System.getProperty("ebean.query.globallimit","1000000"));
private final Binder binder;
private final String dbTrueValue;
public DefaultRelationalQueryEngine(Binder binder, String dbTrueValue) {
this.binder = binder;
this.dbTrueValue = dbTrueValue == null ? "true" : dbTrueValue;
}
public Object findMany(RelationalQueryRequest request) {
SpiSqlQuery query = request.getQuery();
long startTime = System.currentTimeMillis();
SpiTransaction t = request.getTransaction();
Connection conn = t.getInternalConnection();
ResultSet rset = null;
PreparedStatement pstmt = null;
String sql = query.getQuery();
BindParams bindParams = query.getBindParams();
if (!bindParams.isEmpty()) {
// convert any named parameters if required
sql = BindParamsParser.parse(bindParams, sql);
}
try {
String bindLog = "";
String[] propNames;
synchronized (query) {
if (query.isCancelled()){
logger.trace("Query already cancelled");
return null;
}
// synchronise for query.cancel() support
pstmt = conn.prepareStatement(sql);
if (query.getTimeout() > 0){
pstmt.setQueryTimeout(query.getTimeout());
}
if (query.getBufferFetchSizeHint() > 0){
pstmt.setFetchSize(query.getBufferFetchSizeHint());
}
if (!bindParams.isEmpty()) {
bindLog = binder.bind(bindParams, new DataBind(pstmt));
}
if (request.isLogSql()) {
String logSql = sql;
if (TransactionManager.SQL_LOGGER.isTraceEnabled()) {
logSql = Str.add(logSql, "; --bind(", bindLog, ")");
}
t.logSql(logSql);
}
rset = pstmt.executeQuery();
propNames = getPropertyNames(rset);
}
// calculate the initialCapacity of the Map to reduce
// rehashing for queries with 12+ columns
float initCap = (propNames.length) / 0.7f;
int estimateCapacity = (int) initCap + 1;
// determine the maxRows limit
int maxRows = GLOBAL_ROW_LIMIT;
if (query.getMaxRows() >= 1) {
maxRows = query.getMaxRows();
}
int loadRowCount = 0;
SqlQueryListener listener = query.getListener();
BeanCollectionWrapper wrapper = new BeanCollectionWrapper(request);
boolean isMap = wrapper.isMap();
String mapKey = query.getMapKey();
SqlRow bean = null;
while (rset.next()) {
synchronized (query) {
// synchronise for query.cancel() support
if (!query.isCancelled()){
bean = readRow(rset, propNames, estimateCapacity);
}
}
if (bean != null){
// bean can be null if query cancelled
if (listener != null) {
listener.process(bean);
} else {
if (isMap) {
Object keyValue = bean.get(mapKey);
wrapper.addToMap(bean, keyValue);
} else {
wrapper.addToCollection(bean);
}
}
loadRowCount++;
if (loadRowCount == maxRows) {
// break, as we have hit the max rows to fetch...
break;
}
}
}
BeanCollection<?> beanColl = wrapper.getBeanCollection();
if (request.isLogSummary()) {
long exeTime = System.currentTimeMillis() - startTime;
String msg = "SqlQuery rows[" + loadRowCount + "] time[" + exeTime + "] bind[" + bindLog + "]";
t.logSummary(msg);
}
if (query.isCancelled()){
logger.debug("Query was cancelled during execution rows:"+loadRowCount);
}
return beanColl;
} catch (Exception e) {
String m = Message.msg("fetch.error", e.getMessage(), sql);
throw new PersistenceException(m, e);
} finally {
try {
if (rset != null) {
rset.close();
}
} catch (SQLException e) {
logger.error(null, e);
}
try {
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException e) {
logger.error(null, e);
}
}
}
/**
* Build the list of property names.
*/
protected String[] getPropertyNames(ResultSet rset) throws SQLException {
ArrayList<String> propNames = new ArrayList<String>();
ResultSetMetaData rsmd = rset.getMetaData();
int columnsPlusOne = rsmd.getColumnCount()+1;
for (int i = 1; i < columnsPlusOne; i++) {
String columnName = rsmd.getColumnLabel(i);
// will convert columnName to lower case
propNames.add(columnName);
}
return propNames.toArray(new String[propNames.size()]);
}
/**
* Read the row from the ResultSet and return as a MapBean.
*/
protected SqlRow readRow(ResultSet rset, String[] propNames, int initialCapacity) throws SQLException {
// by default a map will rehash on the 12th entry
// it will be pretty common to have 12 or more entries so
// to reduce rehashing I am trying to estimate a good
// initial capacity for the MapBean to use.
SqlRow bean = new DefaultSqlRow(initialCapacity, 0.75f, dbTrueValue);
int index = 0;
for (int i = 0; i < propNames.length; i++) {
index++;
Object value = rset.getObject(index);
bean.set(propNames[i], value);
}
return bean;
}
}
@@ -1,207 +1,207 @@
package com.avaje.ebeaninternal.server.query;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.ebeaninternal.server.core.BasicTypeConverter;
/**
* Used to return raw SQL query results.
* <p>
* Refer to {@link SqlQuery} for examples.
* </p>
* <p>
* There are convenience methods such as getInteger(), getBigDecimal() etc. The
* reason for these methods is that the values put into this map often come
* straight from the JDBC resultSet. Depending on the JDBC driver it may put a
* different type into a given property. For example an Integer, BigDecimal,
* Double could all be put into a property depending on the JDBC driver used.
* These convenience methods automatically convert the value as required
* returning the type you expect.
* </p>
*/
public class DefaultSqlRow implements SqlRow {
static final long serialVersionUID = -3120927797041336242L;
private final String dbTrueValue;
/**
* The underlying map of property data.
*/
Map<String, Object> map;
/**
* Create with a specific Map implementation.
* <p>
* The default Map implementation is LinkedHashMap.
* </p>
*/
public DefaultSqlRow(Map<String, Object> map, String dbTrueValue) {
this.map = map;
this.dbTrueValue = dbTrueValue;
}
/**
* Create a new MapBean based on a LinkedHashMap with default
* initialCapacity (of 16).
*/
public DefaultSqlRow(String dbTrueValue) {
this.map = new LinkedHashMap<String, Object>();
this.dbTrueValue = dbTrueValue;
}
/**
* Create with an initialCapacity and loadFactor.
* <p>
* The defaults of these are 16 and 0.75.
* </p>
* <p>
* Note that the Map will rehash the contents when the number of keys in
* this map reaches its threshold (initialCapacity * loadFactor).
* </p>
*/
public DefaultSqlRow(int initialCapacity, float loadFactor, String dbTrueValue) {
this.map = new LinkedHashMap<String, Object>(initialCapacity, loadFactor);
this.dbTrueValue = dbTrueValue;
}
public Iterator<String> keys() {
return map.keySet().iterator();
}
public Object remove(Object name) {
name = ((String) name).toLowerCase();
return map.remove(name);
}
public Object get(Object name) {
name = ((String) name).toLowerCase();
return map.get(name);
}
public Object put(String name, Object value) {
return setInternal(name, value);
}
public Object set(String name, Object value) {
return setInternal(name, value);
}
private Object setInternal(String name, Object newValue) {
// MapBean properties are always lowercase
name = name.toLowerCase();
// valueList = null;
return map.put(name, newValue);
}
public UUID getUUID(String name) {
Object val = get(name);
return BasicTypeConverter.toUUID(val);
}
public Boolean getBoolean(String name) {
Object val = get(name);
return BasicTypeConverter.toBoolean(val, dbTrueValue);
}
public Integer getInteger(String name) {
Object val = get(name);
return BasicTypeConverter.toInteger(val);
}
public BigDecimal getBigDecimal(String name) {
Object val = get(name);
return BasicTypeConverter.toBigDecimal(val);
}
public Long getLong(String name) {
Object val = get(name);
return BasicTypeConverter.toLong(val);
}
public Double getDouble(String name) {
Object val = get(name);
return BasicTypeConverter.toDouble(val);
}
public Float getFloat(String name) {
Object val = get(name);
return BasicTypeConverter.toFloat(val);
}
public String getString(String name) {
Object val = get(name);
return BasicTypeConverter.toString(val);
}
public java.util.Date getUtilDate(String name) {
Object val = get(name);
return BasicTypeConverter.toUtilDate(val);
}
public Date getDate(String name) {
Object val = get(name);
return BasicTypeConverter.toDate(val);
}
public Timestamp getTimestamp(String name) {
Object val = get(name);
return BasicTypeConverter.toTimestamp(val);
}
public String toString() {
return map.toString();
}
// ------------------------------------
// Normal map methods...
public void clear() {
map.clear();
}
public boolean containsKey(Object key) {
key = ((String) key).toLowerCase();
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public Set<Map.Entry<String, Object>> entrySet() {
return map.entrySet();
}
public boolean isEmpty() {
return map.isEmpty();
}
public Set<String> keySet() {
return map.keySet();
}
public void putAll(Map<? extends String, ? extends Object> t) {
map.putAll(t);
}
public int size() {
return map.size();
}
public Collection<Object> values() {
return map.values();
}
}
package com.avaje.ebeaninternal.server.query;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.avaje.ebean.SqlQuery;
import com.avaje.ebean.SqlRow;
import com.avaje.ebeaninternal.server.core.BasicTypeConverter;
/**
* Used to return raw SQL query results.
* <p>
* Refer to {@link SqlQuery} for examples.
* </p>
* <p>
* There are convenience methods such as getInteger(), getBigDecimal() etc. The
* reason for these methods is that the values put into this map often come
* straight from the JDBC resultSet. Depending on the JDBC driver it may put a
* different type into a given property. For example an Integer, BigDecimal,
* Double could all be put into a property depending on the JDBC driver used.
* These convenience methods automatically convert the value as required
* returning the type you expect.
* </p>
*/
public class DefaultSqlRow implements SqlRow {
static final long serialVersionUID = -3120927797041336242L;
private final String dbTrueValue;
/**
* The underlying map of property data.
*/
Map<String, Object> map;
/**
* Create with a specific Map implementation.
* <p>
* The default Map implementation is LinkedHashMap.
* </p>
*/
public DefaultSqlRow(Map<String, Object> map, String dbTrueValue) {
this.map = map;
this.dbTrueValue = dbTrueValue;
}
/**
* Create a new MapBean based on a LinkedHashMap with default
* initialCapacity (of 16).
*/
public DefaultSqlRow(String dbTrueValue) {
this.map = new LinkedHashMap<String, Object>();
this.dbTrueValue = dbTrueValue;
}
/**
* Create with an initialCapacity and loadFactor.
* <p>
* The defaults of these are 16 and 0.75.
* </p>
* <p>
* Note that the Map will rehash the contents when the number of keys in
* this map reaches its threshold (initialCapacity * loadFactor).
* </p>
*/
public DefaultSqlRow(int initialCapacity, float loadFactor, String dbTrueValue) {
this.map = new LinkedHashMap<String, Object>(initialCapacity, loadFactor);
this.dbTrueValue = dbTrueValue;
}
public Iterator<String> keys() {
return map.keySet().iterator();
}
public Object remove(Object name) {
name = ((String) name).toLowerCase();
return map.remove(name);
}
public Object get(Object name) {
name = ((String) name).toLowerCase();
return map.get(name);
}
public Object put(String name, Object value) {
return setInternal(name, value);
}
public Object set(String name, Object value) {
return setInternal(name, value);
}
private Object setInternal(String name, Object newValue) {
// MapBean properties are always lowercase
name = name.toLowerCase();
// valueList = null;
return map.put(name, newValue);
}
public UUID getUUID(String name) {
Object val = get(name);
return BasicTypeConverter.toUUID(val);
}
public Boolean getBoolean(String name) {
Object val = get(name);
return BasicTypeConverter.toBoolean(val, dbTrueValue);
}
public Integer getInteger(String name) {
Object val = get(name);
return BasicTypeConverter.toInteger(val);
}
public BigDecimal getBigDecimal(String name) {
Object val = get(name);
return BasicTypeConverter.toBigDecimal(val);
}
public Long getLong(String name) {
Object val = get(name);
return BasicTypeConverter.toLong(val);
}
public Double getDouble(String name) {
Object val = get(name);
return BasicTypeConverter.toDouble(val);
}
public Float getFloat(String name) {
Object val = get(name);
return BasicTypeConverter.toFloat(val);
}
public String getString(String name) {
Object val = get(name);
return BasicTypeConverter.toString(val);
}
public java.util.Date getUtilDate(String name) {
Object val = get(name);
return BasicTypeConverter.toUtilDate(val);
}
public Date getDate(String name) {
Object val = get(name);
return BasicTypeConverter.toDate(val);
}
public Timestamp getTimestamp(String name) {
Object val = get(name);
return BasicTypeConverter.toTimestamp(val);
}
public String toString() {
return map.toString();
}
// ------------------------------------
// Normal map methods...
public void clear() {
map.clear();
}
public boolean containsKey(Object key) {
key = ((String) key).toLowerCase();
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public Set<Map.Entry<String, Object>> entrySet() {
return map.entrySet();
}
public boolean isEmpty() {
return map.isEmpty();
}
public Set<String> keySet() {
return map.keySet();
}
public void putAll(Map<? extends String, ? extends Object> t) {
map.putAll(t);
}
public int size() {
return map.size();
}
public Collection<Object> values() {
return map.values();
}
}
@@ -1,122 +1,122 @@
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Future;
import javax.persistence.PersistenceException;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.PagedList;
import com.avaje.ebeaninternal.api.Monitor;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* PagedList implementation based on limit offset types of queries.
*
* @param <T>
* the entity bean type
*/
public class LimitOffsetPagedList<T> implements PagedList<T> {
private final transient EbeanServer server;
private final SpiQuery<T> query;
private final int pageSize;
private final int pageIndex;
private final Monitor monitor = new Monitor();
private int foregroundTotalRowCount = -1;
private Future<Integer> futureRowCount;
private List<T> list;
public LimitOffsetPagedList(EbeanServer server, SpiQuery<T> query, int pageIndex, int pageSize) {
this.server = server;
this.query = query;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
}
public void loadRowCount() {
getFutureRowCount();
}
public Future<Integer> getFutureRowCount() {
synchronized (monitor) {
if (futureRowCount == null) {
futureRowCount = server.findFutureRowCount(query, null);
}
return futureRowCount;
}
}
public List<T> getList() {
synchronized (monitor) {
if (list == null) {
query.setFirstRow(pageIndex * pageSize);
query.setMaxRows(pageSize);
list = server.findList(query, null);
}
return list;
}
}
public int getTotalPageCount() {
int rowCount = getTotalRowCount();
if (rowCount == 0) {
return 0;
} else {
return ((rowCount - 1) / pageSize) + 1;
}
}
public int getTotalRowCount() {
synchronized (monitor) {
if (futureRowCount != null) {
try {
// background query already initiated so get it with a wait
return futureRowCount.get();
} catch (Exception e) {
throw new PersistenceException(e);
}
}
// already fetched?
if (foregroundTotalRowCount > -1) return foregroundTotalRowCount;
// just using foreground thread
foregroundTotalRowCount = server.findRowCount(query, null);
return foregroundTotalRowCount;
}
}
public boolean hasNext() {
return pageIndex < (getTotalPageCount() - 1);
}
public boolean hasPrev() {
return pageIndex > 0;
}
public int getPageIndex() {
return pageIndex;
}
public int getPageSize() {
return pageSize;
}
public String getDisplayXtoYofZ(String to, String of) {
int first = pageIndex * pageSize + 1;
int last = first + getList().size() - 1;
int total = getTotalRowCount();
return first + to + last + of + total;
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.Future;
import javax.persistence.PersistenceException;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.PagedList;
import com.avaje.ebeaninternal.api.Monitor;
import com.avaje.ebeaninternal.api.SpiQuery;
/**
* PagedList implementation based on limit offset types of queries.
*
* @param <T>
* the entity bean type
*/
public class LimitOffsetPagedList<T> implements PagedList<T> {
private final transient EbeanServer server;
private final SpiQuery<T> query;
private final int pageSize;
private final int pageIndex;
private final Monitor monitor = new Monitor();
private int foregroundTotalRowCount = -1;
private Future<Integer> futureRowCount;
private List<T> list;
public LimitOffsetPagedList(EbeanServer server, SpiQuery<T> query, int pageIndex, int pageSize) {
this.server = server;
this.query = query;
this.pageSize = pageSize;
this.pageIndex = pageIndex;
}
public void loadRowCount() {
getFutureRowCount();
}
public Future<Integer> getFutureRowCount() {
synchronized (monitor) {
if (futureRowCount == null) {
futureRowCount = server.findFutureRowCount(query, null);
}
return futureRowCount;
}
}
public List<T> getList() {
synchronized (monitor) {
if (list == null) {
query.setFirstRow(pageIndex * pageSize);
query.setMaxRows(pageSize);
list = server.findList(query, null);
}
return list;
}
}
public int getTotalPageCount() {
int rowCount = getTotalRowCount();
if (rowCount == 0) {
return 0;
} else {
return ((rowCount - 1) / pageSize) + 1;
}
}
public int getTotalRowCount() {
synchronized (monitor) {
if (futureRowCount != null) {
try {
// background query already initiated so get it with a wait
return futureRowCount.get();
} catch (Exception e) {
throw new PersistenceException(e);
}
}
// already fetched?
if (foregroundTotalRowCount > -1) return foregroundTotalRowCount;
// just using foreground thread
foregroundTotalRowCount = server.findRowCount(query, null);
return foregroundTotalRowCount;
}
}
public boolean hasNext() {
return pageIndex < (getTotalPageCount() - 1);
}
public boolean hasPrev() {
return pageIndex > 0;
}
public int getPageIndex() {
return pageIndex;
}
public int getPageSize() {
return pageSize;
}
public String getDisplayXtoYofZ(String to, String of) {
int first = pageIndex * pageSize + 1;
int last = first + getList().size() - 1;
int total = getTotalRowCount();
return first + to + last + of + total;
}
}
@@ -1,43 +1,43 @@
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.FutureTask;
import com.avaje.ebean.FutureIds;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
/**
* Default implementation of FutureIds.
*/
public class QueryFutureIds<T> extends BaseFuture<List<Object>> implements FutureIds<T> {
private final CallableQueryIds<T> call;
public QueryFutureIds(CallableQueryIds<T> call ) {
super(new FutureTask<List<Object>>(call));
this.call = call;
}
public FutureTask<List<Object>> getFutureTask() {
return futureTask;
}
public Transaction getTransaction() {
return call.transaction;
}
public Query<T> getQuery() {
return call.query;
}
public List<Object> getPartialIds() {
return call.query.getIdList();
}
public boolean cancel(boolean mayInterruptIfRunning) {
call.query.cancel();
return super.cancel(mayInterruptIfRunning);
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.FutureTask;
import com.avaje.ebean.FutureIds;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
/**
* Default implementation of FutureIds.
*/
public class QueryFutureIds<T> extends BaseFuture<List<Object>> implements FutureIds<T> {
private final CallableQueryIds<T> call;
public QueryFutureIds(CallableQueryIds<T> call ) {
super(new FutureTask<List<Object>>(call));
this.call = call;
}
public FutureTask<List<Object>> getFutureTask() {
return futureTask;
}
public Transaction getTransaction() {
return call.transaction;
}
public Query<T> getQuery() {
return call.query;
}
public List<Object> getPartialIds() {
return call.query.getIdList();
}
public boolean cancel(boolean mayInterruptIfRunning) {
call.query.cancel();
return super.cancel(mayInterruptIfRunning);
}
}
@@ -1,68 +1,68 @@
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.avaje.ebean.FutureList;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import javax.persistence.PersistenceException;
/**
* Default implementation for FutureList.
*/
public class QueryFutureList<T> extends BaseFuture<List<T>> implements FutureList<T> {
private final CallableQueryList<T> call;
public QueryFutureList(CallableQueryList<T> call) {
super(new FutureTask<List<T>>(call));
this.call = call;
}
public FutureTask<List<T>> getFutureTask() {
return futureTask;
}
public Transaction getTransaction() {
return call.transaction;
}
@Override
public Query<T> getQuery() {
return call.query;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
call.query.cancel();
return super.cancel(mayInterruptIfRunning);
}
@Override
public List<T> getUnchecked() {
try {
return get();
} catch (InterruptedException e) {
throw new PersistenceException(e);
} catch (ExecutionException e) {
throw new PersistenceException(e);
}
}
@Override
public List<T> getUnchecked(long timeout, TimeUnit unit) throws TimeoutException {
try {
return get(timeout, unit);
} catch (InterruptedException e) {
throw new PersistenceException(e);
} catch (ExecutionException e) {
throw new PersistenceException(e);
}
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import com.avaje.ebean.FutureList;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
import javax.persistence.PersistenceException;
/**
* Default implementation for FutureList.
*/
public class QueryFutureList<T> extends BaseFuture<List<T>> implements FutureList<T> {
private final CallableQueryList<T> call;
public QueryFutureList(CallableQueryList<T> call) {
super(new FutureTask<List<T>>(call));
this.call = call;
}
public FutureTask<List<T>> getFutureTask() {
return futureTask;
}
public Transaction getTransaction() {
return call.transaction;
}
@Override
public Query<T> getQuery() {
return call.query;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
call.query.cancel();
return super.cancel(mayInterruptIfRunning);
}
@Override
public List<T> getUnchecked() {
try {
return get();
} catch (InterruptedException e) {
throw new PersistenceException(e);
} catch (ExecutionException e) {
throw new PersistenceException(e);
}
}
@Override
public List<T> getUnchecked(long timeout, TimeUnit unit) throws TimeoutException {
try {
return get(timeout, unit);
} catch (InterruptedException e) {
throw new PersistenceException(e);
} catch (ExecutionException e) {
throw new PersistenceException(e);
}
}
}
@@ -1,39 +1,39 @@
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.FutureTask;
import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
/**
* Future implementation for the row count query.
*/
public class QueryFutureRowCount<T> extends BaseFuture<Integer> implements FutureRowCount<T> {
private final CallableQueryRowCount<T> call;
public QueryFutureRowCount(CallableQueryRowCount<T> call ) {
super(new FutureTask<Integer>(call));
this.call = call;
}
public FutureTask<Integer> getFutureTask() {
return futureTask;
}
public Transaction getTransaction() {
return call.transaction;
}
public Query<T> getQuery() {
return call.query;
}
public boolean cancel(boolean mayInterruptIfRunning) {
call.query.cancel();
return super.cancel(mayInterruptIfRunning);
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.concurrent.FutureTask;
import com.avaje.ebean.FutureRowCount;
import com.avaje.ebean.Query;
import com.avaje.ebean.Transaction;
/**
* Future implementation for the row count query.
*/
public class QueryFutureRowCount<T> extends BaseFuture<Integer> implements FutureRowCount<T> {
private final CallableQueryRowCount<T> call;
public QueryFutureRowCount(CallableQueryRowCount<T> call ) {
super(new FutureTask<Integer>(call));
this.call = call;
}
public FutureTask<Integer> getFutureTask() {
return futureTask;
}
public Transaction getTransaction() {
return call.transaction;
}
public Query<T> getQuery() {
return call.query;
}
public boolean cancel(boolean mayInterruptIfRunning) {
call.query.cancel();
return super.cancel(mayInterruptIfRunning);
}
}
@@ -1,99 +1,99 @@
package com.avaje.ebeaninternal.server.query;
import javax.persistence.PersistenceException;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.DRawSqlSelect;
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.DeployParser;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Factory for SqlSelectClause based on raw sql.
* <p>
* Its job is to execute the sql, read the meta data to determine the columns to
* bean property mapping.
* </p>
*/
public class RawSqlSelectClauseBuilder {
private static final Logger logger = LoggerFactory.getLogger(RawSqlSelectClauseBuilder.class);
private final Binder binder;
private final SqlLimiter dbQueryLimiter;
private final DatabasePlatform dbPlatform;
public RawSqlSelectClauseBuilder(DatabasePlatform dbPlatform, Binder binder) {
this.binder = binder;
this.dbQueryLimiter = dbPlatform.getSqlLimiter();
this.dbPlatform = dbPlatform;
}
/**
* Build based on the includes and using the BeanJoinTree.
*/
public <T> CQuery<T> build(OrmQueryRequest<T> request) throws PersistenceException {
SpiQuery<T> query = request.getQuery();
BeanDescriptor<T> desc = request.getBeanDescriptor();
DeployNamedQuery namedQuery = desc.getNamedQuery(query.getName());
DRawSqlSelect sqlSelect = namedQuery.getSqlSelect();
// create a parser for this specific SqlSelect... has to be really
// as each SqlSelect could have different table alias etc
DeployParser parser = sqlSelect.createDeployPropertyParser();
CQueryPredicates predicates = new CQueryPredicates(binder, request);
// prepare and convert logical property names to dbColumns etc
predicates.prepareRawSql(parser);
SqlTreeAlias alias = new SqlTreeAlias(sqlSelect.getTableAlias());
predicates.parseTableAlias(alias);
String sql = null;
try {
boolean includeRowNumColumn = false;
String orderBy = sqlSelect.getOrderBy(predicates);
// build the actual sql String
sql = sqlSelect.buildSql(orderBy, predicates, request);
if (query.hasMaxRowsOrFirstRow() && dbQueryLimiter != null) {
// wrap with a limit offset or ROW_NUMBER() etc
SqlLimitResponse limitSql = dbQueryLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform));
includeRowNumColumn = limitSql.isIncludesRowNumberColumn();
sql = limitSql.getSql();
} else {
// add back select keyword
// ... was removed to support dbQueryLimiter
sql = "select " + sql;
}
SqlTree sqlTree = sqlSelect.getSqlTree();
CQueryPlan queryPlan = new CQueryPlan(request, sql, sqlTree, true, includeRowNumColumn, "");
CQuery<T> compiledQuery = new CQuery<T>(request, predicates, queryPlan);
return compiledQuery;
} catch (Exception e) {
String msg = "Error with " + desc.getFullName() + " query:\r" + sql;
logger.error(msg);
throw new PersistenceException(e);
}
}
}
package com.avaje.ebeaninternal.server.query;
import javax.persistence.PersistenceException;
import com.avaje.ebean.config.dbplatform.DatabasePlatform;
import com.avaje.ebean.config.dbplatform.SqlLimitResponse;
import com.avaje.ebean.config.dbplatform.SqlLimiter;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.DRawSqlSelect;
import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery;
import com.avaje.ebeaninternal.server.deploy.DeployParser;
import com.avaje.ebeaninternal.server.persist.Binder;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Factory for SqlSelectClause based on raw sql.
* <p>
* Its job is to execute the sql, read the meta data to determine the columns to
* bean property mapping.
* </p>
*/
public class RawSqlSelectClauseBuilder {
private static final Logger logger = LoggerFactory.getLogger(RawSqlSelectClauseBuilder.class);
private final Binder binder;
private final SqlLimiter dbQueryLimiter;
private final DatabasePlatform dbPlatform;
public RawSqlSelectClauseBuilder(DatabasePlatform dbPlatform, Binder binder) {
this.binder = binder;
this.dbQueryLimiter = dbPlatform.getSqlLimiter();
this.dbPlatform = dbPlatform;
}
/**
* Build based on the includes and using the BeanJoinTree.
*/
public <T> CQuery<T> build(OrmQueryRequest<T> request) throws PersistenceException {
SpiQuery<T> query = request.getQuery();
BeanDescriptor<T> desc = request.getBeanDescriptor();
DeployNamedQuery namedQuery = desc.getNamedQuery(query.getName());
DRawSqlSelect sqlSelect = namedQuery.getSqlSelect();
// create a parser for this specific SqlSelect... has to be really
// as each SqlSelect could have different table alias etc
DeployParser parser = sqlSelect.createDeployPropertyParser();
CQueryPredicates predicates = new CQueryPredicates(binder, request);
// prepare and convert logical property names to dbColumns etc
predicates.prepareRawSql(parser);
SqlTreeAlias alias = new SqlTreeAlias(sqlSelect.getTableAlias());
predicates.parseTableAlias(alias);
String sql = null;
try {
boolean includeRowNumColumn = false;
String orderBy = sqlSelect.getOrderBy(predicates);
// build the actual sql String
sql = sqlSelect.buildSql(orderBy, predicates, request);
if (query.hasMaxRowsOrFirstRow() && dbQueryLimiter != null) {
// wrap with a limit offset or ROW_NUMBER() etc
SqlLimitResponse limitSql = dbQueryLimiter.limit(new OrmQueryLimitRequest(sql, orderBy, query, dbPlatform));
includeRowNumColumn = limitSql.isIncludesRowNumberColumn();
sql = limitSql.getSql();
} else {
// add back select keyword
// ... was removed to support dbQueryLimiter
sql = "select " + sql;
}
SqlTree sqlTree = sqlSelect.getSqlTree();
CQueryPlan queryPlan = new CQueryPlan(request, sql, sqlTree, true, includeRowNumColumn, "");
CQuery<T> compiledQuery = new CQuery<T>(request, predicates, queryPlan);
return compiledQuery;
} catch (Exception e) {
String msg = "Error with " + desc.getFullName() + " query:\r" + sql;
logger.error(msg);
throw new PersistenceException(e);
}
}
}
@@ -1,98 +1,98 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import javax.persistence.PersistenceException;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
/**
* Controls the loading of property data into a bean.
* <p>
* Takes into account the differences of lazy loading and
* partial objects.
* </p>
*/
public class SqlBeanLoad {
private final DbReadContext ctx;
private final EntityBean bean;
private final EntityBeanIntercept ebi;
private final Class<?> type;
private final boolean lazyLoading;
private final boolean refreshLoading;
private final boolean rawSql;
public SqlBeanLoad(DbReadContext ctx, Class<?> type, EntityBean bean, Mode queryMode) {
this.ctx = ctx;
this.rawSql = ctx.isRawSql();
this.type = type;
this.lazyLoading = queryMode.equals(Mode.LAZYLOAD_BEAN);
this.refreshLoading = queryMode.equals(Mode.REFRESH_BEAN);
this.bean = bean;
this.ebi = bean == null ? null : bean._ebean_getIntercept();
}
/**
* Return true if this is a lazy loading.
*/
public boolean isLazyLoad() {
return lazyLoading;
}
/**
* Increment the resultSet index 1.
*/
public void loadIgnore(int increment) {
ctx.getDataReader().incrementPos(increment);
}
public Object load(BeanProperty prop) throws SQLException {
if (!rawSql && !prop.isLoadProperty()) {
return null;
}
if ((bean == null)
|| (lazyLoading && ebi.isLoadedProperty(prop.getPropertyIndex()))
|| (type != null && !prop.isAssignableFrom(type))) {
// ignore this property
// ... null: bean already in persistence context
// ... lazyLoading: partial bean that is lazy loading
// ... type: inheritance and not assignable to this instance
prop.loadIgnore(ctx);
return null;
}
try {
Object dbVal = prop.read(ctx);
if (!refreshLoading) {
prop.setValue(bean, dbVal);
} else {
prop.setValueIntercept(bean, dbVal);
}
return dbVal;
} catch (Exception e) {
String msg = "Error loading on " + prop.getFullBeanName();
throw new PersistenceException(msg, e);
}
}
public void loadAssocMany(BeanPropertyAssocMany<?> prop) {
// do nothing, as a lazy loading BeanCollection 'reference'
// is created and registered with the loading context
// in SqlTreeNodeBean.createListProxies()
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import javax.persistence.PersistenceException;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.bean.EntityBeanIntercept;
import com.avaje.ebeaninternal.api.SpiQuery.Mode;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
/**
* Controls the loading of property data into a bean.
* <p>
* Takes into account the differences of lazy loading and
* partial objects.
* </p>
*/
public class SqlBeanLoad {
private final DbReadContext ctx;
private final EntityBean bean;
private final EntityBeanIntercept ebi;
private final Class<?> type;
private final boolean lazyLoading;
private final boolean refreshLoading;
private final boolean rawSql;
public SqlBeanLoad(DbReadContext ctx, Class<?> type, EntityBean bean, Mode queryMode) {
this.ctx = ctx;
this.rawSql = ctx.isRawSql();
this.type = type;
this.lazyLoading = queryMode.equals(Mode.LAZYLOAD_BEAN);
this.refreshLoading = queryMode.equals(Mode.REFRESH_BEAN);
this.bean = bean;
this.ebi = bean == null ? null : bean._ebean_getIntercept();
}
/**
* Return true if this is a lazy loading.
*/
public boolean isLazyLoad() {
return lazyLoading;
}
/**
* Increment the resultSet index 1.
*/
public void loadIgnore(int increment) {
ctx.getDataReader().incrementPos(increment);
}
public Object load(BeanProperty prop) throws SQLException {
if (!rawSql && !prop.isLoadProperty()) {
return null;
}
if ((bean == null)
|| (lazyLoading && ebi.isLoadedProperty(prop.getPropertyIndex()))
|| (type != null && !prop.isAssignableFrom(type))) {
// ignore this property
// ... null: bean already in persistence context
// ... lazyLoading: partial bean that is lazy loading
// ... type: inheritance and not assignable to this instance
prop.loadIgnore(ctx);
return null;
}
try {
Object dbVal = prop.read(ctx);
if (!refreshLoading) {
prop.setValue(bean, dbVal);
} else {
prop.setValueIntercept(bean, dbVal);
}
return dbVal;
} catch (Exception e) {
String msg = "Error loading on " + prop.getFullBeanName();
throw new PersistenceException(msg, e);
}
}
public void loadAssocMany(BeanPropertyAssocMany<?> prop) {
// do nothing, as a lazy loading BeanCollection 'reference'
// is created and registered with the loading context
// in SqlTreeNodeBean.createListProxies()
}
}
@@ -1,155 +1,155 @@
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
/**
* Represents the SELECT clause part of the SQL query.
*/
public class SqlTree {
private final SqlTreeNode rootNode;
/**
* Property if resultSet contains master and detail rows.
*/
private final BeanPropertyAssocMany<?> manyProperty;
private final String manyPropertyName;
private final ElPropertyValue manyPropEl;
private final Set<String> includes;
/**
* Summary of the select being generated.
*/
private final String summary;
private final String selectSql;
private final String fromSql;
/**
* Encrypted Properties require additional binding.
*/
private final BeanProperty[] encryptedProps;
/**
* Where clause for inheritance.
*/
private final String inheritanceWhereSql;
/**
* Create the SqlSelectClause.
*/
public SqlTree(String summary, SqlTreeNode rootNode, String selectSql, String fromSql, String inheritanceWhereSql,
BeanProperty[] encryptedProps, BeanPropertyAssocMany<?> manyProperty, String manyPropertyName,
ElPropertyValue manyPropEl, Set<String> includes) {
this.summary = summary;
this.rootNode = rootNode;
this.selectSql = selectSql;
this.fromSql = fromSql;
this.inheritanceWhereSql = inheritanceWhereSql;
this.encryptedProps = encryptedProps;
this.manyProperty = manyProperty;
this.manyPropertyName = manyPropertyName;
this.manyPropEl = manyPropEl;
this.includes = includes;
}
/**
* Construct for RawSql.
*/
public SqlTree(String summary, SqlTreeNode rootNode) {
this.summary = summary;
this.rootNode = rootNode;
this.selectSql = null;
this.fromSql = null;
this.inheritanceWhereSql = null;
this.encryptedProps = null;
this.manyProperty = null;
this.manyPropertyName = null;
this.manyPropEl = null;
this.includes = null;
}
/**
* Build a select expression chain for RawSql.
*/
public List<String> buildSelectExpressionChain() {
ArrayList<String> list = new ArrayList<String>();
rootNode.buildSelectExpressionChain(list);
return list;
}
/**
* Return the includes. Associated beans lists etc.
*/
public Set<String> getIncludes() {
return includes;
}
/**
* Return the String for the actual SQL.
*/
public String getSelectSql() {
return selectSql;
}
public String getFromSql() {
return fromSql;
}
/**
* Return the where clause for inheritance.
*/
public String getInheritanceWhereSql() {
return inheritanceWhereSql;
}
/**
* Return a summary of the select clause.
*/
public String getSummary() {
return summary;
}
public SqlTreeNode getRootNode() {
return rootNode;
}
/**
* Return the property that is associated with the many. There can only be one
* per SqlSelect. This can be null.
*/
public BeanPropertyAssocMany<?> getManyProperty() {
return manyProperty;
}
public String getManyPropertyName() {
return manyPropertyName;
}
public ElPropertyValue getManyPropertyEl() {
return manyPropEl;
}
/**
* Return true if this query includes a Many association.
*/
public boolean isManyIncluded() {
return (manyProperty != null);
}
public BeanProperty[] getEncryptedProps() {
return encryptedProps;
}
}
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
/**
* Represents the SELECT clause part of the SQL query.
*/
public class SqlTree {
private final SqlTreeNode rootNode;
/**
* Property if resultSet contains master and detail rows.
*/
private final BeanPropertyAssocMany<?> manyProperty;
private final String manyPropertyName;
private final ElPropertyValue manyPropEl;
private final Set<String> includes;
/**
* Summary of the select being generated.
*/
private final String summary;
private final String selectSql;
private final String fromSql;
/**
* Encrypted Properties require additional binding.
*/
private final BeanProperty[] encryptedProps;
/**
* Where clause for inheritance.
*/
private final String inheritanceWhereSql;
/**
* Create the SqlSelectClause.
*/
public SqlTree(String summary, SqlTreeNode rootNode, String selectSql, String fromSql, String inheritanceWhereSql,
BeanProperty[] encryptedProps, BeanPropertyAssocMany<?> manyProperty, String manyPropertyName,
ElPropertyValue manyPropEl, Set<String> includes) {
this.summary = summary;
this.rootNode = rootNode;
this.selectSql = selectSql;
this.fromSql = fromSql;
this.inheritanceWhereSql = inheritanceWhereSql;
this.encryptedProps = encryptedProps;
this.manyProperty = manyProperty;
this.manyPropertyName = manyPropertyName;
this.manyPropEl = manyPropEl;
this.includes = includes;
}
/**
* Construct for RawSql.
*/
public SqlTree(String summary, SqlTreeNode rootNode) {
this.summary = summary;
this.rootNode = rootNode;
this.selectSql = null;
this.fromSql = null;
this.inheritanceWhereSql = null;
this.encryptedProps = null;
this.manyProperty = null;
this.manyPropertyName = null;
this.manyPropEl = null;
this.includes = null;
}
/**
* Build a select expression chain for RawSql.
*/
public List<String> buildSelectExpressionChain() {
ArrayList<String> list = new ArrayList<String>();
rootNode.buildSelectExpressionChain(list);
return list;
}
/**
* Return the includes. Associated beans lists etc.
*/
public Set<String> getIncludes() {
return includes;
}
/**
* Return the String for the actual SQL.
*/
public String getSelectSql() {
return selectSql;
}
public String getFromSql() {
return fromSql;
}
/**
* Return the where clause for inheritance.
*/
public String getInheritanceWhereSql() {
return inheritanceWhereSql;
}
/**
* Return a summary of the select clause.
*/
public String getSummary() {
return summary;
}
public SqlTreeNode getRootNode() {
return rootNode;
}
/**
* Return the property that is associated with the many. There can only be one
* per SqlSelect. This can be null.
*/
public BeanPropertyAssocMany<?> getManyProperty() {
return manyProperty;
}
public String getManyPropertyName() {
return manyPropertyName;
}
public ElPropertyValue getManyPropertyEl() {
return manyPropEl;
}
/**
* Return true if this query includes a Many association.
*/
public boolean isManyIncluded() {
return (manyProperty != null);
}
public BeanProperty[] getEncryptedProps() {
return encryptedProps;
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,107 +1,107 @@
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
/**
* Join to Many (or child of a many) to support where clause predicates on many properties.
*/
public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
private final String parentPrefix;
private final String prefix;
private final BeanPropertyAssoc<?> nodeBeanProp;
/**
* Child joins.
*/
private final SqlTreeNode[] children;
/**
* The many where join which is either INNER or OUTER.
*/
private final SqlJoinType manyJoinType;
public SqlTreeNodeManyWhereJoin(String prefix, BeanPropertyAssoc<?> prop, SqlJoinType manyJoinType) {
this.nodeBeanProp = prop;
this.prefix = prefix;
this.manyJoinType = manyJoinType;
String[] split = SplitName.split(prefix);
this.parentPrefix = split[0];
List<SqlTreeNode> childrenList = new ArrayList<SqlTreeNode>(0);
this.children = childrenList.toArray(new SqlTreeNode[childrenList.size()]);
}
/**
* Append to the FROM clause for this node.
*/
@Override
public void appendFrom(DbSqlContext ctx, SqlJoinType currentJoinType) {
// always use the join type as per this many where join
// (OUTER for disjunction and otherwise INNER)
appendFromBaseTable(ctx, manyJoinType);
for (int i = 0; i < children.length; i++) {
children[i].appendFrom(ctx, manyJoinType);
}
}
/**
* Join to base table for this node. This includes a join to the
* intersection table if this is a ManyToMany node.
*/
public void appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) {
String alias = ctx.getTableAliasManyWhere(prefix);
String parentAlias = ctx.getTableAliasManyWhere(parentPrefix);
if (nodeBeanProp instanceof BeanPropertyAssocOne<?>){
nodeBeanProp.addJoin(joinType, parentAlias, alias, ctx);
} else {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>)nodeBeanProp;
if (!manyProp.isManyToMany()) {
manyProp.addJoin(joinType, parentAlias, alias, ctx);
} else {
String alias2 = alias + "z_";
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
manyToManyJoin.addJoin(joinType, parentAlias, alias2, ctx);
manyProp.addJoin(joinType, alias2, alias, ctx);
}
}
}
public void buildSelectExpressionChain(List<String> selectChain) {
// nothing to add
}
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
// nothing to do here
}
public void appendWhere(DbSqlContext ctx) {
// nothing to do here
}
public void load(DbReadContext ctx, EntityBean parentBean) throws SQLException {
// nothing to do here
}
}
package com.avaje.ebeaninternal.server.query;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
/**
* Join to Many (or child of a many) to support where clause predicates on many properties.
*/
public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
private final String parentPrefix;
private final String prefix;
private final BeanPropertyAssoc<?> nodeBeanProp;
/**
* Child joins.
*/
private final SqlTreeNode[] children;
/**
* The many where join which is either INNER or OUTER.
*/
private final SqlJoinType manyJoinType;
public SqlTreeNodeManyWhereJoin(String prefix, BeanPropertyAssoc<?> prop, SqlJoinType manyJoinType) {
this.nodeBeanProp = prop;
this.prefix = prefix;
this.manyJoinType = manyJoinType;
String[] split = SplitName.split(prefix);
this.parentPrefix = split[0];
List<SqlTreeNode> childrenList = new ArrayList<SqlTreeNode>(0);
this.children = childrenList.toArray(new SqlTreeNode[childrenList.size()]);
}
/**
* Append to the FROM clause for this node.
*/
@Override
public void appendFrom(DbSqlContext ctx, SqlJoinType currentJoinType) {
// always use the join type as per this many where join
// (OUTER for disjunction and otherwise INNER)
appendFromBaseTable(ctx, manyJoinType);
for (int i = 0; i < children.length; i++) {
children[i].appendFrom(ctx, manyJoinType);
}
}
/**
* Join to base table for this node. This includes a join to the
* intersection table if this is a ManyToMany node.
*/
public void appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) {
String alias = ctx.getTableAliasManyWhere(prefix);
String parentAlias = ctx.getTableAliasManyWhere(parentPrefix);
if (nodeBeanProp instanceof BeanPropertyAssocOne<?>){
nodeBeanProp.addJoin(joinType, parentAlias, alias, ctx);
} else {
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>)nodeBeanProp;
if (!manyProp.isManyToMany()) {
manyProp.addJoin(joinType, parentAlias, alias, ctx);
} else {
String alias2 = alias + "z_";
TableJoin manyToManyJoin = manyProp.getIntersectionTableJoin();
manyToManyJoin.addJoin(joinType, parentAlias, alias2, ctx);
manyProp.addJoin(joinType, alias2, alias, ctx);
}
}
}
public void buildSelectExpressionChain(List<String> selectChain) {
// nothing to add
}
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
// nothing to do here
}
public void appendWhere(DbSqlContext ctx) {
// nothing to do here
}
public void load(DbReadContext ctx, EntityBean parentBean) throws SQLException {
// nothing to do here
}
}
@@ -1,99 +1,99 @@
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
/**
* The select properties for a node in the SqlTree.
*/
public class SqlTreeProperties {
private static final TableJoin[] EMPTY_TABLE_JOINS = new TableJoin[0];
/**
* True if this node of the tree should have read only entity beans.
*/
private boolean readOnly;
/**
* set to false if the id field is not included.
*/
private boolean includeId = true;
private TableJoin[] tableJoins = EMPTY_TABLE_JOINS;
/**
* The bean properties in order.
*/
private List<BeanProperty> propsList = new ArrayList<BeanProperty>();
/**
* Maintain a list of property names to detect embedded bean additions.
*/
private LinkedHashSet<String> propNames = new LinkedHashSet<String>();
private boolean allProperties;
public SqlTreeProperties() {
}
public boolean containsProperty(String propName){
return propNames.contains(propName);
}
public void add(BeanProperty[] props) {
for (BeanProperty beanProperty : props) {
propsList.add(beanProperty);
}
}
public void add(BeanProperty prop) {
propsList.add(prop);
propNames.add(prop.getName());
}
public BeanProperty[] getProps() {
return propsList.toArray(new BeanProperty[propsList.size()]);
}
public boolean isIncludeId() {
return includeId;
}
public void setIncludeId(boolean includeId) {
this.includeId = includeId;
}
public boolean isPartialObject() {
return !allProperties;
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public TableJoin[] getTableJoins() {
return tableJoins;
}
public void setTableJoins(TableJoin[] tableJoins) {
this.tableJoins = tableJoins;
}
public void setAllProperties(boolean allProperties) {
this.allProperties = allProperties;
}
public boolean isAllProperties() {
return allProperties;
}
package com.avaje.ebeaninternal.server.query;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.TableJoin;
/**
* The select properties for a node in the SqlTree.
*/
public class SqlTreeProperties {
private static final TableJoin[] EMPTY_TABLE_JOINS = new TableJoin[0];
/**
* True if this node of the tree should have read only entity beans.
*/
private boolean readOnly;
/**
* set to false if the id field is not included.
*/
private boolean includeId = true;
private TableJoin[] tableJoins = EMPTY_TABLE_JOINS;
/**
* The bean properties in order.
*/
private List<BeanProperty> propsList = new ArrayList<BeanProperty>();
/**
* Maintain a list of property names to detect embedded bean additions.
*/
private LinkedHashSet<String> propNames = new LinkedHashSet<String>();
private boolean allProperties;
public SqlTreeProperties() {
}
public boolean containsProperty(String propName){
return propNames.contains(propName);
}
public void add(BeanProperty[] props) {
for (BeanProperty beanProperty : props) {
propsList.add(beanProperty);
}
}
public void add(BeanProperty prop) {
propsList.add(prop);
propNames.add(prop.getName());
}
public BeanProperty[] getProps() {
return propsList.toArray(new BeanProperty[propsList.size()]);
}
public boolean isIncludeId() {
return includeId;
}
public void setIncludeId(boolean includeId) {
this.includeId = includeId;
}
public boolean isPartialObject() {
return !allProperties;
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public TableJoin[] getTableJoins() {
return tableJoins;
}
public void setTableJoins(TableJoin[] tableJoins) {
this.tableJoins = tableJoins;
}
public void setAllProperties(boolean allProperties) {
this.allProperties = allProperties;
}
public boolean isAllProperties() {
return allProperties;
}
}