#125 - ENH: Add support for query returning a List of single Attribute type

This commit is contained in:
Robin Bygrave
2016-08-04 16:41:50 +12:00
parent 9a5cb242ce
commit 1be30f1671
25 changed files with 716 additions and 218 deletions
@@ -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&lt;Customer&gt; 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.
*/
+32
View File
@@ -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);