mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#125 - ENH: Add support for query returning a List of single Attribute type
This commit is contained in:
@@ -988,6 +988,40 @@ public interface EbeanServer {
|
||||
*/
|
||||
<T> Map<?, T> findMap(Query<T> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning a list of values for a single property.
|
||||
*
|
||||
* <h3>Example 1:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* .select("name")
|
||||
* .orderBy().asc("name")
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Example 2:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name")
|
||||
* .where().eq("status", Customer.Status.NEW)
|
||||
* .orderBy().asc("name")
|
||||
* .setMaxRows(100)
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return the list of values for the selected property
|
||||
*
|
||||
* @see Query#findSingleAttributeList()
|
||||
*/
|
||||
<A> List<A> findSingleAttributeList(Query<?> query, Transaction transaction);
|
||||
|
||||
/**
|
||||
* Execute the query returning at most one entity bean or null (if no matching
|
||||
* bean is found).
|
||||
|
||||
@@ -31,7 +31,7 @@ import java.util.Set;
|
||||
* more methods than you would initially expect (the ones duplicated from
|
||||
* Query).
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @see Query#where()
|
||||
*/
|
||||
public interface ExpressionList<T> {
|
||||
@@ -83,14 +83,14 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Add an orderBy clause to the query.
|
||||
*
|
||||
*
|
||||
* @see Query#orderBy(String)
|
||||
*/
|
||||
Query<T> orderBy(String orderBy);
|
||||
|
||||
/**
|
||||
* Add an orderBy clause to the query.
|
||||
*
|
||||
*
|
||||
* @see Query#orderBy(String)
|
||||
*/
|
||||
Query<T> setOrderBy(String orderBy);
|
||||
@@ -164,14 +164,14 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Execute the query returning a list.
|
||||
*
|
||||
*
|
||||
* @see Query#findList()
|
||||
*/
|
||||
List<T> findList();
|
||||
|
||||
/**
|
||||
* Execute the query returning the list of Id's.
|
||||
*
|
||||
*
|
||||
* @see Query#findIds()
|
||||
*/
|
||||
List<Object> findIds();
|
||||
@@ -193,14 +193,14 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Execute the query returning a set.
|
||||
*
|
||||
*
|
||||
* @see Query#findSet()
|
||||
*/
|
||||
Set<T> findSet();
|
||||
|
||||
/**
|
||||
* Execute the query returning a map.
|
||||
*
|
||||
*
|
||||
* @see Query#findMap()
|
||||
*/
|
||||
Map<?, T> findMap();
|
||||
@@ -210,6 +210,38 @@ public interface ExpressionList<T> {
|
||||
*/
|
||||
<K> Map<K, T> findMap(String keyProperty, Class<K> keyType);
|
||||
|
||||
/**
|
||||
* Execute the query returning a list of values for a single property.
|
||||
*
|
||||
* <h3>Example 1:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* .select("name")
|
||||
* .orderBy().asc("name")
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Example 2:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name")
|
||||
* .where().eq("status", Customer.Status.NEW)
|
||||
* .orderBy().asc("name")
|
||||
* .setMaxRows(100)
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return the list of values for the selected property
|
||||
*/
|
||||
<A> List<A> findSingleAttributeList();
|
||||
|
||||
/**
|
||||
* Execute the query returning a single bean or null (if no matching
|
||||
* bean is found).
|
||||
@@ -232,7 +264,7 @@ public interface ExpressionList<T> {
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a Future object for the row count query
|
||||
*/
|
||||
FutureRowCount<T> findFutureCount();
|
||||
@@ -251,7 +283,7 @@ public interface ExpressionList<T> {
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a Future object for the list of Id's
|
||||
*/
|
||||
FutureIds<T> findFutureIds();
|
||||
@@ -263,7 +295,7 @@ public interface ExpressionList<T> {
|
||||
* execution status (isDone etc) and get the value (with or without a
|
||||
* timeout).
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @return a Future object for the list result of the query
|
||||
*/
|
||||
FutureList<T> findFutureList();
|
||||
@@ -326,7 +358,7 @@ public interface ExpressionList<T> {
|
||||
/**
|
||||
* Specify specific properties to fetch on the main/root bean (aka partial
|
||||
* object).
|
||||
*
|
||||
*
|
||||
* @see Query#select(String)
|
||||
*/
|
||||
Query<T> select(String properties);
|
||||
@@ -351,28 +383,28 @@ public interface ExpressionList<T> {
|
||||
|
||||
/**
|
||||
* Set the first row to fetch.
|
||||
*
|
||||
*
|
||||
* @see Query#setFirstRow(int)
|
||||
*/
|
||||
Query<T> setFirstRow(int firstRow);
|
||||
|
||||
/**
|
||||
* Set the maximum number of rows to fetch.
|
||||
*
|
||||
*
|
||||
* @see Query#setMaxRows(int)
|
||||
*/
|
||||
Query<T> setMaxRows(int maxRows);
|
||||
|
||||
/**
|
||||
* Set the name of the property which values become the key of a map.
|
||||
*
|
||||
*
|
||||
* @see Query#setMapKey(String)
|
||||
*/
|
||||
Query<T> setMapKey(String mapKey);
|
||||
|
||||
/**
|
||||
* Set to true to use the query for executing this query.
|
||||
*
|
||||
*
|
||||
* @see Query#setUseCache(boolean)
|
||||
*/
|
||||
Query<T> setUseCache(boolean useCache);
|
||||
@@ -633,7 +665,7 @@ public interface ExpressionList<T> {
|
||||
* To get control over the options you can create an ExampleExpression and set
|
||||
* those options such as case insensitive etc.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* // create an example bean and set the properties
|
||||
@@ -641,26 +673,26 @@ public interface ExpressionList<T> {
|
||||
* Customer example = new Customer();
|
||||
* example.setName("Rob%");
|
||||
* example.setNotes("%something%");
|
||||
*
|
||||
*
|
||||
* List<Customer> list = Ebean.find(Customer.class).where()
|
||||
* // pass the bean into the where() clause
|
||||
* .exampleLike(example)
|
||||
* // you can add other expressions to the same query
|
||||
* .gt("id", 2).findList();
|
||||
*
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
*
|
||||
* Similarly you can create an ExampleExpression
|
||||
*
|
||||
*
|
||||
* <pre>{@code
|
||||
*
|
||||
* Customer example = new Customer();
|
||||
* example.setName("Rob%");
|
||||
* example.setNotes("%something%");
|
||||
*
|
||||
*
|
||||
* // create a ExampleExpression with more control
|
||||
* ExampleExpression qbe = new ExampleExpression(example, true, LikeType.EQUAL_TO).includeZeros();
|
||||
*
|
||||
*
|
||||
* List<Customer> list = Ebean.find(Customer.class).where().add(qbe).findList();
|
||||
*
|
||||
* }</pre>
|
||||
@@ -762,7 +794,7 @@ public interface ExpressionList<T> {
|
||||
* Exists expression
|
||||
*/
|
||||
ExpressionList<T> exists(Query<?> subQuery);
|
||||
|
||||
|
||||
/**
|
||||
* Not exists expression
|
||||
*/
|
||||
@@ -789,7 +821,7 @@ public interface ExpressionList<T> {
|
||||
* Expression where all the property names in the map are equal to the
|
||||
* corresponding value.
|
||||
* </p>
|
||||
*
|
||||
*
|
||||
* @param propertyMap
|
||||
* a map keyed by property names.
|
||||
*/
|
||||
|
||||
@@ -677,6 +677,38 @@ public interface Query<T> {
|
||||
*/
|
||||
<K> Map<K, T> findMap(String keyProperty, Class<K> keyType);
|
||||
|
||||
/**
|
||||
* Execute the query returning a list of values for a single property.
|
||||
*
|
||||
* <h3>Example 1:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* .select("name")
|
||||
* .orderBy().asc("name")
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Example 2:</h3>
|
||||
* <pre>{@code
|
||||
*
|
||||
* List<String> names =
|
||||
* Ebean.find(Customer.class)
|
||||
* .setDistinct(true)
|
||||
* .select("name")
|
||||
* .where().eq("status", Customer.Status.NEW)
|
||||
* .orderBy().asc("name")
|
||||
* .setMaxRows(100)
|
||||
* .findSingleAttributeList();
|
||||
*
|
||||
* }</pre>
|
||||
*
|
||||
* @return the list of values for the selected property
|
||||
*/
|
||||
<A> List<A> findSingleAttributeList();
|
||||
|
||||
/**
|
||||
* Execute the query returning either a single bean or null (if no matching
|
||||
* bean is found).
|
||||
|
||||
@@ -78,6 +78,11 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
ID_LIST,
|
||||
|
||||
/**
|
||||
* Find single attribute.
|
||||
*/
|
||||
ATTRIBUTE,
|
||||
|
||||
/**
|
||||
* Find rowCount.
|
||||
*/
|
||||
@@ -341,6 +346,24 @@ public interface SpiQuery<T> extends Query<T> {
|
||||
*/
|
||||
void setSelectId();
|
||||
|
||||
/**
|
||||
* Mark the query as selecting a single attribute.
|
||||
*/
|
||||
void setSingleAttribute();
|
||||
|
||||
/**
|
||||
* Return true if this is singleAttribute query.
|
||||
*/
|
||||
boolean isSingleAttribute();
|
||||
|
||||
/**
|
||||
* Return true if the query should include the Id property.
|
||||
* <p>
|
||||
* distinct and single attribute queries exclude the Id property.
|
||||
* </p>
|
||||
*/
|
||||
boolean isWithId();
|
||||
|
||||
/**
|
||||
* Set a filter to a join path.
|
||||
*/
|
||||
|
||||
@@ -1152,6 +1152,23 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> List<A> findSingleAttributeList(Query<?> query, Transaction t) {
|
||||
|
||||
SpiOrmQueryRequest request = createQueryRequest(Type.ATTRIBUTE, query, t);
|
||||
Object result = request.getFromQueryCache();
|
||||
if (result != null) {
|
||||
return (List<A>) result;
|
||||
}
|
||||
try {
|
||||
request.initTransIfRequired();
|
||||
return (List<A>) request.findSingleAttributeList();
|
||||
|
||||
} finally {
|
||||
request.endTransIfRequired();
|
||||
}
|
||||
}
|
||||
|
||||
public <T> int findCount(Query<T> query, Transaction t) {
|
||||
|
||||
SpiQuery<T> copy = ((SpiQuery<T>) query).copy();
|
||||
|
||||
@@ -21,6 +21,14 @@ public interface OrmQueryEngine {
|
||||
*/
|
||||
<T> BeanCollection<T> findMany(OrmQueryRequest<T> request);
|
||||
|
||||
/**
|
||||
* Execute the findSingleAttributeList query.
|
||||
*/
|
||||
<A> List<A> findSingleAttributeList(OrmQueryRequest<?> request);
|
||||
|
||||
/**
|
||||
* Execute the findVersions query.
|
||||
*/
|
||||
<T> List<Version<T>> findVersions(OrmQueryRequest<T> request);
|
||||
|
||||
/**
|
||||
|
||||
@@ -373,6 +373,14 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
|
||||
return (Map<?, ?>) queryEngine.findMany(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the findSingleAttributeList query.
|
||||
*/
|
||||
@Override
|
||||
public <A> List<A> findSingleAttributeList() {
|
||||
return queryEngine.findSingleAttributeList(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a bean specific finder if one has been set.
|
||||
*/
|
||||
|
||||
@@ -107,6 +107,11 @@ public interface SpiOrmQueryRequest<T> extends DocQueryRequest<T> {
|
||||
*/
|
||||
Map<?, ?> findMap();
|
||||
|
||||
/**
|
||||
* Execute the findSingleAttributeList query.
|
||||
*/
|
||||
<A> List<A> findSingleAttributeList();
|
||||
|
||||
/**
|
||||
* Try to get the query result from the query cache.
|
||||
*/
|
||||
|
||||
@@ -388,6 +388,11 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return query.findMap(keyProperty, keyType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> List<A> findSingleAttributeList() {
|
||||
return query.findSingleAttributeList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public T findUnique() {
|
||||
return query.findUnique();
|
||||
|
||||
@@ -400,6 +400,11 @@ class JunctionExpression<T> implements SpiJunction<T>, SpiExpression, Expression
|
||||
return exprList.findMap(keyProperty, keyType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> List<A> findSingleAttributeList() {
|
||||
return exprList.findSingleAttributeList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PagedList<T> findPagedList() {
|
||||
return exprList.findPagedList();
|
||||
|
||||
@@ -165,13 +165,35 @@ public class CQueryBuilder {
|
||||
return StringHelper.replaceString(sql, "${RTA}", replaceWith);
|
||||
}
|
||||
|
||||
public CQueryFetchSingleAttribute buildFetchAttributeQuery(OrmQueryRequest<?> request) {
|
||||
|
||||
SpiQuery<?> query = request.getQuery();
|
||||
query.setSingleAttribute();
|
||||
|
||||
CQueryPredicates predicates = new CQueryPredicates(binder, request);
|
||||
CQueryPlan queryPlan = request.getQueryPlan();
|
||||
if (queryPlan != null) {
|
||||
predicates.prepare(false);
|
||||
return new CQueryFetchSingleAttribute(request, predicates, queryPlan);
|
||||
}
|
||||
|
||||
// use RawSql or generated Sql
|
||||
predicates.prepare(true);
|
||||
|
||||
SqlTree sqlTree = createSqlTree(request, predicates, getHistorySupport(query), getDraftSupport(query));
|
||||
SqlLimitResponse s = buildSql(null, request, predicates, sqlTree);
|
||||
|
||||
queryPlan = new CQueryPlan(request, s.getSql(), sqlTree, false, s.isIncludesRowNumberColumn(), predicates.getLogWhereSql());
|
||||
request.putQueryPlan(queryPlan);
|
||||
return new CQueryFetchSingleAttribute(request, predicates, queryPlan);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
@@ -179,8 +201,7 @@ public class CQueryBuilder {
|
||||
if (queryPlan != null) {
|
||||
// skip building the SqlTree and Sql string
|
||||
predicates.prepare(false);
|
||||
String sql = queryPlan.getSql();
|
||||
return new CQueryFetchIds(request, predicates, sql);
|
||||
return new CQueryFetchIds(request, predicates, queryPlan.getSql());
|
||||
}
|
||||
|
||||
// use RawSql or generated Sql
|
||||
@@ -450,8 +471,8 @@ public class CQueryBuilder {
|
||||
}
|
||||
|
||||
sb.append(select.getSelectSql());
|
||||
if (query.isDistinctQuery() && dbOrderBy != null) {
|
||||
// add the orderby columns to the select clause (due to distinct)
|
||||
if (query.isDistinctQuery() && dbOrderBy != null && !query.isSingleAttribute()) {
|
||||
// add the orderBy columns to the select clause (due to distinct)
|
||||
sb.append(", ").append(convertDbOrderByForSelect(dbOrderBy));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,27 @@ public class CQueryEngine {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and execute the findSingleAttributeList query.
|
||||
*/
|
||||
public <A> List<A> findSingleAttributeList(OrmQueryRequest<?> request) {
|
||||
|
||||
CQueryFetchSingleAttribute rcQuery = queryBuilder.buildFetchAttributeQuery(request);
|
||||
try {
|
||||
List list = rcQuery.findList();
|
||||
if (request.isLogSql()) {
|
||||
logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog());
|
||||
}
|
||||
if (request.isLogSummary()) {
|
||||
request.getTransaction().logSummary(rcQuery.getSummary());
|
||||
}
|
||||
return list;
|
||||
|
||||
} catch (SQLException e) {
|
||||
throw CQuery.createPersistenceException(e, request.getTransaction(), rcQuery.getBindLog(), rcQuery.getGeneratedSql());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build and execute the find Id's query.
|
||||
*/
|
||||
@@ -97,15 +118,12 @@ public class CQueryEngine {
|
||||
try {
|
||||
|
||||
BeanIdList list = rcQuery.findIds();
|
||||
|
||||
if (request.isLogSql()) {
|
||||
logGeneratedSql(request, rcQuery.getGeneratedSql(), rcQuery.getBindLog());
|
||||
}
|
||||
|
||||
if (request.isLogSummary()) {
|
||||
request.getTransaction().logSummary(rcQuery.getSummary());
|
||||
}
|
||||
|
||||
if (request.getQuery().isFutureFetch()) {
|
||||
// end the transaction for futureFindIds (it had it's own one)
|
||||
logger.debug("Future findIds completed!");
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
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.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.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
|
||||
import com.avaje.ebeaninternal.server.type.DataReader;
|
||||
import com.avaje.ebeaninternal.server.type.RsetDataReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base compiled query request for single attribute queries.
|
||||
*/
|
||||
public abstract class CQueryFetchBase {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CQueryFetchBase.class);
|
||||
|
||||
/**
|
||||
* The overall find request wrapper object.
|
||||
*/
|
||||
protected final OrmQueryRequest<?> request;
|
||||
|
||||
protected final BeanDescriptor<?> desc;
|
||||
|
||||
protected final SpiQuery<?> query;
|
||||
|
||||
/**
|
||||
* Where clause predicates.
|
||||
*/
|
||||
protected final CQueryPredicates predicates;
|
||||
|
||||
/**
|
||||
* The final sql that is generated.
|
||||
*/
|
||||
protected final String sql;
|
||||
|
||||
protected RsetDataReader dataReader;
|
||||
|
||||
/**
|
||||
* The statement used to create the resultSet.
|
||||
*/
|
||||
protected PreparedStatement pstmt;
|
||||
|
||||
protected String bindLog;
|
||||
|
||||
protected int executionTimeMicros;
|
||||
|
||||
protected int rowCount;
|
||||
|
||||
protected final int maxRows;
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
*/
|
||||
public CQueryFetchBase(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 the bind log.
|
||||
*/
|
||||
public String getBindLog() {
|
||||
return bindLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the generated sql.
|
||||
*/
|
||||
public String getGeneratedSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
protected ResultSet prepareExecute() throws SQLException {
|
||||
|
||||
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(pstmt, conn);
|
||||
|
||||
ResultSet rset = pstmt.executeQuery();
|
||||
dataReader = new RsetDataReader(request.getDataTimeZone(), rset);
|
||||
return rset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the resources.
|
||||
* <p>
|
||||
* The jdbc resultSet and statement need to be closed. Its important that
|
||||
* this method is called.
|
||||
* </p>
|
||||
*/
|
||||
protected void close() {
|
||||
try {
|
||||
if (dataReader != null) {
|
||||
dataReader.close();
|
||||
dataReader = null;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error closing DataReader", e);
|
||||
}
|
||||
try {
|
||||
if (pstmt != null) {
|
||||
pstmt.close();
|
||||
pstmt = null;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error closing PreparedStatement", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisableLazyLoading() {
|
||||
return 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 isAutoTuneProfiling() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void profileBean(EntityBeanIntercept ebi, String prefix) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void setCurrentPrefix(String currentPrefix, Map<String, String> pathMap) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void setLazyLoadedChildBean(EntityBean loadedBean, Object lazyLoadParentId) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDraftQuery() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,86 +1,25 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
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.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
|
||||
import com.avaje.ebeaninternal.server.type.DataReader;
|
||||
import com.avaje.ebeaninternal.server.type.RsetDataReader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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 int executionTimeMicros;
|
||||
|
||||
private int rowCount;
|
||||
|
||||
private final int maxRows;
|
||||
public class CQueryFetchIds extends CQueryFetchBase {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
super(request, predicates, sql);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,20 +36,6 @@ public class CQueryFetchIds {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bind log.
|
||||
*/
|
||||
public String getBindLog() {
|
||||
return bindLog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the generated sql.
|
||||
*/
|
||||
public String getGeneratedSql() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query returning the row count.
|
||||
*/
|
||||
@@ -131,22 +56,7 @@ public class CQueryFetchIds {
|
||||
|
||||
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(pstmt, conn);
|
||||
|
||||
ResultSet rset = pstmt.executeQuery();
|
||||
dataReader = new RsetDataReader(request.getDataTimeZone(), rset);
|
||||
ResultSet rset = prepareExecute();
|
||||
|
||||
boolean hitMaxRows = false;
|
||||
boolean hasMoreRows = false;
|
||||
@@ -183,96 +93,4 @@ public class CQueryFetchIds {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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("Error closing DataReader", e);
|
||||
}
|
||||
try {
|
||||
if (pstmt != null) {
|
||||
pstmt.close();
|
||||
pstmt = null;
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
logger.error("Error closing PreparedStatement", 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDisableLazyLoading() {
|
||||
return 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 isAutoTuneProfiling() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void profileBean(EntityBeanIntercept ebi, String prefix) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void setCurrentPrefix(String currentPrefix, Map<String, String> pathMap) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
public void setLazyLoadedChildBean(EntityBean loadedBean, Object lazyLoadParentId) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDraftQuery() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.avaje.ebeaninternal.server.query;
|
||||
|
||||
import com.avaje.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Executes the select row count query.
|
||||
*/
|
||||
public class CQueryFetchSingleAttribute extends CQueryFetchBase {
|
||||
|
||||
private final BeanProperty property;
|
||||
|
||||
private final ScalarType<Object> scalarType;
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
*/
|
||||
public CQueryFetchSingleAttribute(OrmQueryRequest<?> request, CQueryPredicates predicates, CQueryPlan plan) {
|
||||
super(request, predicates, plan.getSql());
|
||||
this.property = plan.getSingleProperty();
|
||||
this.scalarType = property.getScalarType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a summary description of this query.
|
||||
*/
|
||||
public String getSummary() {
|
||||
StringBuilder sb = new StringBuilder(80);
|
||||
sb.append("FindAttr 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query returning the row count.
|
||||
*/
|
||||
public List<Object> findList() throws SQLException {
|
||||
|
||||
long startNano = System.nanoTime();
|
||||
try {
|
||||
|
||||
List<Object> result = new ArrayList<Object>();
|
||||
|
||||
ResultSet rset = prepareExecute();
|
||||
while (rset.next()) {
|
||||
result.add(scalarType.read(dataReader));
|
||||
dataReader.resetColumnPosition();
|
||||
rowCount++;
|
||||
}
|
||||
|
||||
long exeNano = System.nanoTime() - startNano;
|
||||
executionTimeMicros = (int) exeNano / 1000;
|
||||
|
||||
return result;
|
||||
|
||||
} finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -266,4 +266,7 @@ public class CQueryPlan {
|
||||
return stats.getLastQueryTime();
|
||||
}
|
||||
|
||||
public BeanProperty getSingleProperty() {
|
||||
return sqlTree.getRootNode().getSingleProperty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,12 @@ public class DefaultOrmQueryEngine implements OrmQueryEngine {
|
||||
return queryEngine.findIds(request);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> List<A> findSingleAttributeList(OrmQueryRequest<?> request) {
|
||||
flushJdbcBatchOnQuery(request);
|
||||
return queryEngine.findSingleAttributeList(request);
|
||||
}
|
||||
|
||||
public <T> QueryIterator<T> findIterate(OrmQueryRequest<T> request) {
|
||||
|
||||
// LIMITATION: You can not use QueryIterator to load bean cache
|
||||
|
||||
@@ -260,7 +260,7 @@ public class SqlTreeBuilder {
|
||||
|
||||
// Optional many property for lazy loading query
|
||||
BeanPropertyAssocMany<?> lazyLoadMany = (query == null) ? null : query.getLazyLoadMany();
|
||||
boolean withId = !rawNoId && !subQuery && (query == null || !query.isDistinct());
|
||||
boolean withId = !rawNoId && !subQuery && (query == null || query.isWithId());
|
||||
return new SqlTreeNodeRoot(desc, props, myList, withId, includeJoin, lazyLoadMany, SpiQuery.TemporalMode.of(query), disableLazyLoad);
|
||||
|
||||
} else if (prop instanceof BeanPropertyAssocMany<?>) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.query;
|
||||
import com.avaje.ebean.Version;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
|
||||
import com.avaje.ebeaninternal.server.deploy.DbSqlContext;
|
||||
|
||||
@@ -62,4 +63,10 @@ public interface SqlTreeNode {
|
||||
* Return true if the query has a many join.
|
||||
*/
|
||||
boolean hasMany();
|
||||
|
||||
/**
|
||||
* Return the property for singleAttribute query.
|
||||
*/
|
||||
BeanProperty getSingleProperty();
|
||||
|
||||
}
|
||||
|
||||
@@ -137,6 +137,11 @@ public class SqlTreeNodeBean implements SqlTreeNode {
|
||||
pathMap = createPathMap(prefix, desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanProperty getSingleProperty() {
|
||||
return properties[0];
|
||||
}
|
||||
|
||||
private Map<String, String> createPathMap(String prefix, BeanDescriptor<?> desc) {
|
||||
|
||||
BeanPropertyAssocMany<?>[] manys = desc.propertiesMany();
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.query;
|
||||
import com.avaje.ebean.Version;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.DbReadContext;
|
||||
@@ -55,6 +56,11 @@ public class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
// nothing to do here
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanProperty getSingleProperty() {
|
||||
throw new IllegalStateException("No expected");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the extra join is a many join.
|
||||
* <p>
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.avaje.ebeaninternal.server.query;
|
||||
import com.avaje.ebean.Version;
|
||||
import com.avaje.ebean.bean.EntityBean;
|
||||
import com.avaje.ebeaninternal.api.SpiQuery;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
@@ -39,6 +40,11 @@ public class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
this.parentPrefix = split[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanProperty getSingleProperty() {
|
||||
throw new IllegalStateException("No expected");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAsOfTableAlias(SpiQuery<?> query) {
|
||||
// do nothing here ...
|
||||
|
||||
@@ -192,6 +192,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
*/
|
||||
private boolean forUpdate;
|
||||
|
||||
private boolean singleAttribute;
|
||||
|
||||
/**
|
||||
* Set to true if this query has been tuned by autoTune.
|
||||
*/
|
||||
@@ -534,6 +536,26 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
select(beanDescriptor.getIdBinder().getIdProperty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSingleAttribute() {
|
||||
this.singleAttribute = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a single attribute query.
|
||||
*/
|
||||
public boolean isSingleAttribute() {
|
||||
return singleAttribute;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the Id should be included in the query.
|
||||
*/
|
||||
@Override
|
||||
public boolean isWithId() {
|
||||
return !distinct && !singleAttribute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NaturalKeyBindParam getNaturalKeyBindParam() {
|
||||
NaturalKeyBindParam namedBind = null;
|
||||
@@ -1133,6 +1155,11 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
|
||||
return (Map<K, T>) findMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <A> List<A> findSingleAttributeList() {
|
||||
return (List<A>)server.findSingleAttributeList(this, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T findUnique() {
|
||||
return server.findUnique(this, null);
|
||||
|
||||
Reference in New Issue
Block a user