New deal: every time you call a builder method, a copy of the Query is created,

so that partially constructed queries can be reused without any special care
being taken by the caller. I also switched to the use of a functional list
internally to make this whole process more pleasant and efficient.
This commit is contained in:
Michael Bayne
2010-12-18 17:32:31 +00:00
parent 7d99928d7c
commit 5809a5e8c6
+86 -60
View File
@@ -38,52 +38,61 @@ import static com.google.common.base.Preconditions.checkState;
/** /**
* The root of a fluent mechanism for constructing queries. Obtain an instance via {@link * The root of a fluent mechanism for constructing queries. Obtain an instance via {@link
* DepotRepository#from}. * DepotRepository#from}. For example:
* <pre>{@code
* List<Tuple2<Integer,String>> results = _repo.from(FooRecord.class).
* where(FooRecord.NAME.like("%foo%")).
* descending(FooRecord.NAME).
* limit(25).
* select(FooRecord.ID, FooRecord.NAME);
* }</pre>
*
* <p> Note that each builder method returns a new builder instance so that partial queries can be
* constructed and reused without interfering with one another:
* <pre>{@code
* Query<FooRecord> query = from(FooRecord.class).where(FooRecord.NAME.eq("foo")).limit(10);
* List<Tuple2<Integer,String>> top = query.ascending().select(FooRecord.ID, FooRecord.NAME);
* List<Tuple2<Integer,String>> bot = query.descending().select(FooRecord.ID, FooRecord.NAME);
* }</pre>
*/ */
public class Query<T extends PersistentRecord> public class Query<T extends PersistentRecord>
implements Cloneable implements Cloneable
{ {
public Query (PersistenceContext ctx, DepotRepository repo, Class<T> pclass)
{
_ctx = ctx;
_repo = repo;
_pclass = pclass;
}
/** Disables the use of the cache for this query. */ /** Disables the use of the cache for this query. */
public Query<T> noCache () { public Query<T> noCache () {
return cache(DepotRepository.CacheStrategy.BEST); return cache(DepotRepository.CacheStrategy.BEST);
} }
/** Configures the use of {@link CacheStrategy#BEST} for this query. */ /** Configures the use of {@link DepotRepository.CacheStrategy#BEST} for this query. */
public Query<T> cacheBest () { public Query<T> cacheBest () {
return cache(DepotRepository.CacheStrategy.BEST); return cache(DepotRepository.CacheStrategy.BEST);
} }
/** Configures the use of {@link CacheStrategy#RECORDS} for this query. */ /** Configures the use of {@link DepotRepository.CacheStrategy#RECORDS} for this query. */
public Query<T> cacheRecords () { public Query<T> cacheRecords () {
return cache(DepotRepository.CacheStrategy.RECORDS); return cache(DepotRepository.CacheStrategy.RECORDS);
} }
/** Configures the use of {@link CacheStrategy#SHORT_KEYS} for this query. */ /** Configures the use of {@link DepotRepository.CacheStrategy#SHORT_KEYS} for this query. */
public Query<T> cacheShortKeys () { public Query<T> cacheShortKeys () {
return cache(DepotRepository.CacheStrategy.SHORT_KEYS); return cache(DepotRepository.CacheStrategy.SHORT_KEYS);
} }
/** Configures the use of {@link CacheStrategy#LONG_KEYS} for this query. */ /** Configures the use of {@link DepotRepository.CacheStrategy#LONG_KEYS} for this query. */
public Query<T> cacheLongKeys () { public Query<T> cacheLongKeys () {
return cache(DepotRepository.CacheStrategy.LONG_KEYS); return cache(DepotRepository.CacheStrategy.LONG_KEYS);
} }
/** Configures the use of {@link CacheStrategy#CONTENTS} for this query. */ /** Configures the use of {@link DepotRepository.CacheStrategy#CONTENTS} for this query. */
public Query<T> cacheContents () { public Query<T> cacheContents () {
return cache(DepotRepository.CacheStrategy.CONTENTS); return cache(DepotRepository.CacheStrategy.CONTENTS);
} }
/** Configures the specified caching policy this query. */ /** Configures the specified caching policy this query. */
public Query<T> cache (DepotRepository.CacheStrategy cache) { public Query<T> cache (DepotRepository.CacheStrategy cache) {
_cache = cache; Query<T> query = clone();
return this; query._cache = cache;
return query;
} }
/** /**
@@ -141,8 +150,9 @@ public class Query<T extends PersistentRecord>
public Query<T> where (WhereClause where) public Query<T> where (WhereClause where)
{ {
checkState(_where == null, "Where clause is already configured."); checkState(_where == null, "Where clause is already configured.");
_where = where; Query<T> query = clone();
return this; query._where = where;
return query;
} }
/** /**
@@ -177,11 +187,9 @@ public class Query<T extends PersistentRecord>
*/ */
public Query<T> join (Join join) public Query<T> join (Join join)
{ {
if (_joins == null) { Query<T> query = clone();
_joins = Lists.newArrayList(); query._joins = cons(join, query._joins);
} return query;
_joins.add(join);
return this;
} }
/** /**
@@ -190,8 +198,9 @@ public class Query<T extends PersistentRecord>
public Query<T> groupBy (SQLExpression<?>... exprs) public Query<T> groupBy (SQLExpression<?>... exprs)
{ {
checkState(_groupBy == null, "GroupBy clause is already configured."); checkState(_groupBy == null, "GroupBy clause is already configured.");
_groupBy = new GroupBy(exprs); Query<T> query = clone();
return this; query._groupBy = new GroupBy(exprs);
return query;
} }
/** /**
@@ -224,8 +233,9 @@ public class Query<T extends PersistentRecord>
public Query<T> orderBy (OrderBy orderBy) public Query<T> orderBy (OrderBy orderBy)
{ {
checkState(_orderBy == null, "OrderBy clause is already configured."); checkState(_orderBy == null, "OrderBy clause is already configured.");
_orderBy = orderBy; Query<T> query = clone();
return this; query._orderBy = orderBy;
return query;
} }
/** /**
@@ -234,8 +244,9 @@ public class Query<T extends PersistentRecord>
public Query<T> limit (int count) public Query<T> limit (int count)
{ {
checkState(_limit == null, "Limit clause is already configured."); checkState(_limit == null, "Limit clause is already configured.");
_limit = new Limit(0, count); Query<T> query = clone();
return this; query._limit = new Limit(0, count);
return query;
} }
/** /**
@@ -244,8 +255,9 @@ public class Query<T extends PersistentRecord>
public Query<T> limit (int offset, int count) public Query<T> limit (int offset, int count)
{ {
checkState(_limit == null, "Limit clause is already configured."); checkState(_limit == null, "Limit clause is already configured.");
_limit = new Limit(offset, count); Query<T> query = clone();
return this; query._limit = new Limit(offset, count);
return query;
} }
/** /**
@@ -271,8 +283,9 @@ public class Query<T extends PersistentRecord>
public Query<T> override (FromOverride fromOverride) public Query<T> override (FromOverride fromOverride)
{ {
checkState(_fromOverride == null, "FromOverride clause is already configured."); checkState(_fromOverride == null, "FromOverride clause is already configured.");
_fromOverride = fromOverride; Query<T> query = clone();
return this; query._fromOverride = fromOverride;
return query;
} }
/** /**
@@ -304,11 +317,9 @@ public class Query<T extends PersistentRecord>
*/ */
public Query<T> fieldDef (FieldDefinition fieldDef) public Query<T> fieldDef (FieldDefinition fieldDef)
{ {
if (_fieldDefs == null) { Query<T> query = clone();
_fieldDefs = Lists.newArrayList(); query._fieldDefs = cons(fieldDef, query._fieldDefs);
} return query;
_fieldDefs.add(fieldDef);
return this;
} }
/** /**
@@ -317,8 +328,9 @@ public class Query<T extends PersistentRecord>
public Query<T> forUpdate () public Query<T> forUpdate ()
{ {
checkState(_forUpdate == null, "ForUpdate clause is already configured."); checkState(_forUpdate == null, "ForUpdate clause is already configured.");
_forUpdate = new ForUpdate(); Query<T> query = clone();
return this; query._forUpdate = new ForUpdate();
return query;
} }
/** /**
@@ -554,21 +566,17 @@ public class Query<T extends PersistentRecord>
return _repo.deleteAll(_pclass, _where, invalidator); return _repo.deleteAll(_pclass, _where, invalidator);
} }
/** protected Query (PersistenceContext ctx, DepotRepository repo, Class<T> pclass)
* Returns a clone of this query builder, including all partially configured state. Useful for {
* constructing partially configured queries and then executing variants. _ctx = ctx;
*/ _repo = repo;
public Query<T> clone () _pclass = pclass;
}
protected Query<T> clone ()
{ {
try { try {
@SuppressWarnings("unchecked") Query<T> qb = (Query<T>)super.clone(); @SuppressWarnings("unchecked") Query<T> qb = (Query<T>)super.clone();
// deep copy the list fields, if we have any
if (qb._joins != null) {
qb._joins = Lists.newArrayList(qb._joins);
}
if (qb._fieldDefs != null) {
qb._fieldDefs = Lists.newArrayList(qb._fieldDefs);
}
return qb; return qb;
} catch (Throwable t) { } catch (Throwable t) {
throw new AssertionError(t); throw new AssertionError(t);
@@ -579,16 +587,12 @@ public class Query<T extends PersistentRecord>
{ {
List<QueryClause> clauses = Lists.newArrayList(); List<QueryClause> clauses = Lists.newArrayList();
addIfNotNull(clauses, _where); addIfNotNull(clauses, _where);
if (_joins != null) { addAll(clauses, _joins);
clauses.addAll(_joins);
}
addIfNotNull(clauses, _orderBy); addIfNotNull(clauses, _orderBy);
addIfNotNull(clauses, _groupBy); addIfNotNull(clauses, _groupBy);
addIfNotNull(clauses, _limit); addIfNotNull(clauses, _limit);
addIfNotNull(clauses, _fromOverride); addIfNotNull(clauses, _fromOverride);
if (_fieldDefs != null) { addAll(clauses, _fieldDefs);
clauses.addAll(_fieldDefs);
}
addIfNotNull(clauses, _forUpdate); addIfNotNull(clauses, _forUpdate);
return clauses; return clauses;
} }
@@ -623,6 +627,29 @@ public class Query<T extends PersistentRecord>
return selections.isEmpty() ? null : selections.get(0); return selections.isEmpty() ? null : selections.get(0);
} }
protected static final class Cons<T>
{
public T head;
public Cons<T> tail;
public Cons (T head, Cons<T> tail) {
this.head = head;
this.tail = tail;
}
}
protected static <T> Cons<T> cons (T head, Cons<T> tail)
{
return new Cons<T>(head, tail);
}
protected static void addAll (List<QueryClause> toList, Cons<? extends QueryClause> cons)
{
if (cons != null) {
toList.add(cons.head);
addAll(toList, cons.tail);
}
}
protected final PersistenceContext _ctx; protected final PersistenceContext _ctx;
protected final DepotRepository _repo; protected final DepotRepository _repo;
protected final Class<T> _pclass; protected final Class<T> _pclass;
@@ -635,7 +662,6 @@ public class Query<T extends PersistentRecord>
protected Limit _limit; protected Limit _limit;
protected FromOverride _fromOverride; protected FromOverride _fromOverride;
protected ForUpdate _forUpdate; protected ForUpdate _forUpdate;
protected Cons<Join> _joins;
protected List<Join> _joins; protected Cons<FieldDefinition> _fieldDefs;
protected List<FieldDefinition> _fieldDefs;
} }