diff --git a/src/main/java/com/samskivert/depot/Query.java b/src/main/java/com/samskivert/depot/Query.java
index 035db65..38d76dc 100644
--- a/src/main/java/com/samskivert/depot/Query.java
+++ b/src/main/java/com/samskivert/depot/Query.java
@@ -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
- * DepotRepository#from}.
+ * DepotRepository#from}. For example:
+ *
{@code
+ * List> results = _repo.from(FooRecord.class).
+ * where(FooRecord.NAME.like("%foo%")).
+ * descending(FooRecord.NAME).
+ * limit(25).
+ * select(FooRecord.ID, FooRecord.NAME);
+ * }
+ *
+ * Note that each builder method returns a new builder instance so that partial queries can be
+ * constructed and reused without interfering with one another:
+ *
{@code
+ * Query query = from(FooRecord.class).where(FooRecord.NAME.eq("foo")).limit(10);
+ * List> top = query.ascending().select(FooRecord.ID, FooRecord.NAME);
+ * List> bot = query.descending().select(FooRecord.ID, FooRecord.NAME);
+ * }
*/
public class Query
implements Cloneable
{
- public Query (PersistenceContext ctx, DepotRepository repo, Class pclass)
- {
- _ctx = ctx;
- _repo = repo;
- _pclass = pclass;
- }
-
/** Disables the use of the cache for this query. */
public Query noCache () {
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 cacheBest () {
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 cacheRecords () {
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 cacheShortKeys () {
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 cacheLongKeys () {
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 cacheContents () {
return cache(DepotRepository.CacheStrategy.CONTENTS);
}
/** Configures the specified caching policy this query. */
public Query cache (DepotRepository.CacheStrategy cache) {
- _cache = cache;
- return this;
+ Query query = clone();
+ query._cache = cache;
+ return query;
}
/**
@@ -141,8 +150,9 @@ public class Query
public Query where (WhereClause where)
{
checkState(_where == null, "Where clause is already configured.");
- _where = where;
- return this;
+ Query query = clone();
+ query._where = where;
+ return query;
}
/**
@@ -177,11 +187,9 @@ public class Query
*/
public Query join (Join join)
{
- if (_joins == null) {
- _joins = Lists.newArrayList();
- }
- _joins.add(join);
- return this;
+ Query query = clone();
+ query._joins = cons(join, query._joins);
+ return query;
}
/**
@@ -190,8 +198,9 @@ public class Query
public Query groupBy (SQLExpression>... exprs)
{
checkState(_groupBy == null, "GroupBy clause is already configured.");
- _groupBy = new GroupBy(exprs);
- return this;
+ Query query = clone();
+ query._groupBy = new GroupBy(exprs);
+ return query;
}
/**
@@ -224,8 +233,9 @@ public class Query
public Query orderBy (OrderBy orderBy)
{
checkState(_orderBy == null, "OrderBy clause is already configured.");
- _orderBy = orderBy;
- return this;
+ Query query = clone();
+ query._orderBy = orderBy;
+ return query;
}
/**
@@ -234,8 +244,9 @@ public class Query
public Query limit (int count)
{
checkState(_limit == null, "Limit clause is already configured.");
- _limit = new Limit(0, count);
- return this;
+ Query query = clone();
+ query._limit = new Limit(0, count);
+ return query;
}
/**
@@ -244,8 +255,9 @@ public class Query
public Query limit (int offset, int count)
{
checkState(_limit == null, "Limit clause is already configured.");
- _limit = new Limit(offset, count);
- return this;
+ Query query = clone();
+ query._limit = new Limit(offset, count);
+ return query;
}
/**
@@ -271,8 +283,9 @@ public class Query
public Query override (FromOverride fromOverride)
{
checkState(_fromOverride == null, "FromOverride clause is already configured.");
- _fromOverride = fromOverride;
- return this;
+ Query query = clone();
+ query._fromOverride = fromOverride;
+ return query;
}
/**
@@ -304,11 +317,9 @@ public class Query
*/
public Query fieldDef (FieldDefinition fieldDef)
{
- if (_fieldDefs == null) {
- _fieldDefs = Lists.newArrayList();
- }
- _fieldDefs.add(fieldDef);
- return this;
+ Query query = clone();
+ query._fieldDefs = cons(fieldDef, query._fieldDefs);
+ return query;
}
/**
@@ -317,8 +328,9 @@ public class Query
public Query forUpdate ()
{
checkState(_forUpdate == null, "ForUpdate clause is already configured.");
- _forUpdate = new ForUpdate();
- return this;
+ Query query = clone();
+ query._forUpdate = new ForUpdate();
+ return query;
}
/**
@@ -554,21 +566,17 @@ public class Query
return _repo.deleteAll(_pclass, _where, invalidator);
}
- /**
- * Returns a clone of this query builder, including all partially configured state. Useful for
- * constructing partially configured queries and then executing variants.
- */
- public Query clone ()
+ protected Query (PersistenceContext ctx, DepotRepository repo, Class pclass)
+ {
+ _ctx = ctx;
+ _repo = repo;
+ _pclass = pclass;
+ }
+
+ protected Query clone ()
{
try {
@SuppressWarnings("unchecked") Query qb = (Query)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;
} catch (Throwable t) {
throw new AssertionError(t);
@@ -579,16 +587,12 @@ public class Query
{
List clauses = Lists.newArrayList();
addIfNotNull(clauses, _where);
- if (_joins != null) {
- clauses.addAll(_joins);
- }
+ addAll(clauses, _joins);
addIfNotNull(clauses, _orderBy);
addIfNotNull(clauses, _groupBy);
addIfNotNull(clauses, _limit);
addIfNotNull(clauses, _fromOverride);
- if (_fieldDefs != null) {
- clauses.addAll(_fieldDefs);
- }
+ addAll(clauses, _fieldDefs);
addIfNotNull(clauses, _forUpdate);
return clauses;
}
@@ -623,6 +627,29 @@ public class Query
return selections.isEmpty() ? null : selections.get(0);
}
+ protected static final class Cons
+ {
+ public T head;
+ public Cons tail;
+ public Cons (T head, Cons tail) {
+ this.head = head;
+ this.tail = tail;
+ }
+ }
+
+ protected static Cons cons (T head, Cons tail)
+ {
+ return new Cons(head, tail);
+ }
+
+ protected static void addAll (List toList, Cons extends QueryClause> cons)
+ {
+ if (cons != null) {
+ toList.add(cons.head);
+ addAll(toList, cons.tail);
+ }
+ }
+
protected final PersistenceContext _ctx;
protected final DepotRepository _repo;
protected final Class _pclass;
@@ -635,7 +662,6 @@ public class Query
protected Limit _limit;
protected FromOverride _fromOverride;
protected ForUpdate _forUpdate;
-
- protected List _joins;
- protected List _fieldDefs;
+ protected Cons _joins;
+ protected Cons _fieldDefs;
}