mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
No effective change - change newline char
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+231
-231
@@ -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
+107
-107
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user