Fix for #158 - Add support for using setDistinct (by excluding id property from generated sql)

This commit is contained in:
Rob Bygrave
2014-07-07 23:29:48 +12:00
parent af9a01be77
commit 5cc2eff0d4
8 changed files with 133 additions and 24 deletions
@@ -532,12 +532,24 @@ public interface SpiQuery<T> extends Query<T> {
* Return the index of the first row to return in the query.
*/
public int getFirstRow();
/**
* Internally set by Ebean when this query must use the DISTINCT keyword.
* <p>
* This does not exclude/remove the use of the id property.
*/
public Query<T> setSqlDistinct(boolean sqlDistinct);
/**
* return true if this query uses DISTINCT.
* Return true if this query has been specified by a user or internally by Ebean to use DISTINCT.
*/
public boolean isDistinctQuery();
/**
* Return true if this query has been specified by a user to use DISTINCT.
*/
public boolean isDistinct();
/**
* Set default select clauses where none have been explicitly defined.
*/
@@ -1112,7 +1112,7 @@ public final class DefaultServer implements SpiEbeanServer {
// predicates on *ToMany properties
if (query.initManyWhereJoins()) {
// we need a sql distinct now
query.setDistinct(true);
query.setSqlDistinct(true);
}
boolean allowOneManyFetch = true;
@@ -144,7 +144,7 @@ public class CQueryBuilder implements Constants {
String sqlSelect = "select count(*)";
if (hasMany) {
// need to count distinct id's ...
query.setDistinct(true);
query.setSqlDistinct(true);
sqlSelect = null;
}
@@ -320,13 +320,13 @@ public class CQueryBuilder implements Constants {
if (!useSqlLimiter) {
sb.append("select ");
if (query.isDistinct()) {
if (query.isDistinctQuery()) {
sb.append("distinct ");
}
}
sb.append(select.getSelectSql());
if (query.isDistinct() && dbOrderBy != null) {
if (query.isDistinctQuery() && dbOrderBy != null) {
// add the orderby columns to the select clause (due to distinct)
sb.append(", ").append(convertDbOrderByForSelect(dbOrderBy));
}
@@ -258,7 +258,8 @@ public class SqlTreeBuilder {
// Optional many property for lazy loading query
BeanPropertyAssocMany<?> lazyLoadMany = (query == null) ? null : query.getLazyLoadForParentsProperty();
return new SqlTreeNodeRoot(desc, props, myList, !subQuery, includeJoin, lazyLoadMany);
boolean withId = !subQuery && (query == null || !query.isDistinct());
return new SqlTreeNodeRoot(desc, props, myList, withId, includeJoin, lazyLoadMany);
} else if (prop instanceof BeanPropertyAssocMany<?>) {
return new SqlTreeNodeManyRoot(prefix, (BeanPropertyAssocMany<?>) prop, props, myList);
@@ -312,7 +313,7 @@ public class SqlTreeBuilder {
// as we are now going to join to the many then we need
// to add the distinct to the sql query to stop duplicate
// rows...
query.setDistinct(true);
query.setSqlDistinct(true);
}
}
}
@@ -194,7 +194,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
Mode queryMode = ctx.getQueryMode();
PersistenceContext persistenceContext = ctx.getPersistenceContext();
PersistenceContext persistenceContext = !readId ? null : ctx.getPersistenceContext();
Object id = null;
if (!readId) {
@@ -274,8 +274,9 @@ public class SqlTreeNodeBean implements SqlTreeNode {
} else if (localBean != null) {
ctx.setCurrentPrefix(prefix, pathMap);
createListProxies(localDesc, ctx, localBean);
if (readId) {
createListProxies(localDesc, ctx, localBean);
}
localDesc.postLoad(localBean, null);
if (localBean instanceof EntityBean) {
@@ -290,7 +291,10 @@ public class SqlTreeNodeBean implements SqlTreeNode {
}
if (partialObject) {
ctx.register(null, ebi);
if (readId) {
// register for lazy loading
ctx.register(null, ebi);
}
} else {
ebi.setFullyLoadedBean(true);
}
@@ -128,11 +128,16 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
private String lazyLoadProperty;
private String lazyLoadManyPath;
/**
* Set to true by a user wanting a DISTINCT query (id property must be excluded).
*/
private boolean distinct;
/**
* Set to true if you want a DISTINCT query.
* Set to true internally by Ebean when it needs the DISTINCT keyword added to the query (id property still expected).
*/
private boolean distinct;
private boolean sqlDistinct;
/**
* Set to true if this is a future fetch using background threads.
@@ -431,7 +436,8 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
copy.query = query;
copy.additionalWhere = additionalWhere;
copy.additionalHaving = additionalHaving;
copy.distinct = distinct;
copy.distinct = distinct;
copy.sqlDistinct = sqlDistinct;
copy.timeout = timeout;
copy.mapKey = mapKey;
copy.id = id;
@@ -658,7 +664,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
}
builder.add((type == null ? 0 : type.ordinal()+1));
builder.add(autoFetchTuned).add(distinct).add(query);
builder.add(autoFetchTuned).add(distinct).add(sqlDistinct).add(query);
builder.add(firstRow).add(maxRows).add(orderBy).add(forUpdate);
builder.add(rawWhereClause).add(additionalWhere).add(additionalHaving);
builder.add(mapKey);
@@ -1022,19 +1028,33 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return this;
}
/**
* return true if user specified to use SQL DISTINCT (effectively excludes id property).
*/
public boolean isDistinct() {
return distinct;
}
/**
* Internally set to use SQL DISTINCT on the query but still have id property included.
*/
public DefaultOrmQuery<T> setDistinct(boolean distinct) {
this.distinct = distinct;
return this;
}
/**
* return true if this query uses DISTINCT.
* Return true if this query uses SQL DISTINCT either explicitly by the user or internally defined by ebean.
*/
public boolean isDistinct() {
return distinct;
public boolean isDistinctQuery() {
return distinct || sqlDistinct;
}
/**
* Set whether this query uses DISTINCT.
* Internally set to use SQL DISTINCT on the query but still have id property included.
*/
public DefaultOrmQuery<T> setDistinct(boolean isDistinct) {
this.distinct = isDistinct;
public DefaultOrmQuery<T> setSqlDistinct(boolean sqlDistinct) {
this.sqlDistinct = sqlDistinct;
return this;
}
@@ -38,7 +38,7 @@ public class OrmQueryLimitRequest implements SqlLimitRequest {
}
public boolean isDistinct() {
return ormQuery.isDistinct();
return ormQuery.isDistinctQuery();
}
public SpiQuery<?> getOrmQuery() {