From f6a2ad10004c1501865f9b6ecfb7e5856af13a84 Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Wed, 8 Dec 2010 08:23:27 +0000 Subject: [PATCH] Exciting new additions: 1. ColumnExp is now parameterized on the type of the column it represents. This enables item number two. 2. The beginnings of a very primitive implementation of individual field selection, which looks like: List> results = from(FooRecord.class).where( FooRecord.ID.in(someIds)).select(FooRecord.ID, FooRecord.NAME); I don't advise using this new functionality just yet, because it's very primitive and will probably break if you try to do anything fancy. Plus it doesn't interact with the cache at all yet. However, I wanted to get the typed ColumnExp bits in there so that I can migrate all the existing projects over and get that painful business out of the way. Note for the jumpy: migration in this case means eliminating a bunch of unchecked type usage warnings, rather than behavioral changes. The existing Depot functionality continues to work exactly as is. The types are not used except for in the new individual field selection code. One bit of code that will break is calls to updatePartial() which used to take a Map and now take a Map, ?>. Since those were already in generics land, they won't just fail with an unchecked type warning, the compiler will reject the old call (the calls are binary compatible, so unrecompiled code will still work fine). Fortunately the Map-taking updatePartial is not called that much outside of MSOY, which I have already migrated to fully parameterized ColumnExp land. --- .../com/samskivert/depot/CountRecord.java | 2 +- .../com/samskivert/depot/DepotRepository.java | 32 ++-- src/main/java/com/samskivert/depot/Key.java | 34 ++-- .../java/com/samskivert/depot/KeySet.java | 4 +- .../com/samskivert/depot/MultiKeySet.java | 4 +- .../samskivert/depot/PersistentRecord.java | 23 +-- .../com/samskivert/depot/QueryBuilder.java | 36 +++- .../com/samskivert/depot/SchemaMigration.java | 6 +- .../java/com/samskivert/depot/Tuple2.java | 46 +++++ .../samskivert/depot/annotation/Index.java | 2 +- .../depot/clause/FieldDefinition.java | 4 +- .../depot/clause/FieldOverride.java | 2 +- .../com/samskivert/depot/clause/Join.java | 2 +- .../samskivert/depot/clause/SelectClause.java | 8 +- .../com/samskivert/depot/clause/Where.java | 24 +-- .../depot/expression/ColumnExp.java | 14 +- .../samskivert/depot/impl/BuildVisitor.java | 24 +-- .../com/samskivert/depot/impl/ColumnSet.java | 65 +++++++ .../depot/impl/DepotMarshaller.java | 44 ++--- .../impl/DepotMigrationHistoryRecord.java | 4 +- .../com/samskivert/depot/impl/DepotUtil.java | 16 +- .../depot/impl/ExpressionEvaluator.java | 6 +- .../samskivert/depot/impl/FindAllQuery.java | 165 ++++++++++++++---- .../depot/impl/FragmentVisitor.java | 2 +- .../samskivert/depot/impl/HSQLBuilder.java | 2 +- .../samskivert/depot/impl/MySQLBuilder.java | 2 +- .../depot/impl/PostgreSQLBuilder.java | 2 +- .../depot/impl/QueryMarshaller.java | 55 ++++++ .../samskivert/depot/impl/QueryResult.java | 41 +++++ .../depot/impl/clause/UpdateClause.java | 8 +- .../samskivert/depot/tools/GenRecordTask.java | 24 +++ .../samskivert/depot/tools/record_column.tmpl | 2 +- .../com/samskivert/depot/AllTypesRecord.java | 72 ++++---- .../com/samskivert/depot/AllTypesTest.java | 1 + .../java/com/samskivert/depot/CrudTest.java | 6 +- .../com/samskivert/depot/EnumKeyRecord.java | 4 +- .../depot/GeneratedValueRecord.java | 4 +- .../com/samskivert/depot/MonkeyRecord.java | 6 +- .../java/com/samskivert/depot/TestRecord.java | 14 +- 39 files changed, 585 insertions(+), 227 deletions(-) create mode 100644 src/main/java/com/samskivert/depot/Tuple2.java create mode 100644 src/main/java/com/samskivert/depot/impl/ColumnSet.java create mode 100644 src/main/java/com/samskivert/depot/impl/QueryMarshaller.java create mode 100644 src/main/java/com/samskivert/depot/impl/QueryResult.java diff --git a/src/main/java/com/samskivert/depot/CountRecord.java b/src/main/java/com/samskivert/depot/CountRecord.java index 6743fc8..bf636d2 100644 --- a/src/main/java/com/samskivert/depot/CountRecord.java +++ b/src/main/java/com/samskivert/depot/CountRecord.java @@ -34,7 +34,7 @@ public class CountRecord extends PersistentRecord { // AUTO-GENERATED: FIELDS START public static final Class _R = CountRecord.class; - public static final ColumnExp COUNT = colexp(_R, "count"); + public static final ColumnExp COUNT = colexp(_R, "count"); // AUTO-GENERATED: FIELDS END /** The computed count. */ diff --git a/src/main/java/com/samskivert/depot/DepotRepository.java b/src/main/java/com/samskivert/depot/DepotRepository.java index 778107a..6954ba4 100644 --- a/src/main/java/com/samskivert/depot/DepotRepository.java +++ b/src/main/java/com/samskivert/depot/DepotRepository.java @@ -337,13 +337,20 @@ public abstract class DepotRepository return _ctx.invoke(new FindAllKeysQuery(_ctx, type, forUpdate, clauses)); } + // public List findAll ( + // Class type, Iterable clauses) + // throws DatabaseException + // { + // return _ctx.invoke(new FindAllQuery.ForColumns(_ctx, type, clauses, column)); + // } + /** * 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); + return new QueryBuilder(_ctx, this, type); } /** @@ -422,7 +429,7 @@ public abstract class DepotRepository * * @throws DatabaseException if any problem is encountered communicating with the database. */ - public int update (T record, final ColumnExp... modifiedFields) + public int update (T record, final ColumnExp... modifiedFields) throws DatabaseException { @SuppressWarnings("unchecked") Class pClass = (Class) record.getClass(); @@ -451,7 +458,7 @@ public abstract class DepotRepository * @throws DatabaseException if any problem is encountered communicating with the database. */ public int updatePartial ( - Key key, ColumnExp field, Object value, Object... more) + Key key, ColumnExp field, Object value, Object... more) throws DatabaseException { return updatePartial(key.getPersistentClass(), key, key, field, value, more); @@ -471,7 +478,8 @@ public abstract class DepotRepository * values that duplicate another row already in the database. * @throws DatabaseException if any problem is encountered communicating with the database. */ - public int updatePartial (Key key, Map updates) + public int updatePartial ( + Key key, Map, ?> updates) throws DatabaseException { return updatePartial(key.getPersistentClass(), key, key, updates); @@ -498,15 +506,15 @@ public abstract class DepotRepository * @throws DatabaseException if any problem is encountered communicating with the database. */ public int updatePartial ( - Class type, final WhereClause key, CacheInvalidator invalidator, - Map updates) + Class type, WhereClause key, CacheInvalidator invalidator, + Map, ?> updates) throws DatabaseException { // separate the arguments into keys and values - final ColumnExp[] fields = new ColumnExp[updates.size()]; + final ColumnExp[] fields = new ColumnExp[updates.size()]; final SQLExpression[] values = new SQLExpression[fields.length]; int ii = 0; - for (Map.Entry entry : updates.entrySet()) { + for (Map.Entry, ?> entry : updates.entrySet()) { fields[ii] = entry.getKey(); values[ii++] = makeValue(entry.getValue()); } @@ -536,16 +544,16 @@ public abstract class DepotRepository */ public int updatePartial ( Class type, final WhereClause key, CacheInvalidator invalidator, - ColumnExp field, Object value, Object... more) + ColumnExp field, Object value, Object... more) throws DatabaseException { // separate the updates into keys and values - final ColumnExp[] fields = new ColumnExp[1+more.length/2]; + final ColumnExp[] fields = new ColumnExp[1+more.length/2]; final SQLExpression[] values = new SQLExpression[fields.length]; fields[0] = field; values[0] = makeValue(value); for (int ii = 1, idx = 0; ii < fields.length; ii++) { - fields[ii] = (ColumnExp)more[idx++]; + fields[ii] = (ColumnExp)more[idx++]; values[ii] = makeValue(more[idx++]); } return updatePartial(type, key, invalidator, fields, values); @@ -572,7 +580,7 @@ public abstract class DepotRepository */ public int updatePartial ( Class type, final WhereClause key, CacheInvalidator invalidator, - ColumnExp[] fields, SQLExpression[] values) + ColumnExp[] fields, SQLExpression[] values) throws DatabaseException { requireNotComputed(type, "updatePartial"); diff --git a/src/main/java/com/samskivert/depot/Key.java b/src/main/java/com/samskivert/depot/Key.java index 6ec3a78..780e995 100644 --- a/src/main/java/com/samskivert/depot/Key.java +++ b/src/main/java/com/samskivert/depot/Key.java @@ -73,30 +73,32 @@ public class Key extends WhereClause /** * Creates a single column key. */ - public static Key newKey ( - Class pClass, ColumnExp ix, Comparable val) + public static > Key newKey ( + Class pClass, ColumnExp ix, V val) { - return new Key(pClass, new ColumnExp[] { ix }, new Comparable[] { val }); + return new Key(pClass, new ColumnExp[] { ix }, new Comparable[] { val }); } /** * Creates a two column key. */ - public static Key newKey ( - Class pClass, ColumnExp ix1, Comparable val1, ColumnExp ix2, Comparable val2) + public static , V2 extends Comparable> + Key newKey (Class pClass, ColumnExp ix1, V1 val1, ColumnExp ix2, V2 val2) { - return new Key(pClass, new ColumnExp[] { ix1, ix2 }, new Comparable[] { val1, val2 }); + return new Key(pClass, new ColumnExp[] { ix1, ix2 }, + new Comparable[] { val1, val2 }); } /** * Creates a three column key. */ - public static Key newKey ( - Class pClass, ColumnExp ix1, Comparable val1, ColumnExp ix2, Comparable val2, - ColumnExp ix3, Comparable val3) + public static , + V2 extends Comparable, V3 extends Comparable> + Key newKey (Class pClass, ColumnExp ix1, V1 val1, ColumnExp ix2, V2 val2, + ColumnExp ix3, V3 val3) { - return new Key(pClass, new ColumnExp[] { ix1, ix2, ix3 }, - new Comparable[] { val1, val2, val3 }); + return new Key(pClass, new ColumnExp[] { ix1, ix2, ix3 }, + new Comparable[] { val1, val2, val3 }); } /** @@ -126,7 +128,7 @@ public class Key extends WhereClause /** * Constructs a new multi-column {@code Key} with the given values. */ - public Key (Class pClass, ColumnExp[] fields, Comparable[] values) + public Key (Class pClass, ColumnExp[] fields, Comparable[] values) { this(pClass, toCanonicalOrder(pClass, fields, values)); } @@ -198,7 +200,7 @@ public class Key extends WhereClause */ public void toShortString (StringBuilder builder) { - ColumnExp[] keyFields = DepotUtil.getKeyFields(_pClass); + ColumnExp[] keyFields = DepotUtil.getKeyFields(_pClass); for (int ii = 0; ii < keyFields.length; ii ++) { if (ii > 0) { builder.append(":"); @@ -243,14 +245,14 @@ public class Key extends WhereClause } protected static Comparable[] toCanonicalOrder ( - Class pClass, ColumnExp[] fields, Comparable[] values) + Class pClass, ColumnExp[] fields, Comparable[] values) { if (fields.length != values.length) { throw new IllegalArgumentException("Field and Value arrays must be of equal length."); } // look up the cached primary key fields for this object - ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass); + ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass); // fast path! if (fields.length == 1 && keyFields.length == 1 && keyFields[0].equals(fields[0])) { @@ -258,7 +260,7 @@ public class Key extends WhereClause } // build a local map of field name -> field value - Map> map = Maps.newHashMap(); + Map, Comparable> map = Maps.newHashMap(); for (int ii = 0; ii < fields.length; ii++) { map.put(fields[ii], values[ii]); } diff --git a/src/main/java/com/samskivert/depot/KeySet.java b/src/main/java/com/samskivert/depot/KeySet.java index 92e539b..be305cf 100644 --- a/src/main/java/com/samskivert/depot/KeySet.java +++ b/src/main/java/com/samskivert/depot/KeySet.java @@ -62,7 +62,7 @@ public abstract class KeySet extends WhereClause return new EmptyKeySet(pClass); } - ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass); + ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass); if (keyFields.length == 1) { Comparable first = keys.iterator().next().getValues()[0]; Comparable[] keyArray; @@ -99,7 +99,7 @@ public abstract class KeySet extends WhereClause public static KeySet newSimpleKeySet ( Class pClass, Collection> keys) { - ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass); + ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass); if (keyFields.length != 1) { throw new IllegalArgumentException( "Cannot create KeySet using simple keys for record with non-simple primary key " + diff --git a/src/main/java/com/samskivert/depot/MultiKeySet.java b/src/main/java/com/samskivert/depot/MultiKeySet.java index 5a00b14..91730cf 100644 --- a/src/main/java/com/samskivert/depot/MultiKeySet.java +++ b/src/main/java/com/samskivert/depot/MultiKeySet.java @@ -35,7 +35,7 @@ import com.samskivert.util.Tuple; */ class MultiKeySet extends KeySet { - public MultiKeySet (Class pClass, ColumnExp[] keyFields, Comparable[][] keys) + public MultiKeySet (Class pClass, ColumnExp[] keyFields, Comparable[][] keys) { super(pClass); _keys = keys; @@ -207,5 +207,5 @@ class MultiKeySet extends KeySet } protected Comparable[][] _keys; - protected ColumnExp[] _keyFields; + protected ColumnExp[] _keyFields; } diff --git a/src/main/java/com/samskivert/depot/PersistentRecord.java b/src/main/java/com/samskivert/depot/PersistentRecord.java index 21b0466..f50673c 100644 --- a/src/main/java/com/samskivert/depot/PersistentRecord.java +++ b/src/main/java/com/samskivert/depot/PersistentRecord.java @@ -20,39 +20,28 @@ package com.samskivert.depot; -import java.io.Serializable; - import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.impl.DepotUtil; +import com.samskivert.depot.impl.QueryResult; /** * The base class for all persistent records used in Depot. Persistent records must be cloneable * and serializable; this class is used to enforce those requirements. */ -public class PersistentRecord - implements Cloneable, Serializable +public class PersistentRecord extends QueryResult { // AUTO-GENERATED: FIELDS START public static final Class _R = PersistentRecord.class; // AUTO-GENERATED: FIELDS END - @Override // from Object - public PersistentRecord clone () - { - try { - return (PersistentRecord) super.clone(); - } catch (CloneNotSupportedException cnse) { - throw new AssertionError(cnse); // this should never happen since we are Cloneable - } - } - /** * Creates a column expression for this class with the specified field name. Used by the * autogenerated column expression constants. */ - protected static ColumnExp colexp (Class clazz, String fieldName) + protected static ColumnExp colexp ( + Class clazz, String fieldName) { - return new ColumnExp(clazz, fieldName); + return new ColumnExp(clazz, fieldName); } /** @@ -68,7 +57,7 @@ public class PersistentRecord /** * Register the key fields for a subclass. Called by autogenerated code. */ - protected static void registerKeyFields (ColumnExp... fields) + protected static void registerKeyFields (ColumnExp... fields) { DepotUtil.registerKeyFields(fields); } diff --git a/src/main/java/com/samskivert/depot/QueryBuilder.java b/src/main/java/com/samskivert/depot/QueryBuilder.java index 7c6ed0f..ed058f5 100644 --- a/src/main/java/com/samskivert/depot/QueryBuilder.java +++ b/src/main/java/com/samskivert/depot/QueryBuilder.java @@ -29,6 +29,8 @@ import com.google.common.collect.Lists; import com.samskivert.depot.clause.*; import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.expression.SQLExpression; +import com.samskivert.depot.impl.ColumnSet; +import com.samskivert.depot.impl.FindAllQuery; /** * The root of a fluent mechanism for constructing queries. Obtain an instance via {@link @@ -37,8 +39,9 @@ import com.samskivert.depot.expression.SQLExpression; public class QueryBuilder implements Cloneable { - public QueryBuilder (DepotRepository repo, Class pclass) + public QueryBuilder (PersistenceContext ctx, DepotRepository repo, Class pclass) { + _ctx = ctx; _repo = repo; _pclass = pclass; } @@ -104,7 +107,7 @@ public class QueryBuilder * Configures a {@link Where} clause that selects rows where the supplied column equals the * supplied value. */ - public QueryBuilder where (ColumnExp column, Comparable value) + public > QueryBuilder where (ColumnExp column, V value) { return where(new Where(column, value)); } @@ -113,8 +116,8 @@ public class QueryBuilder * 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) + public , V2 extends Comparable> + QueryBuilder where (ColumnExp index1, V1 value1, ColumnExp index2, V2 value2) { return where(new Where(index1, value1, index2, value2)); } @@ -133,7 +136,7 @@ public class QueryBuilder /** * Adds a {@link Join} clause configured with the supplied left and right columns. */ - public QueryBuilder join (ColumnExp left, ColumnExp right) + public QueryBuilder join (ColumnExp left, ColumnExp right) { return join(new Join(left, right)); } @@ -151,7 +154,7 @@ public class QueryBuilder * Adds a {@link Join} clause configured with the supplied left and right columns and join * type. */ - public QueryBuilder join (ColumnExp left, ColumnExp right, Join.Type type) + public QueryBuilder join (ColumnExp left, ColumnExp right, Join.Type type) { return join(new Join(left, right).setType(type)); } @@ -279,7 +282,7 @@ public class QueryBuilder /** * Adds a {@link FieldDefinition} clause. */ - public QueryBuilder fieldDef (ColumnExp field, SQLExpression override) + public QueryBuilder fieldDef (ColumnExp field, SQLExpression override) { return fieldDef(new FieldDefinition(field, override)); } @@ -348,6 +351,24 @@ public class QueryBuilder return _repo.load(CountRecord.class, _cache, getClauseArray()).count; } + /** + * Returns just the supplied column from the rows matching the query. + */ + public List select (ColumnExp column) + { + return _ctx.invoke(new FindAllQuery.ForColumns( + _ctx, ColumnSet.create(_pclass, column), getClauses())); + } + + /** + * Returns just the supplied columns from the rows matching the query. + */ + public List> select (ColumnExp col1, ColumnExp col2) + { + return _ctx.invoke(new FindAllQuery.ForColumns>( + _ctx, ColumnSet.create(_pclass, col1, col2), getClauses())); + } + /** * Deletes the records that match the configured query clauses. Note that only the where * clauses are used to evaluate a deletion. Attempts to use other clauses will result in @@ -455,6 +476,7 @@ public class QueryBuilder requireNull(_forUpdate, "ForUpdate clause not supported by delete."); } + protected final PersistenceContext _ctx; protected final DepotRepository _repo; protected final Class _pclass; diff --git a/src/main/java/com/samskivert/depot/SchemaMigration.java b/src/main/java/com/samskivert/depot/SchemaMigration.java index 0fd3f8f..155ddc9 100644 --- a/src/main/java/com/samskivert/depot/SchemaMigration.java +++ b/src/main/java/com/samskivert/depot/SchemaMigration.java @@ -76,7 +76,7 @@ public abstract class SchemaMigration extends Modifier */ public static class Rename extends SchemaMigration { - public Rename (int targetVersion, String oldColumnName, ColumnExp newColumn) { + public Rename (int targetVersion, String oldColumnName, ColumnExp newColumn) { super(targetVersion); _oldColumnName = oldColumnName; _fieldName = newColumn.name; @@ -131,7 +131,7 @@ public abstract class SchemaMigration extends Modifier */ public static class Retype extends SchemaMigration { - public Retype (int targetVersion, ColumnExp column) { + public Retype (int targetVersion, ColumnExp column) { super(targetVersion); _fieldName = column.name; } @@ -170,7 +170,7 @@ public abstract class SchemaMigration extends Modifier */ public static class Add extends SchemaMigration { - public Add (int targetVersion, ColumnExp column, String defaultValue) { + public Add (int targetVersion, ColumnExp column, String defaultValue) { super(targetVersion); _fieldName = column.name; _defaultValue = defaultValue; diff --git a/src/main/java/com/samskivert/depot/Tuple2.java b/src/main/java/com/samskivert/depot/Tuple2.java new file mode 100644 index 0000000..c4932d1 --- /dev/null +++ b/src/main/java/com/samskivert/depot/Tuple2.java @@ -0,0 +1,46 @@ +// +// $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.io.Serializable; + +import com.samskivert.depot.impl.QueryResult; + +/** + * Contains a two column result. + */ +public class Tuple2 implements Serializable +{ + public final A a; + public final B b; + + public Tuple2 (A a, B b) + { + this.a = a; + this.b = b; + } + + @Override + public String toString () + { + return "[" + a + "," + b + "]"; + } +} diff --git a/src/main/java/com/samskivert/depot/annotation/Index.java b/src/main/java/com/samskivert/depot/annotation/Index.java index 9aa1b45..9ac539a 100644 --- a/src/main/java/com/samskivert/depot/annotation/Index.java +++ b/src/main/java/com/samskivert/depot/annotation/Index.java @@ -41,7 +41,7 @@ public @interface Index * If this is defined, a static method must be defined on the record that provides the index * configuration. The method must match one of the following two signatures: *
-     * public static ColumnExp[] indexName ()
+     * public static ColumnExp[] indexName ()
      * public static List<Tuple<SQLExpression, OrderBy.Order>> indexName ()
      * 
* The first form will result in a simple multicolum index being created diff --git a/src/main/java/com/samskivert/depot/clause/FieldDefinition.java b/src/main/java/com/samskivert/depot/clause/FieldDefinition.java index 885c5e5..0814b76 100644 --- a/src/main/java/com/samskivert/depot/clause/FieldDefinition.java +++ b/src/main/java/com/samskivert/depot/clause/FieldDefinition.java @@ -46,7 +46,7 @@ public class FieldDefinition implements QueryClause public FieldDefinition (String field, Class pClass, String pCol) { - this(field, new ColumnExp(pClass, pCol)); + this(field, new ColumnExp(pClass, pCol)); } public FieldDefinition (String field, SQLExpression override) @@ -55,7 +55,7 @@ public class FieldDefinition implements QueryClause _definition = override; } - public FieldDefinition (ColumnExp field, SQLExpression override) + public FieldDefinition (ColumnExp field, SQLExpression override) { _field = field.name; _definition = override; diff --git a/src/main/java/com/samskivert/depot/clause/FieldOverride.java b/src/main/java/com/samskivert/depot/clause/FieldOverride.java index 4893e7b..a453c4f 100644 --- a/src/main/java/com/samskivert/depot/clause/FieldOverride.java +++ b/src/main/java/com/samskivert/depot/clause/FieldOverride.java @@ -50,7 +50,7 @@ public class FieldOverride extends FieldDefinition super(field, override); } - public FieldOverride (ColumnExp field, SQLExpression override) + public FieldOverride (ColumnExp field, SQLExpression override) { super(field, override); } diff --git a/src/main/java/com/samskivert/depot/clause/Join.java b/src/main/java/com/samskivert/depot/clause/Join.java index 5672f68..39f0223 100644 --- a/src/main/java/com/samskivert/depot/clause/Join.java +++ b/src/main/java/com/samskivert/depot/clause/Join.java @@ -37,7 +37,7 @@ public class Join implements QueryClause /** Indicates the join type to be used. The default is INNER. */ public static enum Type { INNER, LEFT_OUTER, RIGHT_OUTER } - public Join (ColumnExp primary, ColumnExp join) + public Join (ColumnExp primary, ColumnExp join) { _joinClass = join.getPersistentClass(); _joinCondition = new Equals(primary, join); diff --git a/src/main/java/com/samskivert/depot/clause/SelectClause.java b/src/main/java/com/samskivert/depot/clause/SelectClause.java index 0891763..1509707 100644 --- a/src/main/java/com/samskivert/depot/clause/SelectClause.java +++ b/src/main/java/com/samskivert/depot/clause/SelectClause.java @@ -43,7 +43,7 @@ public class SelectClause * class for rows that match the supplied clauses. */ public SelectClause (Class pClass, - ColumnExp[] columns, QueryClause... clauses) + ColumnExp[] columns, QueryClause... clauses) { this(pClass, columns, Arrays.asList(clauses)); } @@ -52,7 +52,7 @@ public class SelectClause * Creates a new select clause, selecting the supplied columns from the specified persistent * class for rows that match the supplied clauses. */ - public SelectClause (Class pClass, ColumnExp[] columns, + public SelectClause (Class pClass, ColumnExp[] columns, Iterable clauses) { _pClass = pClass; @@ -133,7 +133,7 @@ public class SelectClause return _pClass; } - public ColumnExp[] getFields () + public ColumnExp[] getFields () { return _fields; } @@ -231,7 +231,7 @@ public class SelectClause protected Class _pClass; /** The persistent fields to select. */ - protected ColumnExp[] _fields; + protected ColumnExp[] _fields; /** The from override clause, if any. */ protected FromOverride _fromOverride; diff --git a/src/main/java/com/samskivert/depot/clause/Where.java b/src/main/java/com/samskivert/depot/clause/Where.java index 10df7bc..eeb0078 100644 --- a/src/main/java/com/samskivert/depot/clause/Where.java +++ b/src/main/java/com/samskivert/depot/clause/Where.java @@ -37,26 +37,28 @@ import com.samskivert.depot.impl.operator.IsNull; */ public class Where extends WhereClause { - public Where (ColumnExp column, Comparable value) + public > Where (ColumnExp column, V value) { - this(new ColumnExp[] { column }, new Comparable[] { value }); + this(new ColumnExp[] { column }, new Comparable[] { value }); } - public Where (ColumnExp index1, Comparable value1, - ColumnExp index2, Comparable value2) + public , V2 extends Comparable> Where ( + ColumnExp index1, V1 value1, ColumnExp index2, V2 value2) { - this(new ColumnExp[] { index1, index2 }, new Comparable[] { value1, value2 }); + this(new ColumnExp[] { index1, index2 }, new Comparable[] { value1, value2 }); } - public Where (ColumnExp index1, Comparable value1, - ColumnExp index2, Comparable value2, - ColumnExp index3, Comparable value3) + public , V2 extends Comparable, + V3 extends Comparable> + Where (ColumnExp index1, V1 value1, + ColumnExp index2, V2 value2, + ColumnExp index3, V3 value3) { - this(new ColumnExp[] { index1, index2, index3 }, + this(new ColumnExp[] { index1, index2, index3 }, new Comparable[] { value1, value2, value3 }); } - public Where (ColumnExp[] columns, Comparable[] values) + public Where (ColumnExp[] columns, Comparable[] values) { this(toCondition(columns, values)); } @@ -90,7 +92,7 @@ public class Where extends WhereClause return String.valueOf(_condition); } - protected static SQLExpression toCondition (ColumnExp[] columns, Comparable[] values) + protected static SQLExpression toCondition (ColumnExp[] columns, Comparable[] values) { SQLExpression[] comparisons = new SQLExpression[columns.length]; for (int ii = 0; ii < columns.length; ii ++) { diff --git a/src/main/java/com/samskivert/depot/expression/ColumnExp.java b/src/main/java/com/samskivert/depot/expression/ColumnExp.java index e35353b..0385262 100644 --- a/src/main/java/com/samskivert/depot/expression/ColumnExp.java +++ b/src/main/java/com/samskivert/depot/expression/ColumnExp.java @@ -30,7 +30,7 @@ import com.samskivert.depot.impl.FragmentVisitor; * An expression that unambiguously identifies a field of a class, for example * GameRecord.itemId. */ -public class ColumnExp extends FluentExp +public class ColumnExp extends FluentExp { /** The name of the column we reference. */ public final String name; @@ -47,13 +47,13 @@ public class ColumnExp extends FluentExp * expression. This is useful for "casting" a column expression from a parent class to a * derived class. */ - public ColumnExp as (Class oClass) + public ColumnExp as (Class oClass) { - return new ColumnExp(oClass, name); + return new ColumnExp(oClass, name); } /** Returns a {@link Join} on this column and the supplied target. */ - public Join join (ColumnExp join) + public Join join (ColumnExp join) { return new Join(this, join); } @@ -84,9 +84,9 @@ public class ColumnExp extends FluentExp @Override // from Object public boolean equals (Object other) { - return (other instanceof ColumnExp) && - ((ColumnExp)other)._pClass.equals(_pClass) && - ((ColumnExp)other).name.equals(this.name); + return (other instanceof ColumnExp) && + ((ColumnExp)other)._pClass.equals(_pClass) && + ((ColumnExp)other).name.equals(this.name); } @Override // from Object diff --git a/src/main/java/com/samskivert/depot/impl/BuildVisitor.java b/src/main/java/com/samskivert/depot/impl/BuildVisitor.java index 9ef6e86..b933673 100644 --- a/src/main/java/com/samskivert/depot/impl/BuildVisitor.java +++ b/src/main/java/com/samskivert/depot/impl/BuildVisitor.java @@ -147,7 +147,7 @@ public abstract class BuildVisitor implements FragmentVisitor public Void visit (Key.Expression key) { Class pClass = key.getPersistentClass(); - ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass); + ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass); Comparable[] values = key.getValues(); for (int ii = 0; ii < keyFields.length; ii ++) { if (ii > 0) { @@ -241,7 +241,7 @@ public abstract class BuildVisitor implements FragmentVisitor return null; } - public Void visit (ColumnExp columnExp) + public Void visit (ColumnExp columnExp) { appendRhsColumn(columnExp.getPersistentClass(), columnExp); return null; @@ -373,7 +373,7 @@ public abstract class BuildVisitor implements FragmentVisitor // while expanding column names in the SELECT query, do aliasing and expansion _enableAliasing = _enableOverrides = true; - for (ColumnExp field : selectClause.getFields()) { + for (ColumnExp field : selectClause.getFields()) { // write column to a temporary buffer StringBuilder saved = _builder; _builder = new StringBuilder(); @@ -457,7 +457,7 @@ public abstract class BuildVisitor implements FragmentVisitor appendTableAbbreviation(pClass); _builder.append(" set "); - ColumnExp[] fields = updateClause.getFields(); + ColumnExp[] fields = updateClause.getFields(); Object pojo = updateClause.getPojo(); SQLExpression[] values = updateClause.getValues(); for (int ii = 0; ii < fields.length; ii ++) { @@ -503,7 +503,7 @@ public abstract class BuildVisitor implements FragmentVisitor { if (!_allowComplexIndices) { for (Tuple field : createIndexClause.getFields()) { - if (!(field.left instanceof ColumnExp)) { + if (!(field.left instanceof ColumnExp)) { log.warning("This database can't handle complex indexes. Aborting creation.", "ixName", createIndexClause.getName()); return null; @@ -757,7 +757,7 @@ public abstract class BuildVisitor implements FragmentVisitor } protected Void bindField ( - Class pClass, ColumnExp field, Object pojo) + Class pClass, ColumnExp field, Object pojo) { final DepotMarshaller marshaller = _types.getMarshaller(pClass); _bindables.add(newBindable(marshaller, field, pojo)); @@ -781,7 +781,7 @@ public abstract class BuildVisitor implements FragmentVisitor // equivalent of an lvalue; something that can appear to the left of an equals sign. // We do not prepend this identifier with a table abbreviation, nor do we expand // field overrides, shadowOf declarations, or the like: it is just a column name. - protected void appendLhsColumn (Class type, ColumnExp field) + protected void appendLhsColumn (Class type, ColumnExp field) { // TODO: nix type and use the class from the supplied ColumnExp DepotMarshaller dm = _types.getMarshaller(type); @@ -796,7 +796,7 @@ public abstract class BuildVisitor implements FragmentVisitor // Appends an expression for the given field on the given persistent record; this can // appear in a SELECT list, in WHERE clauses, etc, etc. - protected void appendRhsColumn (Class type, ColumnExp field) + protected void appendRhsColumn (Class type, ColumnExp field) { DepotMarshaller dm = _types.getMarshaller(type); if (dm == null) { @@ -903,11 +903,11 @@ public abstract class BuildVisitor implements FragmentVisitor Object pojo = insertClause.getPojo(); DepotMarshaller marsh = _types.getMarshaller(pClass); Set idFields = insertClause.getIdentityFields(); - ColumnExp[] fields = marsh.getColumnFieldNames(); + ColumnExp[] fields = marsh.getColumnFieldNames(); _builder.append("("); boolean comma = false; - for (ColumnExp field : fields) { + for (ColumnExp field : fields) { if (idFields.contains(field.name)) { continue; } @@ -920,7 +920,7 @@ public abstract class BuildVisitor implements FragmentVisitor _builder.append(") values ("); comma = false; - for (ColumnExp field : fields) { + for (ColumnExp field : fields) { if (idFields.contains(field.name)) { continue; } @@ -945,7 +945,7 @@ public abstract class BuildVisitor implements FragmentVisitor } protected static Bindable newBindable ( - final DepotMarshaller marshaller, final ColumnExp field, final Object pojo) + final DepotMarshaller marshaller, final ColumnExp field, final Object pojo) { return new Bindable() { public void doBind (Connection conn, PreparedStatement stmt, int argIx) diff --git a/src/main/java/com/samskivert/depot/impl/ColumnSet.java b/src/main/java/com/samskivert/depot/impl/ColumnSet.java new file mode 100644 index 0000000..4a77420 --- /dev/null +++ b/src/main/java/com/samskivert/depot/impl/ColumnSet.java @@ -0,0 +1,65 @@ +// +// $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.impl; + +import com.samskivert.depot.PersistentRecord; +import com.samskivert.depot.Tuple2; +import com.samskivert.depot.expression.ColumnExp; + +/** + * Contains a set of columns which are to be selected from a query result. + */ +public abstract class ColumnSet +{ + public static ColumnSet create ( + Class ptype, ColumnExp column) + { + return new ColumnSet(ptype, new ColumnExp[] { column }) { + public V createObject (Object[] results) { + @SuppressWarnings("unchecked") V result = (V)results[0]; + return result; + } + }; + } + + public static ColumnSet> create ( + Class ptype, ColumnExp col1, ColumnExp col2) + { + return new ColumnSet>(ptype, new ColumnExp[] { col1, col2 }) { + public Tuple2 createObject (Object[] results) { + @SuppressWarnings("unchecked") V1 r1 = (V1)results[0]; + @SuppressWarnings("unchecked") V2 r2 = (V2)results[1]; + return new Tuple2(r1, r2); + } + }; + } + + public final Class ptype; + public final ColumnExp[] columns; + + public abstract R createObject (Object[] results); + + protected ColumnSet (Class ptype, ColumnExp[] columns) + { + this.ptype = ptype; + this.columns = columns; + } +} diff --git a/src/main/java/com/samskivert/depot/impl/DepotMarshaller.java b/src/main/java/com/samskivert/depot/impl/DepotMarshaller.java index 4fd03a1..d6f35a4 100644 --- a/src/main/java/com/samskivert/depot/impl/DepotMarshaller.java +++ b/src/main/java/com/samskivert/depot/impl/DepotMarshaller.java @@ -76,7 +76,7 @@ import static com.samskivert.depot.Log.log; * Handles the marshalling and unmarshalling of persistent instances to JDBC primitives ({@link * PreparedStatement} and {@link ResultSet}). */ -public class DepotMarshaller +public class DepotMarshaller implements QueryMarshaller { /** The name of a private static field that must be defined for all persistent object classes. * It is used to handle schema migration. If automatic schema migration is not desired, define @@ -119,7 +119,7 @@ public class DepotMarshaller boolean seenIdentityGenerator = false; // introspect on the class and create marshallers and indices for persistent fields - List fields = Lists.newArrayList(); + List> fields = Lists.newArrayList(); ListMultimap> namedFieldIndices = ArrayListMultimap.create(); ListMultimap> uniqueNamedFieldIndices = @@ -146,7 +146,7 @@ public class DepotMarshaller FieldMarshaller fm = FieldMarshaller.createMarshaller(field); _fields.put(field.getName(), fm); - ColumnExp fieldColumn = new ColumnExp(_pClass, field.getName()); + ColumnExp fieldColumn = new ColumnExp(_pClass, field.getName()); fields.add(fieldColumn); // check to see if this is our primary key @@ -245,7 +245,7 @@ public class DepotMarshaller } // generate our full list of fields/columns for use in queries - _allFields = fields.toArray(new ColumnExp[fields.size()]); + _allFields = fields.toArray(new ColumnExp[fields.size()]); // now check for @Entity annotations on the entire superclass chain Class iterClass = pClass.asSubclass(PersistentRecord.class); @@ -254,7 +254,7 @@ public class DepotMarshaller if (entity != null) { // add any indices needed for uniqueness constraints for (UniqueConstraint constraint : entity.uniqueConstraints()) { - ColumnExp[] colExps = new ColumnExp[constraint.fields().length]; + ColumnExp[] colExps = new ColumnExp[constraint.fields().length]; int ii = 0; for (String field : constraint.fields()) { FieldMarshaller fm = _fields.get(field); @@ -262,7 +262,7 @@ public class DepotMarshaller throw new IllegalArgumentException( "Unknown unique constraint field: " + field); } - colExps[ii ++] = new ColumnExp(_pClass, field); + colExps[ii ++] = new ColumnExp(_pClass, field); } _indexes.add(buildIndex(constraint.name(), true, colExps)); } @@ -316,7 +316,7 @@ public class DepotMarshaller /** * Returns all the persistent fields of our class, in definition order. */ - public ColumnExp[] getFieldNames () + public ColumnExp[] getFieldNames () { return _allFields; } @@ -324,7 +324,7 @@ public class DepotMarshaller /** * Returns all the persistent fields that correspond to concrete table columns. */ - public ColumnExp[] getColumnFieldNames () + public ColumnExp[] getColumnFieldNames () { return _columnFields; } @@ -374,11 +374,11 @@ public class DepotMarshaller * Return the names of the columns that constitute the primary key of our associated persistent * record. */ - public ColumnExp[] getPrimaryKeyFields () + public ColumnExp[] getPrimaryKeyFields () { - ColumnExp[] pkcols = new ColumnExp[_pkColumns.size()]; + ColumnExp[] pkcols = new ColumnExp[_pkColumns.size()]; for (int ii = 0; ii < pkcols.length; ii ++) { - pkcols[ii] = new ColumnExp(_pClass, _pkColumns.get(ii).getField().getName()); + pkcols[ii] = new ColumnExp(_pClass, _pkColumns.get(ii).getField().getName()); } return pkcols; } @@ -533,10 +533,10 @@ public class DepotMarshaller // figure out the list of fields that correspond to actual table columns and generate the // SQL used to create and migrate our table (unless we're a computed entity) - _columnFields = new ColumnExp[_allFields.length]; + _columnFields = new ColumnExp[_allFields.length]; ColumnDefinition[] declarations = new ColumnDefinition[_allFields.length]; int jj = 0; - for (ColumnExp field : _allFields) { + for (ColumnExp field : _allFields) { FieldMarshaller fm = _fields.get(field.name); // include all persistent non-computed fields ColumnDefinition colDef = fm.getColumnDefinition(); @@ -778,7 +778,7 @@ public class DepotMarshaller Set preMigrateColumns = Sets.newHashSet(metaData.tableColumns); // add any missing columns - for (ColumnExp field : _columnFields) { + for (ColumnExp field : _columnFields) { final FieldMarshaller fmarsh = _fields.get(field.name); if (metaData.tableColumns.remove(fmarsh.getColumnName())) { continue; @@ -969,10 +969,10 @@ public class DepotMarshaller protected CreateIndexClause buildIndex (String name, boolean unique, Object config) { List> definition = Lists.newArrayList(); - if (config instanceof ColumnExp) { - definition.add(new Tuple((ColumnExp)config, Order.ASC)); - } else if (config instanceof ColumnExp[]) { - for (ColumnExp column : (ColumnExp[])config) { + if (config instanceof ColumnExp) { + definition.add(new Tuple((ColumnExp)config, Order.ASC)); + } else if (config instanceof ColumnExp[]) { + for (ColumnExp column : (ColumnExp[])config) { definition.add(new Tuple(column, Order.ASC)); } } else if (config instanceof SQLExpression) { @@ -994,7 +994,7 @@ public class DepotMarshaller } // translate an array of field names to an array of column names - protected String[] fieldsToColumns (ColumnExp[] fields) + protected String[] fieldsToColumns (ColumnExp[] fields) { String[] columns = new String[fields.length]; for (int ii = 0; ii < columns.length; ii ++) { @@ -1015,7 +1015,7 @@ public class DepotMarshaller TableMetaData meta, PersistenceContext ctx, SQLBuilder builder) throws DatabaseException { - for (ColumnExp field : _columnFields) { + for (ColumnExp field : _columnFields) { FieldMarshaller fmarsh = _fields.get(field.name); meta.tableColumns.remove(fmarsh.getColumnName()); } @@ -1135,10 +1135,10 @@ public class DepotMarshaller protected List> _pkColumns; /** The persisent fields of our object, in definition order. */ - protected ColumnExp[] _allFields; + protected ColumnExp[] _allFields; /** The fields of our object with directly corresponding table columns. */ - protected ColumnExp[] _columnFields; + protected ColumnExp[] _columnFields; /** The indexes defined for this record. */ protected List _indexes = Lists.newArrayList(); diff --git a/src/main/java/com/samskivert/depot/impl/DepotMigrationHistoryRecord.java b/src/main/java/com/samskivert/depot/impl/DepotMigrationHistoryRecord.java index 6307f4f..f205cd8 100644 --- a/src/main/java/com/samskivert/depot/impl/DepotMigrationHistoryRecord.java +++ b/src/main/java/com/samskivert/depot/impl/DepotMigrationHistoryRecord.java @@ -35,8 +35,8 @@ public class DepotMigrationHistoryRecord extends PersistentRecord { // AUTO-GENERATED: FIELDS START public static final Class _R = DepotMigrationHistoryRecord.class; - public static final ColumnExp IDENT = colexp(_R, "ident"); - public static final ColumnExp WHEN_COMPLETED = colexp(_R, "whenCompleted"); + public static final ColumnExp IDENT = colexp(_R, "ident"); + public static final ColumnExp WHEN_COMPLETED = colexp(_R, "whenCompleted"); // AUTO-GENERATED: FIELDS END /** Our schema version. Probably not likely to change. */ diff --git a/src/main/java/com/samskivert/depot/impl/DepotUtil.java b/src/main/java/com/samskivert/depot/impl/DepotUtil.java index b8b7b4c..140fa0e 100644 --- a/src/main/java/com/samskivert/depot/impl/DepotUtil.java +++ b/src/main/java/com/samskivert/depot/impl/DepotUtil.java @@ -41,7 +41,7 @@ public class DepotUtil * Returns an array containing the names of the primary key fields for the supplied persistent * class. The values are introspected and cached for the lifetime of the VM. */ - public static ColumnExp[] getKeyFields (Class pClass) + public static ColumnExp[] getKeyFields (Class pClass) { return _keyFields.get(pClass); } @@ -52,7 +52,7 @@ public class DepotUtil * subclasses. Calling this method after the key fields have already been registered will * have no effect. */ - public static void registerKeyFields (ColumnExp... fields) + public static void registerKeyFields (ColumnExp... fields) { // TODO: Checks? For example: Validate all exps from same class? // Make a defensive copy of the array? Hide this method from public consumption? @@ -69,21 +69,21 @@ public class DepotUtil /** A (never expiring) cache of primary key field names for all persistent classes (of which * there are merely dozens, so we don't need to worry about expiring). */ - protected static ConcurrentMap, ColumnExp[]> _keyFields = + protected static ConcurrentMap, ColumnExp[]> _keyFields = new MapMaker() // newly generated PersistentRecord classes will register their key fields via // registerKeyFields, which will return an ordering determined at genrecord time. // We fall back to computing the fields at runtime for older PersistentRecord classes. - .makeComputingMap(new Function, ColumnExp[]>() { - public ColumnExp[] apply (Class pClass) { - List kflist = Lists.newArrayList(); + .makeComputingMap(new Function, ColumnExp[]>() { + public ColumnExp[] apply (Class pClass) { + List> kflist = Lists.newArrayList(); for (Field field : pClass.getFields()) { // look for @Id fields if (field.getAnnotation(Id.class) != null) { - kflist.add(new ColumnExp(pClass, field.getName())); + kflist.add(new ColumnExp(pClass, field.getName())); } } - return kflist.toArray(new ColumnExp[kflist.size()]); + return kflist.toArray(new ColumnExp[kflist.size()]); } }); } diff --git a/src/main/java/com/samskivert/depot/impl/ExpressionEvaluator.java b/src/main/java/com/samskivert/depot/impl/ExpressionEvaluator.java index 25340a7..d9556b3 100644 --- a/src/main/java/com/samskivert/depot/impl/ExpressionEvaluator.java +++ b/src/main/java/com/samskivert/depot/impl/ExpressionEvaluator.java @@ -175,7 +175,7 @@ public class ExpressionEvaluator return null; } - public Object visit (ColumnExp columnExp) + public Object visit (ColumnExp columnExp) { Class pClass = columnExp.getPersistentClass(); if (pClass != _pClass) { @@ -186,7 +186,7 @@ public class ExpressionEvaluator Field field = pClass.getField(columnExp.name); if (field == null) { log.warning("Couldn't locate field on class", "field", columnExp.name, - "class", pClass); + "class", pClass); return new NoValue("Internal Error"); } return field.get(_pRec); @@ -241,7 +241,7 @@ public class ExpressionEvaluator return new NoValue("Column lookup on unknown persistent class: " + pClass); } - ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass); + ColumnExp[] keyFields = DepotUtil.getKeyFields(pClass); Comparable[] values = key.getValues(); for (int ii = 0; ii < keyFields.length; ii ++) { diff --git a/src/main/java/com/samskivert/depot/impl/FindAllQuery.java b/src/main/java/com/samskivert/depot/impl/FindAllQuery.java index 60f1b95..8d5ca5c 100644 --- a/src/main/java/com/samskivert/depot/impl/FindAllQuery.java +++ b/src/main/java/com/samskivert/depot/impl/FindAllQuery.java @@ -35,7 +35,6 @@ import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.common.collect.Maps; -import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.depot.CacheAdapter.CacheCategory; import com.samskivert.depot.DatabaseException; import com.samskivert.depot.DepotRepository.CacheStrategy; @@ -48,7 +47,9 @@ import com.samskivert.depot.Stats; import com.samskivert.depot.clause.FieldOverride; import com.samskivert.depot.clause.QueryClause; import com.samskivert.depot.clause.SelectClause; +import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.impl.operator.In; +import com.samskivert.jdbc.DatabaseLiaison; import static com.samskivert.depot.Log.log; @@ -56,13 +57,28 @@ import static com.samskivert.depot.Log.log; * This class implements the functionality required by {@link DepotRepository#findAll}: fetch * a collection of persistent objects using one of two included strategies. */ -public abstract class FindAllQuery extends Query> +public abstract class FindAllQuery + extends Query> { + /** + * A base class for queries that fetch a full record at a time. + */ + public static abstract class FullRecordQuery + extends FindAllQuery + { + protected FullRecordQuery (PersistenceContext ctx, Class type) { + super(type, ctx.getMarshaller(type), new CloningCloner()); + _dmarsh = ctx.getMarshaller(type); + } + + protected DepotMarshaller _dmarsh; + } + /** * The two-pass collection query implementation. See {@link DepotRepository#findAll} for * details. */ - public static class WithCache extends FindAllQuery + public static class WithCache extends FullRecordQuery { public WithCache (PersistenceContext ctx, Class type, Iterable clauses, CacheStrategy strategy) @@ -70,7 +86,7 @@ public abstract class FindAllQuery extends Query extends Query extends Query extends Query extends FindAllQuery + public static class WithKeys extends FullRecordQuery { public WithKeys (PersistenceContext ctx, Iterable> keys) throws DatabaseException @@ -197,7 +213,7 @@ public abstract class FindAllQuery extends Query extends FindAllQuery + public static class Explicitly extends FullRecordQuery { public Explicitly (PersistenceContext ctx, Class type, Iterable clauses, boolean cachedContents) @@ -205,10 +221,10 @@ public abstract class FindAllQuery extends Query extends Query extends Query + extends FindAllQuery + { + public ForColumns (PersistenceContext ctx, ColumnSet cset, + Iterable clauses) + throws DatabaseException + { + super(cset.ptype, new ColumnSetQueryMarshaller(ctx, cset), + new NonCloningCloner()); + _select = new SelectClause(cset.ptype, cset.columns, clauses); + } + + @Override // from Query + public List getCachedResult (PersistenceContext ctx) + { + return null; + } + + // from Query + public List invoke (PersistenceContext ctx, Connection conn, DatabaseLiaison liaison) + throws SQLException + { + SQLBuilder builder = ctx.getSQLBuilder(DepotTypes.getDepotTypes(ctx, _select)); + builder.newQuery(_select); + ResultSet rs = builder.prepare(conn).executeQuery(); + List result = Lists.newArrayList(); + while (rs.next()) { + result.add(_marsh.createObject(rs)); + } + return result; + } + + protected SelectClause _select; + } + + protected static class ColumnSetQueryMarshaller + implements QueryMarshaller + { + public ColumnSetQueryMarshaller (PersistenceContext ctx, ColumnSet cset) { + _cset = cset; + _cmarsh = ctx.getMarshaller(cset.ptype); + } + + public String getTableName () { + return _cmarsh.getTableName(); + } + + public ColumnExp[] getFieldNames () { + return _cset.columns; + } + + public Key getPrimaryKey (Object object) { + return _cmarsh.getPrimaryKey(object); + } + + public R createObject (ResultSet rs) throws SQLException { + Object[] data = new Object[_cset.columns.length]; + for (int ii = 0; ii < data.length; ii++) { + data[ii] = _cmarsh.getFieldMarshaller(_cset.columns[ii].name).getFromSet(rs); + } + return _cset.createObject(data); + } + + protected ColumnSet _cset; + protected DepotMarshaller _cmarsh; + } + // from Query public void updateStats (Stats stats) { @@ -258,21 +341,22 @@ public abstract class FindAllQuery extends Query type) + protected FindAllQuery (Class type, QueryMarshaller marsh, Cloner cloner) throws DatabaseException { _type = type; - _marsh = ctx.getMarshaller(type); + _marsh = marsh; + _cloner = cloner; } protected Set> loadFromCache (PersistenceContext ctx, Iterable> allKeys, - Map, T> entities) + Map, R> entities) { Set> fetchKeys = Sets.newHashSet(); for (Key key : allKeys) { - T value = ctx.cacheLookup(new KeyCacheKey(key)); + R value = ctx.cacheLookup(new KeyCacheKey(key)); if (value != null) { - @SuppressWarnings("unchecked") T newValue = (T) value.clone(); + R newValue = _cloner.clone(value); entities.put(key, newValue); continue; } @@ -286,13 +370,13 @@ public abstract class FindAllQuery extends Query resolve (Iterable> allKeys, Map, T> entities) + protected List resolve (Iterable> allKeys, Map, R> entities) { - List result = (allKeys instanceof Collection) - ? Lists.newArrayListWithCapacity(((Collection)allKeys).size()) - : Lists.newArrayList(); + List result = (allKeys instanceof Collection) + ? Lists.newArrayListWithCapacity(((Collection)allKeys).size()) + : Lists.newArrayList(); for (Key key : allKeys) { - T value = entities.get(key); + R value = entities.get(key); if (value != null) { result.add(value); } @@ -300,9 +384,9 @@ public abstract class FindAllQuery extends Query loadAndResolve (PersistenceContext ctx, Connection conn, - Iterable> allKeys, Set> fetchKeys, - Map, T> entities, String origStmt) + protected List loadAndResolve (PersistenceContext ctx, Connection conn, + Iterable> allKeys, Set> fetchKeys, + Map, R> entities, String origStmt) throws SQLException { if (PersistenceContext.CACHE_DEBUG && fetchKeys.size() > 0) { @@ -330,7 +414,7 @@ public abstract class FindAllQuery extends Query> keys, - Map, T> entities, String origStmt) + Map, R> entities, String origStmt) throws SQLException { SelectClause select = new SelectClause( @@ -341,12 +425,12 @@ public abstract class FindAllQuery extends Query key = _marsh.getPrimaryKey(obj); if (entities.put(key, obj) != null) { dups++; } - ctx.cacheStore(CacheCategory.RECORD, new KeyCacheKey(key), obj.clone()); + ctx.cacheStore(CacheCategory.RECORD, new KeyCacheKey(key), _cloner.clone(obj)); got.add(key); cnt++; } @@ -378,8 +462,27 @@ public abstract class FindAllQuery extends Query { + C clone (C object); + } + protected static class CloningCloner implements Cloner { + public C clone (C object) { + @SuppressWarnings("unchecked") C clone = (C) object.clone(); + return clone; + } + } + protected static class NonCloningCloner implements Cloner { + public C clone (C object) { + return object; + } + } + protected Class _type; - protected DepotMarshaller _marsh; + protected QueryMarshaller _marsh; + protected Cloner _cloner; protected int _cachedQueries, _uncachedQueries, _explicitQueries; protected int _cachedRecords, _uncachedRecords; } diff --git a/src/main/java/com/samskivert/depot/impl/FragmentVisitor.java b/src/main/java/com/samskivert/depot/impl/FragmentVisitor.java index 40c31ac..66d2d65 100644 --- a/src/main/java/com/samskivert/depot/impl/FragmentVisitor.java +++ b/src/main/java/com/samskivert/depot/impl/FragmentVisitor.java @@ -95,7 +95,7 @@ public interface FragmentVisitor public T visit (In in); public T visit (FullText.Match match); public T visit (FullText.Rank match); - public T visit (ColumnExp columnExp); + public T visit (ColumnExp columnExp); public T visit (Not not); public T visit (GroupBy groupBy); public T visit (ForUpdate forUpdate); diff --git a/src/main/java/com/samskivert/depot/impl/HSQLBuilder.java b/src/main/java/com/samskivert/depot/impl/HSQLBuilder.java index fff3c6a..daf70a9 100644 --- a/src/main/java/com/samskivert/depot/impl/HSQLBuilder.java +++ b/src/main/java/com/samskivert/depot/impl/HSQLBuilder.java @@ -76,7 +76,7 @@ public class HSQLBuilder for (String ftsWord : ftsWords) { // build comparisons between each word and column bits.add(new Like(new Lower( - new ColumnExp(pClass, field)), "%"+ftsWord+"%", true)); + new ColumnExp(pClass, field)), "%"+ftsWord+"%", true)); } } // then just OR them all together and we have our query diff --git a/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java b/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java index 425b66b..2199cea 100644 --- a/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java +++ b/src/main/java/com/samskivert/depot/impl/MySQLBuilder.java @@ -160,7 +160,7 @@ public class MySQLBuilder if (ii > 0) { _builder.append(", "); } - new ColumnExp(pClass, fields[ii]).accept(this); + new ColumnExp(pClass, fields[ii]).accept(this); } _builder.append(") against ("); bindValue(fullText.getQuery()); diff --git a/src/main/java/com/samskivert/depot/impl/PostgreSQLBuilder.java b/src/main/java/com/samskivert/depot/impl/PostgreSQLBuilder.java index a63e9a2..fdbbc78 100644 --- a/src/main/java/com/samskivert/depot/impl/PostgreSQLBuilder.java +++ b/src/main/java/com/samskivert/depot/impl/PostgreSQLBuilder.java @@ -138,7 +138,7 @@ public class PostgreSQLBuilder // see if we will be inserting any columns whatsoever Class pClass = insertClause.getPersistentClass(); Set idFields = insertClause.getIdentityFields(); - for (ColumnExp field : _types.getMarshaller(pClass).getColumnFieldNames()) { + for (ColumnExp field : _types.getMarshaller(pClass).getColumnFieldNames()) { if (!idFields.contains(field.name)) { // we found a field we're inserting, so call super and finish super.appendInsertColumns(insertClause); diff --git a/src/main/java/com/samskivert/depot/impl/QueryMarshaller.java b/src/main/java/com/samskivert/depot/impl/QueryMarshaller.java new file mode 100644 index 0000000..70ee56e --- /dev/null +++ b/src/main/java/com/samskivert/depot/impl/QueryMarshaller.java @@ -0,0 +1,55 @@ +// +// $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.impl; + +import java.sql.ResultSet; +import java.sql.SQLException; + +import com.samskivert.depot.Key; +import com.samskivert.depot.PersistentRecord; +import com.samskivert.depot.expression.ColumnExp; + +/** + * Marshalls the results for a query. + */ +public interface QueryMarshaller +{ + /** + * Returns the name of the table in which persistent instances of our class are stored. By + * default this is the classname of the persistent object without the package. + */ + String getTableName (); + + /** + * Returns the field names being selected for this query. + */ + ColumnExp[] getFieldNames (); + + /** + * Extracts the primary key from the supplied object. + */ + Key getPrimaryKey (Object object); + + /** + * Creates an instance of the query result from the supplied result set. + */ + R createObject (ResultSet rs) throws SQLException; +} diff --git a/src/main/java/com/samskivert/depot/impl/QueryResult.java b/src/main/java/com/samskivert/depot/impl/QueryResult.java new file mode 100644 index 0000000..9e16a18 --- /dev/null +++ b/src/main/java/com/samskivert/depot/impl/QueryResult.java @@ -0,0 +1,41 @@ +// +// $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.impl; + +import java.io.Serializable; + +/** + * A base class for all query results (persistent records, or subsets of persistent records in the + * form of tuples of varying arity). + */ +public abstract class QueryResult + implements Serializable, Cloneable +{ + @Override + public QueryResult clone () + { + try { + return (QueryResult) super.clone(); + } catch (CloneNotSupportedException cnse) { + throw new AssertionError(cnse); // this should never happen since we are Cloneable + } + } +} diff --git a/src/main/java/com/samskivert/depot/impl/clause/UpdateClause.java b/src/main/java/com/samskivert/depot/impl/clause/UpdateClause.java index b242b99..8f4fbba 100644 --- a/src/main/java/com/samskivert/depot/impl/clause/UpdateClause.java +++ b/src/main/java/com/samskivert/depot/impl/clause/UpdateClause.java @@ -37,7 +37,7 @@ public class UpdateClause implements QueryClause { public UpdateClause (Class pClass, WhereClause where, - ColumnExp[] fields, PersistentRecord pojo) + ColumnExp[] fields, PersistentRecord pojo) { _pClass = pClass; _where = where; @@ -47,7 +47,7 @@ public class UpdateClause } public UpdateClause (Class pClass, WhereClause where, - ColumnExp[] fields, SQLExpression[] values) + ColumnExp[] fields, SQLExpression[] values) { _pClass = pClass; _fields = fields; @@ -61,7 +61,7 @@ public class UpdateClause return _where; } - public ColumnExp[] getFields () + public ColumnExp[] getFields () { return _fields; } @@ -108,7 +108,7 @@ public class UpdateClause protected WhereClause _where; /** The persistent fields to update. */ - protected ColumnExp[] _fields; + protected ColumnExp[] _fields; /** The field values, or null. */ protected SQLExpression[] _values; diff --git a/src/main/java/com/samskivert/depot/tools/GenRecordTask.java b/src/main/java/com/samskivert/depot/tools/GenRecordTask.java index 104f570..de76b33 100644 --- a/src/main/java/com/samskivert/depot/tools/GenRecordTask.java +++ b/src/main/java/com/samskivert/depot/tools/GenRecordTask.java @@ -44,6 +44,7 @@ import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.Reference; import org.apache.tools.ant.util.ClasspathUtils; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -276,6 +277,7 @@ public class GenRecordTask extends Task // create our substitution mappings Map fsubs = Maps.newHashMap(subs); + fsubs.put("type", getTypeName(f.getType())); fsubs.put("field", fname); fsubs.put("capfield", StringUtil.unStudlyName(fname).toUpperCase()); @@ -458,6 +460,17 @@ public class GenRecordTask extends Task return name; } + protected static String getTypeName (Class clazz) + { + Class nclass = clazz.isPrimitive() ? BOXES.get(clazz) : clazz; + Class eclass = nclass.getEnclosingClass(); + if (eclass != null) { + return getTypeName(eclass) + "." + nclass.getSimpleName(); + } else { + return nclass.getSimpleName(); + } + } + /** A list of filesets that contain tile images. */ protected List _filesets = Lists.newArrayList(); @@ -490,4 +503,15 @@ public class GenRecordTask extends Task /** A regular expression for matching the class or interface declaration. */ protected static final Pattern NAME_PATTERN = Pattern.compile( "^\\s*public\\s+(?:abstract\\s+)?(interface|class)\\s+(\\w+)(\\W|$)"); + + protected static final Map, Class> BOXES = + ImmutableMap.,Class>builder(). + put(Boolean.TYPE, Boolean.class). + put(Byte.TYPE, Byte.class). + put(Short.TYPE, Short.class). + put(Character.TYPE, Character.class). + put(Integer.TYPE, Integer.class). + put(Long.TYPE, Long.class). + put(Float.TYPE, Float.class). + put(Double.TYPE, Double.class).build(); } diff --git a/src/main/resources/com/samskivert/depot/tools/record_column.tmpl b/src/main/resources/com/samskivert/depot/tools/record_column.tmpl index 36a2cf7..ff0d82f 100644 --- a/src/main/resources/com/samskivert/depot/tools/record_column.tmpl +++ b/src/main/resources/com/samskivert/depot/tools/record_column.tmpl @@ -1 +1 @@ - public static final ColumnExp @capfield@ = colexp(_R, "@field@"); + public static final ColumnExp<@type@> @capfield@ = colexp(_R, "@field@"); diff --git a/src/test/java/com/samskivert/depot/AllTypesRecord.java b/src/test/java/com/samskivert/depot/AllTypesRecord.java index 260b2bc..dcee08c 100644 --- a/src/test/java/com/samskivert/depot/AllTypesRecord.java +++ b/src/test/java/com/samskivert/depot/AllTypesRecord.java @@ -36,42 +36,42 @@ public class AllTypesRecord extends PersistentRecord { // AUTO-GENERATED: FIELDS START public static final Class _R = AllTypesRecord.class; - public static final ColumnExp RECORD_ID = colexp(_R, "recordId"); - public static final ColumnExp BOOLEAN_VALUE = colexp(_R, "booleanValue"); - public static final ColumnExp BYTE_VALUE = colexp(_R, "byteValue"); - public static final ColumnExp SHORT_VALUE = colexp(_R, "shortValue"); - public static final ColumnExp INT_VALUE = colexp(_R, "intValue"); - public static final ColumnExp LONG_VALUE = colexp(_R, "longValue"); - public static final ColumnExp FLOAT_VALUE = colexp(_R, "floatValue"); - public static final ColumnExp DOUBLE_VALUE = colexp(_R, "doubleValue"); - public static final ColumnExp BOXED_BOOLEAN = colexp(_R, "boxedBoolean"); - public static final ColumnExp BOXED_BYTE = colexp(_R, "boxedByte"); - public static final ColumnExp BOXED_SHORT = colexp(_R, "boxedShort"); - public static final ColumnExp BOXED_INT = colexp(_R, "boxedInt"); - public static final ColumnExp BOXED_LONG = colexp(_R, "boxedLong"); - public static final ColumnExp BOXED_FLOAT = colexp(_R, "boxedFloat"); - public static final ColumnExp BOXED_DOUBLE = colexp(_R, "boxedDouble"); - public static final ColumnExp BYTE_ARRAY = colexp(_R, "byteArray"); - public static final ColumnExp INT_ARRAY = colexp(_R, "intArray"); - public static final ColumnExp STRING = colexp(_R, "string"); - public static final ColumnExp DATE = colexp(_R, "date"); - public static final ColumnExp TIME = colexp(_R, "time"); - public static final ColumnExp TIMESTAMP = colexp(_R, "timestamp"); - public static final ColumnExp TEST_ENUM = colexp(_R, "testEnum"); - public static final ColumnExp NULL_BOXED_BOOLEAN = colexp(_R, "nullBoxedBoolean"); - public static final ColumnExp NULL_BOXED_BYTE = colexp(_R, "nullBoxedByte"); - public static final ColumnExp NULL_BOXED_SHORT = colexp(_R, "nullBoxedShort"); - public static final ColumnExp NULL_BOXED_INT = colexp(_R, "nullBoxedInt"); - public static final ColumnExp NULL_BOXED_LONG = colexp(_R, "nullBoxedLong"); - public static final ColumnExp NULL_BOXED_FLOAT = colexp(_R, "nullBoxedFloat"); - public static final ColumnExp NULL_BOXED_DOUBLE = colexp(_R, "nullBoxedDouble"); - public static final ColumnExp NULL_BYTE_ARRAY = colexp(_R, "nullByteArray"); - public static final ColumnExp NULL_INT_ARRAY = colexp(_R, "nullIntArray"); - public static final ColumnExp NULL_STRING = colexp(_R, "nullString"); - public static final ColumnExp NULL_DATE = colexp(_R, "nullDate"); - public static final ColumnExp NULL_TIME = colexp(_R, "nullTime"); - public static final ColumnExp NULL_TIMESTAMP = colexp(_R, "nullTimestamp"); - public static final ColumnExp NULL_TEST_ENUM = colexp(_R, "nullTestEnum"); + public static final ColumnExp RECORD_ID = colexp(_R, "recordId"); + public static final ColumnExp BOOLEAN_VALUE = colexp(_R, "booleanValue"); + public static final ColumnExp BYTE_VALUE = colexp(_R, "byteValue"); + public static final ColumnExp SHORT_VALUE = colexp(_R, "shortValue"); + public static final ColumnExp INT_VALUE = colexp(_R, "intValue"); + public static final ColumnExp LONG_VALUE = colexp(_R, "longValue"); + public static final ColumnExp FLOAT_VALUE = colexp(_R, "floatValue"); + public static final ColumnExp DOUBLE_VALUE = colexp(_R, "doubleValue"); + public static final ColumnExp BOXED_BOOLEAN = colexp(_R, "boxedBoolean"); + public static final ColumnExp BOXED_BYTE = colexp(_R, "boxedByte"); + public static final ColumnExp BOXED_SHORT = colexp(_R, "boxedShort"); + public static final ColumnExp BOXED_INT = colexp(_R, "boxedInt"); + public static final ColumnExp BOXED_LONG = colexp(_R, "boxedLong"); + public static final ColumnExp BOXED_FLOAT = colexp(_R, "boxedFloat"); + public static final ColumnExp BOXED_DOUBLE = colexp(_R, "boxedDouble"); + public static final ColumnExp BYTE_ARRAY = colexp(_R, "byteArray"); + public static final ColumnExp INT_ARRAY = colexp(_R, "intArray"); + public static final ColumnExp STRING = colexp(_R, "string"); + public static final ColumnExp DATE = colexp(_R, "date"); + public static final ColumnExp