diff --git a/src/main/java/com/samskivert/depot/CountRecord.java b/src/main/java/com/samskivert/depot/CountRecord.java
index 2f568ea..6743fc8 100644
--- a/src/main/java/com/samskivert/depot/CountRecord.java
+++ b/src/main/java/com/samskivert/depot/CountRecord.java
@@ -25,11 +25,9 @@ import com.samskivert.depot.annotation.Entity;
import com.samskivert.depot.expression.ColumnExp;
/**
- * Handy record for computing the count of something. For example:
- *
- * return load(CountRecord.class, from(ForumThreadRecord.class),
- * where(ForumThreadRecord.GROUP_ID.eq(groupId)).count;
- *
+ * Handy record for computing the count of something. In general, you need not use this directly,
+ * but should instead use {@link QueryBuilder#selectCount}. For example: {@code
+ * from(ForumThreadRecord.class).where(ForumThreadRecord.GROUP_ID.eq(groupId)).selectCount()}
*/
@Computed @Entity
public class CountRecord extends PersistentRecord
diff --git a/src/main/java/com/samskivert/depot/DepotRepository.java b/src/main/java/com/samskivert/depot/DepotRepository.java
index 0dd2953..778107a 100644
--- a/src/main/java/com/samskivert/depot/DepotRepository.java
+++ b/src/main/java/com/samskivert/depot/DepotRepository.java
@@ -337,6 +337,15 @@ public abstract class DepotRepository
return _ctx.invoke(new FindAllKeysQuery(_ctx, type, forUpdate, clauses));
}
+ /**
+ * Returns a builder that can be used to construct a query in a fluent style. For example:
+ * {@code from(FooRecord.class).where(ID.greaterThan(25)).ascending(SIZE).select()}
+ */
+ public QueryBuilder from (Class type)
+ {
+ return new QueryBuilder(this, type);
+ }
+
/**
* Inserts the supplied persistent object into the database, assigning its primary key (if it
* has one) in the process.
diff --git a/src/main/java/com/samskivert/depot/FluentRepository.java b/src/main/java/com/samskivert/depot/FluentRepository.java
deleted file mode 100644
index e5d03f1..0000000
--- a/src/main/java/com/samskivert/depot/FluentRepository.java
+++ /dev/null
@@ -1,150 +0,0 @@
-//
-// $Id$
-//
-// Depot library - a Java relational persistence library
-// Copyright (C) 2006-2010 Michael Bayne and Pär Winzell
-//
-// This library is free software; you can redistribute it and/or modify it
-// under the terms of the GNU Lesser General Public License as published
-// by the Free Software Foundation; either version 2.1 of the License, or
-// (at your option) any later version.
-//
-// This library is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-// Lesser General Public License for more details.
-//
-// You should have received a copy of the GNU Lesser General Public
-// License along with this library; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-package com.samskivert.depot;
-
-import com.samskivert.depot.clause.ForUpdate;
-import com.samskivert.depot.clause.FromOverride;
-import com.samskivert.depot.clause.GroupBy;
-import com.samskivert.depot.clause.Join;
-import com.samskivert.depot.clause.Limit;
-import com.samskivert.depot.clause.OrderBy;
-import com.samskivert.depot.clause.Where;
-import com.samskivert.depot.expression.ColumnExp;
-import com.samskivert.depot.expression.SQLExpression;
-
-/**
- * Provides a variety of helper methods for constructing queries more concisely (and in some case
- * using a more fluent style).
- */
-public abstract class FluentRepository extends DepotRepository
-{
- protected FluentRepository (PersistenceContext context)
- {
- super(context);
- }
-
- /**
- * Returns a {@link Where} clause that ANDs together all of the supplied expressions.
- */
- protected static Where where (SQLExpression... exprs)
- {
- switch (exprs.length) {
- case 0:
- throw new IllegalArgumentException("Must supply at least one expression.");
- case 1:
- return new Where(exprs[0]);
- default:
- return new Where(Ops.and(exprs));
- }
- }
-
- /**
- * Returns a {@link Where} clause that selects rows where the supplied column equals the
- * supplied value.
- */
- protected static Where where (ColumnExp column, Comparable> value)
- {
- return new Where(column, value);
- }
-
- /**
- * Returns a {@link Where} clause that selects rows where both supplied columns equal both
- * supplied values.
- */
- protected static Where where (ColumnExp index1, Comparable> value1,
- ColumnExp index2, Comparable> value2)
- {
- return new Where(index1, value1, index2, value2);
- }
-
- /**
- * Returns a {@link Join} clause configured with the supplied left and right columns.
- */
- protected static Join join (ColumnExp left, ColumnExp right)
- {
- return new Join(left, right);
- }
-
- /**
- * Returns a {@link GroupBy} clause on the supplied group expressions.
- */
- protected static GroupBy groupBy (SQLExpression... exprs)
- {
- return new GroupBy(exprs);
- }
-
- /**
- * Returns an {@link OrderBy} clause configured to randomly order the results.
- */
- protected static OrderBy random ()
- {
- return OrderBy.random();
- }
-
- /**
- * Returns an {@link OrderBy} clause that ascends on the supplied expression.
- */
- protected static OrderBy ascending (SQLExpression value)
- {
- return OrderBy.ascending(value);
- }
-
- /**
- * Returns an {@link OrderBy} clause that descends on the supplied expression.
- */
- protected static OrderBy descending (SQLExpression value)
- {
- return OrderBy.descending(value);
- }
-
- /**
- * Returns a {@link Limit} clause configured with the supplied offset and count.
- */
- protected static Limit limit (int offset, int count)
- {
- return new Limit(offset, count);
- }
-
- /**
- * Returns a {@link FromOverride} clause configured with the supplied override class.
- */
- protected static FromOverride from (Class extends PersistentRecord> fromClass)
- {
- return new FromOverride(fromClass);
- }
-
- /**
- * Returns a {@link FromOverride} clause configured with the supplied override classes.
- */
- protected static FromOverride from (Class extends PersistentRecord> fromClass1,
- Class extends PersistentRecord> fromClass2)
- {
- return new FromOverride(fromClass1, fromClass2);
- }
-
- /**
- * Returns a {@link ForUpdate} clause which marks this query as selecting for update.
- */
- protected static ForUpdate forUpdate ()
- {
- return new ForUpdate();
- }
-}
diff --git a/src/main/java/com/samskivert/depot/QueryBuilder.java b/src/main/java/com/samskivert/depot/QueryBuilder.java
new file mode 100644
index 0000000..70f12e6
--- /dev/null
+++ b/src/main/java/com/samskivert/depot/QueryBuilder.java
@@ -0,0 +1,280 @@
+//
+// $Id$
+//
+// Depot library - a Java relational persistence library
+// Copyright (C) 2006-2010 Michael Bayne and Pär Winzell
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.samskivert.depot;
+
+import java.util.List;
+
+import com.google.common.collect.Lists;
+
+import com.samskivert.depot.clause.*;
+import com.samskivert.depot.expression.ColumnExp;
+import com.samskivert.depot.expression.SQLExpression;
+
+/**
+ * The root of a fluent mechanism for constructing queries. Obtain an instance via {@link
+ * DepotRepository#from}.
+ */
+public class QueryBuilder
+{
+ public QueryBuilder (DepotRepository repo, Class pclass)
+ {
+ _repo = repo;
+ _pclass = pclass;
+ }
+
+ /** Disables the use of the cache for this query. */
+ public QueryBuilder noCache () {
+ _cache = DepotRepository.CacheStrategy.BEST;
+ return this;
+ }
+
+ /** Configures the use of {@link CacheStrategy#BEST} for this query. */
+ public QueryBuilder cacheBest () {
+ _cache = DepotRepository.CacheStrategy.BEST;
+ return this;
+ }
+
+ /** Configures the use of {@link CacheStrategy#RECORDS} for this query. */
+ public QueryBuilder cacheRecords () {
+ _cache = DepotRepository.CacheStrategy.RECORDS;
+ return this;
+ }
+
+ /** Configures the use of {@link CacheStrategy#SHORT_KEYS} for this query. */
+ public QueryBuilder cacheShortKeys () {
+ _cache = DepotRepository.CacheStrategy.SHORT_KEYS;
+ return this;
+ }
+
+ /** Configures the use of {@link CacheStrategy#LONG_KEYS} for this query. */
+ public QueryBuilder cacheLongKeys () {
+ _cache = DepotRepository.CacheStrategy.LONG_KEYS;
+ return this;
+ }
+
+ /** Configures the use of {@link CacheStrategy#CONTENTS} for this query. */
+ public QueryBuilder cacheContents () {
+ _cache = DepotRepository.CacheStrategy.CONTENTS;
+ return this;
+ }
+
+ /**
+ * Configures a {@link Where} clause that ANDs together all of the supplied expressions.
+ */
+ public QueryBuilder where (SQLExpression... exprs)
+ {
+ switch (exprs.length) {
+ case 0:
+ throw new IllegalArgumentException("Must supply at least one expression.");
+ case 1:
+ _where = new Where(exprs[0]);
+ break;
+ default:
+ _where = new Where(Ops.and(exprs));
+ break;
+ }
+ return this;
+ }
+
+ /**
+ * Configures a {@link Where} clause that selects rows where the supplied column equals the
+ * supplied value.
+ */
+ public QueryBuilder where (ColumnExp column, Comparable> value)
+ {
+ _where = new Where(column, value);
+ return this;
+ }
+
+ /**
+ * Configures a {@link Where} clause that selects rows where both supplied columns equal both
+ * supplied values.
+ */
+ public QueryBuilder where (ColumnExp index1, Comparable> value1,
+ ColumnExp index2, Comparable> value2)
+ {
+ _where = new Where(index1, value1, index2, value2);
+ return this;
+ }
+
+ /**
+ * Configures a {@link Join} clause configured with the supplied left and right columns.
+ */
+ public QueryBuilder join (ColumnExp left, ColumnExp right)
+ {
+ _join = new Join(left, right);
+ return this;
+ }
+
+ /**
+ * Configures a {@link GroupBy} clause on the supplied group expressions.
+ */
+ public QueryBuilder groupBy (SQLExpression... exprs)
+ {
+ _groupBy = new GroupBy(exprs);
+ return this;
+ }
+
+ /**
+ * Configures an {@link OrderBy} clause configured to randomly order the results.
+ */
+ public QueryBuilder randomOrder ()
+ {
+ _orderBy = OrderBy.random();
+ return this;
+ }
+
+ /**
+ * Configures an {@link OrderBy} clause that ascends on the supplied expression.
+ */
+ public QueryBuilder ascending (SQLExpression value)
+ {
+ _orderBy = OrderBy.ascending(value);
+ return this;
+ }
+
+ /**
+ * Configures an {@link OrderBy} clause that descends on the supplied expression.
+ */
+ public QueryBuilder descending (SQLExpression value)
+ {
+ _orderBy = OrderBy.descending(value);
+ return this;
+ }
+
+ /**
+ * Configures a {@link Limit} clause configured with the supplied offset and count.
+ */
+ public QueryBuilder limit (int offset, int count)
+ {
+ _limit = new Limit(offset, count);
+ return this;
+ }
+
+ /**
+ * Configures a {@link FromOverride} clause configured with the supplied override class.
+ */
+ public QueryBuilder override (Class extends PersistentRecord> fromClass)
+ {
+ _fromOverride = new FromOverride(fromClass);
+ return this;
+ }
+
+ /**
+ * Configures a {@link FromOverride} clause configured with the supplied override classes.
+ */
+ public QueryBuilder override (Class extends PersistentRecord> fromClass1,
+ Class extends PersistentRecord> fromClass2)
+ {
+ _fromOverride = new FromOverride(fromClass1, fromClass2);
+ return this;
+ }
+
+ /**
+ * Configures a {@link ForUpdate} clause which marks this query as selecting for update.
+ */
+ public QueryBuilder forUpdate ()
+ {
+ _forUpdate = new ForUpdate();
+ return this;
+ }
+
+ /**
+ * Loads the first persistent object that matches the configured query clauses.
+ */
+ public T load ()
+ {
+ return _repo.load(_pclass, _cache, getClauseArray());
+ }
+
+ /**
+ * Loads all persistent objects that match the configured query clauses.
+ */
+ public List select ()
+ throws DatabaseException
+ {
+ return _repo.findAll(_pclass, _cache, getClauses());
+ }
+
+ /**
+ * Loads the keys of all persistent objects that match the configured query clauses. Note that
+ * cache configuration is ignored for key-only queries.
+ *
+ * @param useMaster if true, the query will be run using a read-write connection to ensure that
+ * it talks to the master database, if false, the query will be run on a read-only connection
+ * and may load keys from a slave. For performance reasons, you should always pass false unless
+ * you know you will be modifying the database as a result of this query and absolutely need
+ * the latest data.
+ */
+ public List> selectKeys (boolean useMaster)
+ throws DatabaseException
+ {
+ return _repo.findAllKeys(_pclass, useMaster, getClauses());
+ }
+
+ /**
+ * Returns the count of rows that match the configured query clauses.
+ */
+ public int selectCount ()
+ {
+ _fromOverride = new FromOverride(_pclass);
+ return _repo.load(CountRecord.class, _cache, getClauseArray()).count;
+ }
+
+ protected List getClauses ()
+ {
+ List clauses = Lists.newArrayList();
+ addIfNotNull(clauses, _where);
+ addIfNotNull(clauses, _join);
+ addIfNotNull(clauses, _orderBy);
+ addIfNotNull(clauses, _groupBy);
+ addIfNotNull(clauses, _limit);
+ addIfNotNull(clauses, _fromOverride);
+ addIfNotNull(clauses, _forUpdate);
+ return clauses;
+ }
+
+ protected QueryClause[] getClauseArray ()
+ {
+ List clauses = getClauses();
+ return clauses.toArray(new QueryClause[clauses.size()]);
+ }
+
+ protected void addIfNotNull (List clauses, QueryClause clause)
+ {
+ if (clause != null) {
+ clauses.add(clause);
+ }
+ }
+
+ protected final DepotRepository _repo;
+ protected final Class _pclass;
+
+ protected DepotRepository.CacheStrategy _cache = DepotRepository.CacheStrategy.BEST;
+
+ protected Where _where;
+ protected Join _join;
+ protected OrderBy _orderBy;
+ protected GroupBy _groupBy;
+ protected Limit _limit;
+ protected FromOverride _fromOverride;
+ protected ForUpdate _forUpdate;
+}
diff --git a/src/test/java/com/samskivert/depot/CountTest.java b/src/test/java/com/samskivert/depot/CountTest.java
new file mode 100644
index 0000000..51e05a8
--- /dev/null
+++ b/src/test/java/com/samskivert/depot/CountTest.java
@@ -0,0 +1,54 @@
+//
+// $Id$
+//
+// Depot library - a Java relational persistence library
+// Copyright (C) 2006-2010 Michael Bayne and Pär Winzell
+//
+// This library is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published
+// by the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+package com.samskivert.depot;
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import com.samskivert.depot.clause.Where;
+
+/**
+ * Tests row counting.
+ */
+public class CountTest extends TestBase
+{
+ @Test
+ public void testCount ()
+ {
+ for (int ii = 1; ii < 100; ii++) {
+ _repo.insert(createTestRecord(ii));
+ }
+
+ assertEquals(99, _repo.from(TestRecord.class).selectCount());
+ assertEquals(49, _repo.from(TestRecord.class).
+ where(TestRecord.RECORD_ID.lessThan(50)).selectCount());
+
+ _repo.deleteAll(TestRecord.class, new Where(Exps.literal("true")));
+
+ assertEquals(0, _repo.from(TestRecord.class).
+ where(TestRecord.RECORD_ID.lessThan(50)).selectCount());
+ assertEquals(0, _repo.from(TestRecord.class).selectCount());
+ }
+
+ // the HSQL in-memory database persists for the lifetime of the VM, which means we have to
+ // clean up after ourselves in every test; thus we go ahead and share a repository
+ protected TestRepository _repo = createTestRepository();
+}
diff --git a/src/test/java/com/samskivert/depot/GeneratedValueTest.java b/src/test/java/com/samskivert/depot/GeneratedValueTest.java
index b419107..217a079 100644
--- a/src/test/java/com/samskivert/depot/GeneratedValueTest.java
+++ b/src/test/java/com/samskivert/depot/GeneratedValueTest.java
@@ -27,8 +27,6 @@ import org.junit.Test;
import com.google.common.collect.Iterables;
-import com.samskivert.depot.clause.Where;
-
import static org.junit.Assert.assertEquals;
public class GeneratedValueTest
@@ -47,8 +45,8 @@ public class GeneratedValueTest
assertEquals(0, rec.recordId);
assertEquals(1, dr.insert(rec));
assertEquals(1, rec.recordId);
- List recs =
- dr.findAll(GeneratedValueRecord.class, new Where(GeneratedValueRecord.VALUE.eq(2)));
+ List recs = dr.from(GeneratedValueRecord.class).
+ where(GeneratedValueRecord.VALUE.eq(2)).select();
assertEquals(1, Iterables.getOnlyElement(recs).recordId);
}
}
diff --git a/src/test/java/com/samskivert/depot/QueryTest.java b/src/test/java/com/samskivert/depot/QueryTest.java
index b16c631..ab70290 100644
--- a/src/test/java/com/samskivert/depot/QueryTest.java
+++ b/src/test/java/com/samskivert/depot/QueryTest.java
@@ -29,6 +29,7 @@ import org.junit.Test;
import static org.junit.Assert.*;
import com.samskivert.depot.annotation.Computed;
+import com.samskivert.depot.expression.SQLExpression;
import com.samskivert.depot.clause.Where;
/**
@@ -63,9 +64,9 @@ public class QueryTest extends TestBase
assertEquals(0, _repo.deleteAll(TestRecord.class, none));
// test collection caching (TODO: check that the records are ==)
- Where where = new Where(TestRecord.RECORD_ID.greaterThan(CREATE_RECORDS-50));
- assertEquals(50, _repo.findAll(TestRecord.class, where).size());
- assertEquals(50, _repo.findAll(TestRecord.class, where).size());
+ SQLExpression where = TestRecord.RECORD_ID.greaterThan(CREATE_RECORDS-50);
+ assertEquals(50, _repo.from(TestRecord.class).where(where).select().size());
+ assertEquals(50, _repo.from(TestRecord.class).where(where).select().size());
// test a partial key set
KeySet some = KeySet.newSimpleKeySet(
diff --git a/src/test/java/com/samskivert/depot/TestRepository.java b/src/test/java/com/samskivert/depot/TestRepository.java
index 0a787bd..d3f74b7 100644
--- a/src/test/java/com/samskivert/depot/TestRepository.java
+++ b/src/test/java/com/samskivert/depot/TestRepository.java
@@ -26,7 +26,7 @@ import java.util.Set;
/**
* A test tool for the Depot repository services.
*/
-public class TestRepository extends FluentRepository
+public class TestRepository extends DepotRepository
{
public TestRecord loadNoCache (int recordId)
{
@@ -45,7 +45,7 @@ public class TestRepository extends FluentRepository
public List loadEnums (Set types)
{
- return findAll(EnumKeyRecord.class, where(EnumKeyRecord.TYPE.in(types)));
+ return from(EnumKeyRecord.class).where(EnumKeyRecord.TYPE.in(types)).select();
}
public void storeEnum (EnumKeyRecord record)