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