From a9c992bb44aea227d8dab1e1ff6341a955bff42e Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Thu, 8 Jan 2009 02:10:54 +0000 Subject: [PATCH] Great big revampage: no more strings anywhere. It's all ColumnExps all the way down (well, all the way to the public API anyway). Revamped index creation while I was in there because that was one of the big string users. Now you just put @Index on the field you want indexed, and if you want a multi-column index you do things with a magical static method just like we do for complex (function) indices. @UniqueConstraint may still go away since it's basically exactly the same thing as @Index(unique=true), so it's kind of pointless to support both. --- etc/libs-incl.xml | 1 + .../com/samskivert/depot/DepotRepository.java | 70 +++--- src/java/com/samskivert/depot/Key.java | 35 +-- src/java/com/samskivert/depot/KeySet.java | 4 +- .../samskivert/depot/PersistentRecord.java | 15 ++ .../com/samskivert/depot/SchemaMigration.java | 14 +- .../samskivert/depot/annotation/Index.java | 26 +-- .../depot/annotation/UniqueConstraint.java | 13 +- .../depot/clause/CreateIndexClause.java | 14 +- .../samskivert/depot/clause/DeleteClause.java | 2 +- .../depot/clause/FieldDefinition.java | 6 + .../samskivert/depot/clause/InsertClause.java | 6 +- .../com/samskivert/depot/clause/Join.java | 7 - .../samskivert/depot/clause/UpdateClause.java | 1 + .../depot/expression/ColumnExp.java | 22 +- .../samskivert/depot/impl/BindVisitor.java | 6 +- .../samskivert/depot/impl/BuildVisitor.java | 6 +- .../depot/impl/DepotMarshaller.java | 201 +++++++----------- .../impl/DepotMigrationHistoryRecord.java | 18 +- .../depot/impl/ExpressionVisitor.java | 6 +- .../samskivert/depot/impl/HSQLBuilder.java | 4 +- .../samskivert/depot/impl/MySQLBuilder.java | 6 +- .../depot/operator/Conditionals.java | 23 +- .../samskivert/depot/tests/TestRecord.java | 61 +----- .../depot/tests/TestRepository.java | 4 +- .../samskivert/depot/tools/GenRecordTask.java | 36 ++-- .../samskivert/depot/tools/record_column.tmpl | 4 +- .../samskivert/depot/tools/record_key.tmpl | 2 +- .../samskivert/depot/tools/record_name.tmpl | 2 - .../samskivert/depot/tools/record_proto.tmpl | 1 + 30 files changed, 260 insertions(+), 356 deletions(-) delete mode 100644 src/java/com/samskivert/depot/tools/record_name.tmpl create mode 100644 src/java/com/samskivert/depot/tools/record_proto.tmpl diff --git a/etc/libs-incl.xml b/etc/libs-incl.xml index 288d954..25d58b1 100644 --- a/etc/libs-incl.xml +++ b/etc/libs-incl.xml @@ -3,6 +3,7 @@ + diff --git a/src/java/com/samskivert/depot/DepotRepository.java b/src/java/com/samskivert/depot/DepotRepository.java index f9ec34d..a43cac4 100644 --- a/src/java/com/samskivert/depot/DepotRepository.java +++ b/src/java/com/samskivert/depot/DepotRepository.java @@ -47,6 +47,7 @@ import com.samskivert.depot.clause.InsertClause; import com.samskivert.depot.clause.QueryClause; import com.samskivert.depot.clause.UpdateClause; import com.samskivert.depot.clause.WhereClause; +import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.expression.SQLExpression; import com.samskivert.depot.expression.ValueExp; import com.samskivert.depot.impl.DepotMarshaller; @@ -55,8 +56,8 @@ import com.samskivert.depot.impl.DepotTypes; import com.samskivert.depot.impl.FindAllKeysQuery; import com.samskivert.depot.impl.FindAllQuery; import com.samskivert.depot.impl.FindOneQuery; -import com.samskivert.depot.impl.Modifier; import com.samskivert.depot.impl.Modifier.*; +import com.samskivert.depot.impl.Modifier; import com.samskivert.depot.impl.SQLBuilder; import static com.samskivert.depot.Log.log; @@ -208,7 +209,7 @@ public abstract class DepotRepository * * @throws DatabaseException if any problem is encountered communicating with the database. */ - protected T load (Class type, String ix, Comparable val, + protected T load (Class type, ColumnExp ix, Comparable val, QueryClause... clauses) throws DatabaseException { @@ -221,8 +222,8 @@ public abstract class DepotRepository * * @throws DatabaseException if any problem is encountered communicating with the database. */ - protected T load (Class type, String ix1, Comparable val1, - String ix2, Comparable val2, + protected T load (Class type, ColumnExp ix1, Comparable val1, + ColumnExp ix2, Comparable val2, QueryClause... clauses) throws DatabaseException { @@ -235,8 +236,8 @@ public abstract class DepotRepository * * @throws DatabaseException if any problem is encountered communicating with the database. */ - protected T load (Class type, String ix1, Comparable val1, - String ix2, Comparable val2, String ix3, + protected T load (Class type, ColumnExp ix1, Comparable val1, + ColumnExp ix2, Comparable val2, ColumnExp ix3, Comparable val3, QueryClause... clauses) throws DatabaseException { @@ -448,7 +449,7 @@ public abstract class DepotRepository updateKey(marsh.getPrimaryKey(_result, false)); } - builder.newQuery(new InsertClause(pClass, _result, identityFields)); + builder.newQuery(new InsertClause(pClass, _result, identityFields)); PreparedStatement stmt = builder.prepare(conn); try { @@ -515,7 +516,7 @@ public abstract class DepotRepository * * @throws DatabaseException if any problem is encountered communicating with the database. */ - protected int update (T record, final String... modifiedFields) + protected int update (T record, final ColumnExp... modifiedFields) throws DatabaseException { @SuppressWarnings("unchecked") Class pClass = (Class) record.getClass(); @@ -527,7 +528,8 @@ public abstract class DepotRepository throw new IllegalArgumentException("Can't update record with null primary key."); } - UpdateClause update = new UpdateClause(pClass, key, modifiedFields, record); + UpdateClause update = new UpdateClause( + pClass, key, ColumnExp.toNames(modifiedFields), record); final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, update)); builder.newQuery(update); @@ -551,7 +553,7 @@ public abstract class DepotRepository * * @param type the type of the persistent object to be modified. * @param primaryKey the primary key to match in the update. - * @param updates an mapping from the names of the fields/columns ti the values to be assigned. + * @param updates an mapping from the columns to the values to be assigned. * * @return the number of rows modified by this action. * @@ -560,12 +562,12 @@ public abstract class DepotRepository * @throws DatabaseException if any problem is encountered communicating with the database. */ protected int updatePartial ( - Class type, Comparable primaryKey, Map updates) + Class type, Comparable primaryKey, Map updates) throws DatabaseException { Object[] fieldsValues = new Object[updates.size()*2]; int idx = 0; - for (Map.Entry entry : updates.entrySet()) { + for (Map.Entry entry : updates.entrySet()) { fieldsValues[idx++] = entry.getKey(); fieldsValues[idx++] = entry.getValue(); } @@ -577,7 +579,7 @@ public abstract class DepotRepository * * @param type the type of the persistent object to be modified. * @param primaryKey the primary key to match in the update. - * @param fieldsValues an array containing the names of the fields/columns and the values to be + * @param fieldsValues an array containing the columns (as ColumnExp) and the values to be * assigned, in key, value, key, value, etc. order. * * @return the number of rows modified by this action. @@ -598,7 +600,7 @@ public abstract class DepotRepository * primary key. * * @param type the type of the persistent object to be modified. - * @param fieldsValues an array containing the names of the fields/columns and the values to be + * @param fieldsValues an array containing the columns (as ColumnExp) and the values to be * assigned, in key, value, key, value, etc. order. * * @return the number of rows modified by this action. @@ -608,7 +610,7 @@ public abstract class DepotRepository * @throws DatabaseException if any problem is encountered communicating with the database. */ protected int updatePartial ( - Class type, String ix1, Comparable val1, String ix2, Comparable val2, + Class type, ColumnExp ix1, Comparable val1, ColumnExp ix2, Comparable val2, Object... fieldsValues) throws DatabaseException { @@ -620,7 +622,7 @@ public abstract class DepotRepository * primary key. * * @param type the type of the persistent object to be modified. - * @param fieldsValues an array containing the names of the fields/columns and the values to be + * @param fieldsValues an array containing the columns (as ColumnExp) and the values to be * assigned, in key, value, key, value, etc. order. * * @return the number of rows modified by this action. @@ -630,8 +632,8 @@ public abstract class DepotRepository * @throws DatabaseException if any problem is encountered communicating with the database. */ protected int updatePartial ( - Class type, String ix1, Comparable val1, String ix2, Comparable val2, - String ix3, Comparable val3, Object... fieldsValues) + Class type, ColumnExp ix1, Comparable val1, ColumnExp ix2, Comparable val2, + ColumnExp ix3, Comparable val3, Object... fieldsValues) throws DatabaseException { return updatePartial(new Key(type, ix1, val1, ix2, val2, ix3, val3), fieldsValues); @@ -641,7 +643,7 @@ public abstract class DepotRepository * Updates the specified columns for all persistent objects matching the supplied key. * * @param key the key for the persistent objects to be modified. - * @param fieldsValues an array containing the names of the fields/columns and the values to be + * @param fieldsValues an array containing the columns (as ColumnExp) and the values to be * assigned, in key, value, key, value, etc. order. * * @return the number of rows modified by this action. @@ -666,7 +668,7 @@ public abstract class DepotRepository * @param key the key to match in the update. * @param invalidator a cache invalidator that will be run prior to the update to flush the * relevant persistent objects from the cache, or null if no invalidation is needed. - * @param fieldsValues an array containing the names of the fields/columns and the values to be + * @param fieldsValues an array containing the columns (as ColumnExp) and the values to be * assigned, in key, value, key, value, etc. order. * * @return the number of rows modified by this action. @@ -688,7 +690,7 @@ public abstract class DepotRepository final String[] fields = new String[fieldsValues.length/2]; final SQLExpression[] values = new SQLExpression[fields.length]; for (int ii = 0, idx = 0; ii < fields.length; ii++) { - fields[ii] = (String)fieldsValues[idx++]; + fields[ii] = ((ColumnExp)fieldsValues[idx++]).name; values[ii] = new ValueExp(fieldsValues[idx++]); } UpdateClause update = new UpdateClause(type, key, fields, values); @@ -720,8 +722,7 @@ public abstract class DepotRepository * * @param type the type of the persistent object to be modified. * @param primaryKey the key to match in the update. - * @param fieldsValues an array containing the names of the fields/columns and the values to be - * assigned, in key, literal value, key, literal value, etc. order. + * @param fieldsValues a map containing the columns and the values to be assigned. * * @return the number of rows modified by this action. * @@ -730,7 +731,7 @@ public abstract class DepotRepository * @throws DatabaseException if any problem is encountered communicating with the database. */ protected int updateLiteral ( - Class type, Comparable primaryKey, Map fieldsValues) + Class type, Comparable primaryKey, Map fieldsValues) throws DatabaseException { Key key = _ctx.getMarshaller(type).makePrimaryKey(primaryKey); @@ -748,8 +749,7 @@ public abstract class DepotRepository * * * @param type the type of the persistent object to be modified. - * @param fieldsValues an array containing the names of the fields/columns and the values to be - * assigned, in key, literal value, key, literal value, etc. order. + * @param fieldsValues a map containing the columns and the values to be assigned. * * @return the number of rows modified by this action. * @@ -758,8 +758,8 @@ public abstract class DepotRepository * @throws DatabaseException if any problem is encountered communicating with the database. */ protected int updateLiteral ( - Class type, String ix1, Comparable val1, String ix2, Comparable val2, - Map fieldsValues) + Class type, ColumnExp ix1, Comparable val1, ColumnExp ix2, Comparable val2, + Map fieldsValues) throws DatabaseException { Key key = new Key(type, ix1, val1, ix2, val2); @@ -787,8 +787,8 @@ public abstract class DepotRepository * @throws DatabaseException if any problem is encountered communicating with the database. */ protected int updateLiteral ( - Class type, String ix1, Comparable val1, String ix2, Comparable val2, - String ix3, Comparable val3, Map fieldsValues) + Class type, ColumnExp ix1, Comparable val1, ColumnExp ix2, Comparable val2, + ColumnExp ix3, Comparable val3, Map fieldsValues) throws DatabaseException { Key key = new Key(type, ix1, val1, ix2, val2, ix3, val3); @@ -818,7 +818,7 @@ public abstract class DepotRepository */ protected int updateLiteral ( Class type, final WhereClause key, CacheInvalidator invalidator, - Map fieldsValues) + Map fieldsValues) throws DatabaseException { requireNotComputed(type, "updateLiteral"); @@ -832,8 +832,8 @@ public abstract class DepotRepository final String[] fields = new String[fieldsValues.size()]; final SQLExpression[] values = new SQLExpression[fields.length]; int ii = 0; - for (Map.Entry entry : fieldsValues.entrySet()) { - fields[ii] = entry.getKey(); + for (Map.Entry entry : fieldsValues.entrySet()) { + fields[ii] = entry.getKey().name; values[ii] = entry.getValue(); ii ++; } @@ -907,7 +907,7 @@ public abstract class DepotRepository updateKey(marsh.getPrimaryKey(_result, false)); } - builder.newQuery(new InsertClause(pClass, _result, identityFields)); + builder.newQuery(new InsertClause(pClass, _result, identityFields)); stmt = builder.prepare(conn); int mods = stmt.executeUpdate(); @@ -1012,7 +1012,7 @@ public abstract class DepotRepository } where.validateQueryType(type); // and another - DeleteClause delete = new DeleteClause(type, where); + DeleteClause delete = new DeleteClause(type, where); final SQLBuilder builder = _ctx.getSQLBuilder(DepotTypes.getDepotTypes(_ctx, delete)); builder.newQuery(delete); diff --git a/src/java/com/samskivert/depot/Key.java b/src/java/com/samskivert/depot/Key.java index 190fe81..27ff295 100644 --- a/src/java/com/samskivert/depot/Key.java +++ b/src/java/com/samskivert/depot/Key.java @@ -28,6 +28,7 @@ import java.util.Map; import com.google.common.collect.Maps; import com.samskivert.depot.clause.WhereClause; +import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.expression.SQLExpression; import com.samskivert.depot.impl.DepotMarshaller; import com.samskivert.depot.impl.DepotUtil; @@ -71,33 +72,42 @@ public class Key extends WhereClause /** * Constructs a new single-column {@code Key} with the given value. */ - public Key (Class pClass, String ix, Comparable val) + public Key (Class pClass, ColumnExp ix, Comparable val) { - this(pClass, new String[] { ix }, new Comparable[] { val }); + this(pClass, new ColumnExp[] { ix }, new Comparable[] { val }); } /** * Constructs a new two-column {@code Key} with the given values. */ - public Key (Class pClass, String ix1, Comparable val1, - String ix2, Comparable val2) + public Key (Class pClass, ColumnExp ix1, Comparable val1, + ColumnExp ix2, Comparable val2) { - this(pClass, new String[] { ix1, ix2 }, new Comparable[] { val1, val2 }); + this(pClass, new ColumnExp[] { ix1, ix2 }, new Comparable[] { val1, val2 }); } /** * Constructs a new three-column {@code Key} with the given values. */ - public Key (Class pClass, String ix1, Comparable val1, - String ix2, Comparable val2, String ix3, Comparable val3) + public Key (Class pClass, ColumnExp ix1, Comparable val1, + ColumnExp ix2, Comparable val2, ColumnExp ix3, Comparable val3) { - this(pClass, new String[] { ix1, ix2, ix3 }, new Comparable[] { val1, val2, val3 }); + this(pClass, new ColumnExp[] { ix1, ix2, ix3 }, new Comparable[] { val1, val2, val3 }); + } + + /** + * TEMP: legacy foo bar! + */ + public Key (Class pClass, String[] fields, Comparable[] values) + { + _pClass = pClass; + _values = values; } /** * Constructs a new multi-column {@code Key} with the given values. */ - public Key (Class pClass, String[] fields, Comparable[] values) + public Key (Class pClass, ColumnExp[] fields, Comparable[] values) { if (fields.length != values.length) { throw new IllegalArgumentException("Field and Value arrays must be of equal length."); @@ -109,7 +119,7 @@ public class Key extends WhereClause // build a local map of field name -> field value Map> map = Maps.newHashMap(); for (int i = 0; i < fields.length; i ++) { - map.put(fields[i], values[i]); + map.put(fields[i].name, values[i]); } // look up the cached primary key fields for this object @@ -138,9 +148,10 @@ public class Key extends WhereClause } /** - * Used to create a key when you know you have the canonical values array. + * Used to create a key when you know you have the canonical values array. Don't call this + * unless you know what you're doing! */ - protected Key (Class pClass, Comparable[] values) + public Key (Class pClass, Comparable[] values) { _pClass = pClass; _values = values; diff --git a/src/java/com/samskivert/depot/KeySet.java b/src/java/com/samskivert/depot/KeySet.java index 85e4a87..0a3bc6f 100644 --- a/src/java/com/samskivert/depot/KeySet.java +++ b/src/java/com/samskivert/depot/KeySet.java @@ -33,6 +33,7 @@ import com.google.common.base.Function; import com.samskivert.util.StringUtil; import com.samskivert.depot.clause.WhereClause; +import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.expression.LiteralExp; import com.samskivert.depot.expression.SQLExpression; import com.samskivert.depot.impl.DepotUtil; @@ -171,7 +172,8 @@ public abstract class KeySet extends WhereClause // from WhereClause public SQLExpression getWhereExpression () { // Single-column keys result in the compact IN(keyVal1, keyVal2, ...) - return new Conditionals.In(_pClass, DepotUtil.getKeyFields(_pClass)[0], _keys); + ColumnExp column = new ColumnExp(_pClass, DepotUtil.getKeyFields(_pClass)[0]); + return new Conditionals.In(column, _keys); } // from Iterable> diff --git a/src/java/com/samskivert/depot/PersistentRecord.java b/src/java/com/samskivert/depot/PersistentRecord.java index 0341943..7f87378 100644 --- a/src/java/com/samskivert/depot/PersistentRecord.java +++ b/src/java/com/samskivert/depot/PersistentRecord.java @@ -22,6 +22,8 @@ package com.samskivert.depot; import java.io.Serializable; +import com.samskivert.depot.expression.ColumnExp; + /** * 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. @@ -29,6 +31,10 @@ import java.io.Serializable; public class PersistentRecord implements Cloneable, Serializable { + // AUTO-GENERATED: FIELDS START + public static final Class _R = PersistentRecord.class; + // AUTO-GENERATED: FIELDS END + @Override // from Object public PersistentRecord clone () { @@ -38,4 +44,13 @@ public class PersistentRecord throw new RuntimeException(cnse); // this should never happen } } + + /** + * 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) + { + return new ColumnExp(clazz, fieldName); + } } diff --git a/src/java/com/samskivert/depot/SchemaMigration.java b/src/java/com/samskivert/depot/SchemaMigration.java index de0062a..6dd8fa3 100644 --- a/src/java/com/samskivert/depot/SchemaMigration.java +++ b/src/java/com/samskivert/depot/SchemaMigration.java @@ -26,7 +26,9 @@ import java.util.Map; import com.samskivert.jdbc.ColumnDefinition; import com.samskivert.jdbc.DatabaseLiaison; + import com.samskivert.depot.annotation.Column; +import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.impl.FieldMarshaller; import com.samskivert.depot.impl.Modifier; @@ -74,10 +76,10 @@ public abstract class SchemaMigration extends Modifier */ public static class Rename extends SchemaMigration { - public Rename (int targetVersion, String oldColumnName, String newColumnName) { + public Rename (int targetVersion, String oldColumnName, ColumnExp newColumn) { super(targetVersion); _oldColumnName = oldColumnName; - _newColumnName = newColumnName; + _newColumnName = newColumn.name; } @Override public boolean runBeforeDefault () { @@ -132,9 +134,9 @@ public abstract class SchemaMigration extends Modifier */ public static class Retype extends SchemaMigration { - public Retype (int targetVersion, String fieldName) { + public Retype (int targetVersion, ColumnExp column) { super(targetVersion); - _fieldName = fieldName; + _fieldName = column.name; } @Override public boolean runBeforeDefault () { @@ -170,9 +172,9 @@ public abstract class SchemaMigration extends Modifier */ public static class Add extends SchemaMigration { - public Add (int targetVersion, String fieldName, String defaultValue) { + public Add (int targetVersion, ColumnExp column, String defaultValue) { super(targetVersion); - _fieldName = fieldName; + _fieldName = column.name; _defaultValue = defaultValue; } diff --git a/src/java/com/samskivert/depot/annotation/Index.java b/src/java/com/samskivert/depot/annotation/Index.java index 17ec791..e5811d4 100644 --- a/src/java/com/samskivert/depot/annotation/Index.java +++ b/src/java/com/samskivert/depot/annotation/Index.java @@ -20,6 +20,7 @@ package com.samskivert.depot.annotation; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -27,23 +28,22 @@ import java.lang.annotation.Target; /** * Defines an index on an entity table. */ -@Target(value={}) +@Target(value=ElementType.FIELD) @Retention(value=RetentionPolicy.RUNTIME) public @interface Index { - /** Defines the name of the index. */ - String name (); + /** Defines the name of the 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 List> indexName ()
+     * 
+ * The first form will result in a simple multicolum index being created with the supplied + * columns. The second will create a function index using the supplied SQLExpressions. + */ + String name () default ""; /** Does this index enforce a uniqueness constraint? */ boolean unique () default false; - - /** Defines the fields on which the index operates. */ - String[] fields () default {}; - - /** Whether or not this is a complex index. If true, a static method - * must be defined on the record that declares this index of the signature: - *
public static List> indexNameExpression ()
- * which should return the index's defining expressions and whether each one is ascending - * or descending. */ - boolean complex () default false; } diff --git a/src/java/com/samskivert/depot/annotation/UniqueConstraint.java b/src/java/com/samskivert/depot/annotation/UniqueConstraint.java index 9b65690..a005dab 100644 --- a/src/java/com/samskivert/depot/annotation/UniqueConstraint.java +++ b/src/java/com/samskivert/depot/annotation/UniqueConstraint.java @@ -25,15 +25,16 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** - * This annotation is used to specify that a unique constraint is to be included in the - * generated DDL for a table. + * Used to specify a uniqueness constraint on a set of columns. If you want a single column to be + * unique, simply use {@link Column#unique}. */ @Target(value={}) @Retention(value=RetentionPolicy.RUNTIME) public @interface UniqueConstraint { - /** - * An array of the field names that make up the constraint - */ - public String[] fieldNames () default {}; + /** The name of the index that will be created to enforce this constraint. */ + String name (); + + /** An array of the field names that make up the constraint */ + String[] fields (); } diff --git a/src/java/com/samskivert/depot/clause/CreateIndexClause.java b/src/java/com/samskivert/depot/clause/CreateIndexClause.java index 8a36857..7925f9f 100644 --- a/src/java/com/samskivert/depot/clause/CreateIndexClause.java +++ b/src/java/com/samskivert/depot/clause/CreateIndexClause.java @@ -32,17 +32,15 @@ import com.samskivert.util.Tuple; /** * Represents an CREATE INDEX instruction to the database. */ -public class CreateIndexClause +public class CreateIndexClause implements QueryClause { /** - * Create a new {@link CreateIndexClause} clause. The name must be unique within the - * relevant database. + * Create a new {@link CreateIndexClause} clause. The name must be unique within the relevant + * database. */ - public CreateIndexClause ( - Class pClass, String name, boolean unique, - List> fields) - + public CreateIndexClause (Class pClass, String name, boolean unique, + List> fields) { _pClass = pClass; _name = name; @@ -83,9 +81,7 @@ public class CreateIndexClause } protected Class _pClass; - protected String _name; - protected boolean _unique; /** The components of the index, e.g. columns or functions of columns. */ diff --git a/src/java/com/samskivert/depot/clause/DeleteClause.java b/src/java/com/samskivert/depot/clause/DeleteClause.java index dce3792..c0aa60d 100644 --- a/src/java/com/samskivert/depot/clause/DeleteClause.java +++ b/src/java/com/samskivert/depot/clause/DeleteClause.java @@ -28,7 +28,7 @@ import com.samskivert.depot.impl.ExpressionVisitor; /** * Builds actual SQL given a main persistent type and some {@link QueryClause} objects. */ -public class DeleteClause implements QueryClause +public class DeleteClause implements QueryClause { public DeleteClause (Class pClass, WhereClause where) { diff --git a/src/java/com/samskivert/depot/clause/FieldDefinition.java b/src/java/com/samskivert/depot/clause/FieldDefinition.java index 533a16f..25135e5 100644 --- a/src/java/com/samskivert/depot/clause/FieldDefinition.java +++ b/src/java/com/samskivert/depot/clause/FieldDefinition.java @@ -55,6 +55,12 @@ public class FieldDefinition implements QueryClause _definition = override; } + public FieldDefinition (ColumnExp field, SQLExpression override) + { + _field = field.name; + _definition = override; + } + /** * The field we're defining. The Query object uses this for indexing. */ diff --git a/src/java/com/samskivert/depot/clause/InsertClause.java b/src/java/com/samskivert/depot/clause/InsertClause.java index 532d1d8..bc20ed8 100644 --- a/src/java/com/samskivert/depot/clause/InsertClause.java +++ b/src/java/com/samskivert/depot/clause/InsertClause.java @@ -29,10 +29,10 @@ import com.samskivert.depot.impl.ExpressionVisitor; /** * Builds actual SQL given a main persistent type and some {@link QueryClause} objects. */ -public class InsertClause implements QueryClause +public class InsertClause implements QueryClause { - public InsertClause ( - Class pClass, Object pojo, Set identityFields) + public InsertClause (Class pClass, Object pojo, + Set identityFields) { _pClass = pClass; _pojo = pojo; diff --git a/src/java/com/samskivert/depot/clause/Join.java b/src/java/com/samskivert/depot/clause/Join.java index 0e52969..f09c3ee 100644 --- a/src/java/com/samskivert/depot/clause/Join.java +++ b/src/java/com/samskivert/depot/clause/Join.java @@ -37,13 +37,6 @@ 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 (Class pClass, String pCol, - Class joinClass, String jCol) - { - _joinClass = joinClass; - _joinCondition = new Equals(new ColumnExp(joinClass, jCol), new ColumnExp(pClass, pCol)); - } - public Join (ColumnExp primary, ColumnExp join) { _joinClass = join.getPersistentClass(); diff --git a/src/java/com/samskivert/depot/clause/UpdateClause.java b/src/java/com/samskivert/depot/clause/UpdateClause.java index e8be8a6..b05087b 100644 --- a/src/java/com/samskivert/depot/clause/UpdateClause.java +++ b/src/java/com/samskivert/depot/clause/UpdateClause.java @@ -23,6 +23,7 @@ package com.samskivert.depot.clause; import java.util.Collection; import com.samskivert.depot.PersistentRecord; +import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.expression.SQLExpression; import com.samskivert.depot.impl.ExpressionVisitor; diff --git a/src/java/com/samskivert/depot/expression/ColumnExp.java b/src/java/com/samskivert/depot/expression/ColumnExp.java index e965b68..2f93b1c 100644 --- a/src/java/com/samskivert/depot/expression/ColumnExp.java +++ b/src/java/com/samskivert/depot/expression/ColumnExp.java @@ -31,11 +31,24 @@ import com.samskivert.depot.impl.ExpressionVisitor; public class ColumnExp implements SQLExpression { + /** The name of the column we reference. */ + public final String name; + + /** Converts an array of column expressions to an array of just the bare names. */ + public static String[] toNames (ColumnExp[] columns) + { + String[] names = new String[columns.length]; + for (int ii = 0; ii < names.length; ii++) { + names[ii] = columns[ii].name; + } + return names; + } + public ColumnExp (Class pClass, String field) { super(); _pClass = pClass; - _pField = field; + this.name = field; } // from SQLExpression @@ -57,18 +70,15 @@ public class ColumnExp public String getField () { - return _pField; + return name; } @Override // from Object public String toString () { - return "\"" + _pField + "\""; // TODO: qualify with record name and be uber verbose? + return "\"" + name + "\""; // TODO: qualify with record name and be uber verbose? } /** The table that hosts the column we reference, or null. */ protected final Class _pClass; - - /** The name of the column we reference. */ - protected final String _pField; } diff --git a/src/java/com/samskivert/depot/impl/BindVisitor.java b/src/java/com/samskivert/depot/impl/BindVisitor.java index ac6eb22..24dc2f3 100644 --- a/src/java/com/samskivert/depot/impl/BindVisitor.java +++ b/src/java/com/samskivert/depot/impl/BindVisitor.java @@ -248,7 +248,7 @@ public class BindVisitor implements ExpressionVisitor updateClause.getWhereClause().accept(this); } - public void visit (InsertClause insertClause) + public void visit (InsertClause insertClause) { DepotMarshaller marsh = _types.getMarshaller(insertClause.getPersistentClass()); Object pojo = insertClause.getPojo(); @@ -266,12 +266,12 @@ public class BindVisitor implements ExpressionVisitor } } - public void visit (DeleteClause deleteClause) + public void visit (DeleteClause deleteClause) { deleteClause.getWhereClause().accept(this); } - public void visit (CreateIndexClause createIndexClause) + public void visit (CreateIndexClause createIndexClause) { for (Tuple field : createIndexClause.getFields()) { field.left.accept(this); diff --git a/src/java/com/samskivert/depot/impl/BuildVisitor.java b/src/java/com/samskivert/depot/impl/BuildVisitor.java index 54b4bd3..67c20cd 100644 --- a/src/java/com/samskivert/depot/impl/BuildVisitor.java +++ b/src/java/com/samskivert/depot/impl/BuildVisitor.java @@ -430,7 +430,7 @@ public abstract class BuildVisitor implements ExpressionVisitor updateClause.getWhereClause().accept(this); } - public void visit (DeleteClause deleteClause) + public void visit (DeleteClause deleteClause) { _builder.append("delete from "); appendTableName(deleteClause.getPersistentClass()); @@ -440,7 +440,7 @@ public abstract class BuildVisitor implements ExpressionVisitor deleteClause.getWhereClause().accept(this); } - public void visit (InsertClause insertClause) + public void visit (InsertClause insertClause) { Class pClass = insertClause.getPersistentClass(); DepotMarshaller marsh = _types.getMarshaller(pClass); @@ -480,7 +480,7 @@ public abstract class BuildVisitor implements ExpressionVisitor _builder.append(")"); } - public void visit (CreateIndexClause createIndexClause) + public void visit (CreateIndexClause createIndexClause) { _builder.append("create "); if (createIndexClause.isUnique()) { diff --git a/src/java/com/samskivert/depot/impl/DepotMarshaller.java b/src/java/com/samskivert/depot/impl/DepotMarshaller.java index caee8dd..829f403 100644 --- a/src/java/com/samskivert/depot/impl/DepotMarshaller.java +++ b/src/java/com/samskivert/depot/impl/DepotMarshaller.java @@ -31,7 +31,8 @@ import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; @@ -46,6 +47,7 @@ import com.samskivert.depot.PersistenceContext; import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.SchemaMigration; import com.samskivert.depot.Stats; +import com.samskivert.depot.annotation.Column; import com.samskivert.depot.annotation.Computed; import com.samskivert.depot.annotation.Entity; import com.samskivert.depot.annotation.FullTextIndex; @@ -194,6 +196,20 @@ public class DepotMarshaller } } + // check whether this field is indexed + Index index = field.getAnnotation(Index.class); + if (index != null) { + String ixName = index.name().equals("") ? field.getName() + "Index" : index.name(); + _indexes.add(buildIndex(ixName, index.unique(), + new ColumnExp(_pClass, field.getName()))); + } + + // if this column is marked as unique, that also means we create an index + Column column = field.getAnnotation(Column.class); + if (column != null && column.unique()) { + _indexes.add(buildIndex(field.getName() + "Index", true, + new ColumnExp(_pClass, field.getName()))); + } } // if we did not find a schema version field, freak out (but not for computed records, for @@ -207,32 +223,30 @@ public class DepotMarshaller _allFields = fields.toArray(new String[fields.size()]); // now check for @Entity annotations on the entire superclass chain - Class iterClass = pClass; + Class iterClass = pClass.asSubclass(PersistentRecord.class); do { entity = iterClass.getAnnotation(Entity.class); if (entity != null) { + // add any indices needed for uniqueness constraints for (UniqueConstraint constraint : entity.uniqueConstraints()) { - String[] conFields = constraint.fieldNames(); - Set colSet = Sets.newHashSet(); - for (int ii = 0; ii < conFields.length; ii ++) { - FieldMarshaller fm = _fields.get(conFields[ii]); + List colExps = Lists.newArrayList(); + for (String field : constraint.fields()) { + FieldMarshaller fm = _fields.get(field); if (fm == null) { throw new IllegalArgumentException( - "Unknown unique constraint field: " + conFields[ii]); + "Unknown unique constraint field: " + field); } - colSet.add(fm.getColumnName()); + colExps.add(new ColumnExp(_pClass, field)); } - _uniqueConstraints.add(colSet); + _indexes.add(buildIndex(constraint.name(), true, colExps)); } + // add any explicit multicolumn or complex indices for (Index index : entity.indices()) { - if (_indexes.containsKey(index.name())) { - continue; - } - _indexes.put(index.name(), index); + _indexes.add(buildIndex(index.name(), index.unique())); } - // if there are FTS indexes in the Table, map those out here for future use + // note any FTS indices for (FullTextIndex fti : entity.fullTextIndices()) { if (_fullTextIndexes.containsKey(fti.name())) { continue; @@ -241,7 +255,7 @@ public class DepotMarshaller } } - iterClass = iterClass.getSuperclass(); + iterClass = iterClass.getSuperclass().asSubclass(PersistentRecord.class); } while (PersistentRecord.class.isAssignableFrom(iterClass) && !PersistentRecord.class.equals(iterClass)); @@ -385,7 +399,7 @@ public class DepotMarshaller // make sure the keys are all null or all non-null if (nulls == 0) { - return makePrimaryKey(values); + return new Key(_pClass, values); } else if (nulls == values.length) { return null; } else if (nulls == zeros) { @@ -394,7 +408,7 @@ public class DepotMarshaller // this is a compromise that allows sensible things like (id=99, type=0) but // unfortunately also allows less sensible things like (id=0, type=5) while // continuing to disallow the dangerous (id=0) - return makePrimaryKey(values); + return new Key(_pClass, values); } // throw an informative error message @@ -413,19 +427,15 @@ public class DepotMarshaller /** * Creates a primary key record for the type of object handled by this marshaller, using the - * supplied primary key value. + * supplied primary key value. This is only allowed for records with single column keys. */ - public Key makePrimaryKey (Comparable... values) + public Key makePrimaryKey (Comparable value) { if (!hasPrimaryKey()) { throw new UnsupportedOperationException( getClass().getName() + " does not define a primary key"); } - String[] fields = new String[_pkColumns.size()]; - for (int ii = 0; ii < _pkColumns.size(); ii++) { - fields[ii] = _pkColumns.get(ii).getField().getName(); - } - return new Key(_pClass, fields, values); + return new Key(_pClass, new Comparable[] { value }); } /** @@ -448,7 +458,7 @@ public class DepotMarshaller } values[ii] = (Comparable) keyValue; } - return makePrimaryKey(values); + return new Key(_pClass, values); } /** @@ -703,12 +713,6 @@ public class DepotMarshaller { log.info("Creating initial table '" + getTableName() + "'."); - final String[][] uniqueConCols = new String[_uniqueConstraints.size()][]; - int kk = 0; - for (Set colSet : _uniqueConstraints) { - uniqueConCols[kk++] = colSet.toArray(new String[colSet.size()]); - } - final Iterable indexen = _indexes.values(); ctx.invoke(new Modifier() { @Override protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { @@ -722,14 +726,11 @@ public class DepotMarshaller } liaison.createTableIfMissing( conn, getTableName(), fieldsToColumns(_columnFields), - declarations, uniqueConCols, primaryKeyColumns); + declarations, null, primaryKeyColumns); // add its indexen - for (Index index : indexen) { - String ixName = getTableName() + "_" + index.name(); - execute(conn, builder, index.complex() ? - buildComplexIndex(ixName, index.unique(), index.name()) : - buildSimpleIndex(ixName, index.unique(), index.fields())); + for (CreateIndexClause iclause : _indexes) { + execute(conn, builder, iclause); } // create our value generators @@ -752,7 +753,6 @@ public class DepotMarshaller { if (builder.newQuery(clause)) { PreparedStatement stmt = builder.prepare(conn); - try { return stmt.executeUpdate(); } finally { @@ -783,9 +783,6 @@ public class DepotMarshaller metaData = TableMetaData.load(ctx, getTableName()); } - // this is a little silly, but we need a copy for name disambiguation later - Set indicesCopy = Sets.newHashSet(metaData.indexColumns.keySet()); - // figure out which columns we have in the table now, so that when all is said and done we // can see what new columns we have in the table and run the creation code for any value // generators that are defined on those columns (we can't just track the columns we add in @@ -854,57 +851,17 @@ public class DepotMarshaller } // add any named indices that exist on the record but not yet on the table - for (final Index index : _indexes.values()) { - final String ixName = getTableName() + "_" + index.name(); - if (metaData.indexColumns.containsKey(ixName)) { - // this index already exists - metaData.indexColumns.remove(ixName); + for (final CreateIndexClause iclause : _indexes) { + if (metaData.indexColumns.containsKey(iclause.getName())) { + metaData.indexColumns.remove(iclause.getName()); // this index already exists continue; } // but this is a new, named index, so we create it - log.info("Creating new index: " + ixName); + log.info("Creating new index: " + iclause.getName()); ctx.invoke(new Modifier() { @Override protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { - execute(conn, builder, index.complex() ? - buildComplexIndex(ixName, index.unique(), index.name()) : - buildSimpleIndex(ixName, index.unique(), index.fields())); - return 0; - } - }); - } - - // now check if there are any @Entity(uniqueConstraints) that need to be created - Set> uniqueIndices = Sets.newHashSet(metaData.indexColumns.values()); - - // unique constraints are unordered and may be unnamed, so we view them only as column sets - for (Set colSet : _uniqueConstraints) { - if (uniqueIndices.contains(colSet)) { - // the table already contains precisely this column set - continue; - } - - // else build the new constraint; we'll name it after one of its columns, adding _N - // to resolve any possible ambiguities, because using all the column names in the - // index name may exceed the maximum length of an SQL identifier - String indexName = colSet.iterator().next(); - if (indicesCopy.contains(indexName)) { - int num = 1; - indexName += "_"; - while (indicesCopy.contains(indexName + num)) { - num ++; - } - indexName += num; - } - - final String[] colArr = colSet.toArray(new String[colSet.size()]); - final String fName = indexName; - ctx.invoke(new Modifier() { - @Override - protected int invoke (Connection conn, DatabaseLiaison liaison) throws SQLException { - if (!liaison.tableContainsIndex(conn, getTableName(), fName)) { - execute(conn, builder, buildSimpleIndex(fName, true, colArr)); - } + execute(conn, builder, iclause); return 0; } }); @@ -981,57 +938,49 @@ public class DepotMarshaller return metaData; } - private CreateIndexClause buildComplexIndex (String ixName, boolean unique, String localIxName) - throws SQLException + protected CreateIndexClause buildIndex (String name, boolean unique) { - List> definition; - String methName = localIxName + "Definition"; - Method method; try { - method = _pClass.getMethod(methName); + method = _pClass.getMethod(name); } catch (NoSuchMethodException nsme) { throw new IllegalArgumentException( - "Index flagged as complex, but no defining method '" + methName - + "' found.", nsme); + "Index flagged as complex, but no defining method '" + name + "' found.", nsme); } - Object result; try { - result = method.invoke(null); + return buildIndex(name, unique, method.invoke(null)); } catch (Exception e) { throw new IllegalArgumentException( - "Error calling index definition method '" + methName + "'", e); + "Error calling index definition method '" + name + "'", e); } - - if (result instanceof SQLExpression) { - definition = new ArrayList>(); - definition.add(new Tuple((SQLExpression)result, Order.ASC)); - - } else if (result instanceof List) { - @SuppressWarnings("unchecked") - List> def = (List>)result; - definition = def; - - } else { - throw new IllegalArgumentException("Method '" + methName - + "' must return SQLExpression or " + "List>"); - } - - return new CreateIndexClause(_pClass, ixName, unique, definition); } - private CreateIndexClause buildSimpleIndex (String ixName, boolean unique, String[] fields) - throws SQLException + protected CreateIndexClause buildIndex (String name, boolean unique, Object config) { - List> definition = new ArrayList>(); - - for (String field : fields) { - ColumnExp column = new ColumnExp(_pClass, field); - definition.add(new Tuple(column, Order.ASC)); + 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) { + definition.add(new Tuple(column, Order.ASC)); + } + } else if (config instanceof SQLExpression) { + definition.add(new Tuple((SQLExpression)config, Order.ASC)); + } else if (config instanceof Tuple) { + @SuppressWarnings("unchecked") Tuple tuple = + (Tuple)config; + definition.add(tuple); + } else if (config instanceof List) { + @SuppressWarnings("unchecked") List> defs = + (List>)config; + definition.addAll(defs); + } else { + throw new IllegalArgumentException( + "Method '" + name + "' must return ColumnExp[], SQLExpression or " + + "List>"); } - - return new CreateIndexClause(_pClass, ixName, unique, definition); + return new CreateIndexClause(_pClass, getTableName() + "_" + name, unique, definition); } // translate an array of field names to an array of column names @@ -1197,12 +1146,10 @@ public class DepotMarshaller /** The fields of our object with directly corresponding table columns. */ protected String[] _columnFields; - /** The indexes defined in @Entity annotations for this record. */ - protected Map _indexes = Maps.newHashMap(); - - /** The unique constraints defined in @Entity annotations for this record. */ - protected Set> _uniqueConstraints = Sets.newHashSet(); + /** The indexes defined for this record. */ + protected List _indexes = Lists.newArrayList(); + /** Any full text indices defined on this entity. */ protected Map _fullTextIndexes = Maps.newHashMap(); /** The version of our persistent object schema as specified in the class definition. */ diff --git a/src/java/com/samskivert/depot/impl/DepotMigrationHistoryRecord.java b/src/java/com/samskivert/depot/impl/DepotMigrationHistoryRecord.java index d1a6652..375721e 100644 --- a/src/java/com/samskivert/depot/impl/DepotMigrationHistoryRecord.java +++ b/src/java/com/samskivert/depot/impl/DepotMigrationHistoryRecord.java @@ -34,19 +34,9 @@ import com.samskivert.depot.expression.ColumnExp; public class DepotMigrationHistoryRecord extends PersistentRecord { // AUTO-GENERATED: FIELDS START - /** The column identifier for the {@link #ident} field. */ - public static final String IDENT = "ident"; - - /** The qualified column identifier for the {@link #ident} field. */ - public static final ColumnExp IDENT_C = - new ColumnExp(DepotMigrationHistoryRecord.class, IDENT); - - /** The column identifier for the {@link #whenCompleted} field. */ - public static final String WHEN_COMPLETED = "whenCompleted"; - - /** The qualified column identifier for the {@link #whenCompleted} field. */ - public static final ColumnExp WHEN_COMPLETED_C = - new ColumnExp(DepotMigrationHistoryRecord.class, WHEN_COMPLETED); + public static final Class _R = DepotMigrationHistoryRecord.class; + 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. */ @@ -68,7 +58,7 @@ public class DepotMigrationHistoryRecord extends PersistentRecord { return new Key( DepotMigrationHistoryRecord.class, - new String[] { IDENT }, + new ColumnExp[] { IDENT }, new Comparable[] { ident }); } // AUTO-GENERATED: METHODS END diff --git a/src/java/com/samskivert/depot/impl/ExpressionVisitor.java b/src/java/com/samskivert/depot/impl/ExpressionVisitor.java index 9d329bf..525da56 100644 --- a/src/java/com/samskivert/depot/impl/ExpressionVisitor.java +++ b/src/java/com/samskivert/depot/impl/ExpressionVisitor.java @@ -81,8 +81,8 @@ public interface ExpressionVisitor public void visit (Exists exists); public void visit (SelectClause selectClause); public void visit (UpdateClause updateClause); - public void visit (DeleteClause deleteClause); - public void visit (InsertClause insertClause); - public void visit (CreateIndexClause createIndexClause); + public void visit (DeleteClause deleteClause); + public void visit (InsertClause insertClause); + public void visit (CreateIndexClause createIndexClause); public void visit (DropIndexClause dropIndexClause); } diff --git a/src/java/com/samskivert/depot/impl/HSQLBuilder.java b/src/java/com/samskivert/depot/impl/HSQLBuilder.java index 03a227e..b922279 100644 --- a/src/java/com/samskivert/depot/impl/HSQLBuilder.java +++ b/src/java/com/samskivert/depot/impl/HSQLBuilder.java @@ -67,7 +67,7 @@ public class HSQLBuilder // (lower(COL1) like '%foo%') OR (lower(COL1) like '%bar%') OR ... // (lower(COL2) like '%foo%') OR (lower(COL2) like '%bar%') OR ... // ... and so on. Not efficient, but basically functional. - Class pClass = match.getPersistentRecord(); + Class pClass = match.getPersistentClass(); // find the fields involved String[] fields = _types.getMarshaller(pClass). @@ -117,7 +117,7 @@ public class HSQLBuilder } @Override - public void visit (CreateIndexClause createIndexClause) + public void visit (CreateIndexClause createIndexClause) { for (Tuple field : createIndexClause.getFields()) { if (!(field.left instanceof ColumnExp)) { diff --git a/src/java/com/samskivert/depot/impl/MySQLBuilder.java b/src/java/com/samskivert/depot/impl/MySQLBuilder.java index 649de98..7bd6815 100644 --- a/src/java/com/samskivert/depot/impl/MySQLBuilder.java +++ b/src/java/com/samskivert/depot/impl/MySQLBuilder.java @@ -67,7 +67,7 @@ public class MySQLBuilder @Override public void visit (FullTextMatch match) { _builder.append("match("); - Class pClass = match.getPersistentRecord(); + Class pClass = match.getPersistentClass(); String[] fields = _types.getMarshaller(pClass).getFullTextIndex(match.getName()).fields(); for (int ii = 0; ii < fields.length; ii ++) { @@ -79,7 +79,7 @@ public class MySQLBuilder _builder.append(") against (? in boolean mode)"); } - @Override public void visit (DeleteClause deleteClause) + @Override public void visit (DeleteClause deleteClause) { _builder.append("delete from "); appendTableName(deleteClause.getPersistentClass()); @@ -103,7 +103,7 @@ public class MySQLBuilder } @Override - public void visit (CreateIndexClause createIndexClause) + public void visit (CreateIndexClause createIndexClause) { for (Tuple field : createIndexClause.getFields()) { if (!(field.left instanceof ColumnExp)) { diff --git a/src/java/com/samskivert/depot/operator/Conditionals.java b/src/java/com/samskivert/depot/operator/Conditionals.java index ae48a04..5bab3e9 100644 --- a/src/java/com/samskivert/depot/operator/Conditionals.java +++ b/src/java/com/samskivert/depot/operator/Conditionals.java @@ -38,16 +38,6 @@ public abstract class Conditionals public static class IsNull implements SQLOperator { - public IsNull (String pColumn) - { - this(new ColumnExp(null, pColumn)); - } - - public IsNull (Class pClass, String pColumn) - { - this(new ColumnExp(pClass, pColumn)); - } - public IsNull (ColumnExp column) { _column = column; @@ -199,17 +189,6 @@ public abstract class Conditionals /** The maximum number of keys allowed in an IN() clause. */ public static final int MAX_KEYS = Short.MAX_VALUE; - public In (Class pClass, String pColumn, Comparable... values) - { - this(new ColumnExp(pClass, pColumn), values); - } - - public In (Class pClass, String pColumn, - Collection> values) - { - this(new ColumnExp(pClass, pColumn), values.toArray(new Comparable[values.size()])); - } - public In (ColumnExp column, Comparable... values) { if (values.length == 0) { @@ -329,7 +308,7 @@ public abstract class Conditionals _query = query; } - public Class getPersistentRecord () + public Class getPersistentClass () { return _pClass; } diff --git a/src/java/com/samskivert/depot/tests/TestRecord.java b/src/java/com/samskivert/depot/tests/TestRecord.java index f08fcac..5bf6853 100644 --- a/src/java/com/samskivert/depot/tests/TestRecord.java +++ b/src/java/com/samskivert/depot/tests/TestRecord.java @@ -35,58 +35,18 @@ import com.samskivert.util.StringUtil; /** * A test persistent object. */ -@Entity(indices={ @Index(name="createdIndex", fields={"created"}) }) +@Entity public class TestRecord extends PersistentRecord { // AUTO-GENERATED: FIELDS START - /** The column identifier for the {@link #recordId} field. */ - public static final String RECORD_ID = "recordId"; - - /** The qualified column identifier for the {@link #recordId} field. */ - public static final ColumnExp RECORD_ID_C = - new ColumnExp(TestRecord.class, RECORD_ID); - - /** The column identifier for the {@link #name} field. */ - public static final String NAME = "name"; - - /** The qualified column identifier for the {@link #name} field. */ - public static final ColumnExp NAME_C = - new ColumnExp(TestRecord.class, NAME); - - /** The column identifier for the {@link #age} field. */ - public static final String AGE = "age"; - - /** The qualified column identifier for the {@link #age} field. */ - public static final ColumnExp AGE_C = - new ColumnExp(TestRecord.class, AGE); - - /** The column identifier for the {@link #homeTown} field. */ - public static final String HOME_TOWN = "homeTown"; - - /** The qualified column identifier for the {@link #homeTown} field. */ - public static final ColumnExp HOME_TOWN_C = - new ColumnExp(TestRecord.class, HOME_TOWN); - - /** The column identifier for the {@link #created} field. */ - public static final String CREATED = "created"; - - /** The qualified column identifier for the {@link #created} field. */ - public static final ColumnExp CREATED_C = - new ColumnExp(TestRecord.class, CREATED); - - /** The column identifier for the {@link #lastModified} field. */ - public static final String LAST_MODIFIED = "lastModified"; - - /** The qualified column identifier for the {@link #lastModified} field. */ - public static final ColumnExp LAST_MODIFIED_C = - new ColumnExp(TestRecord.class, LAST_MODIFIED); - - /** The column identifier for the {@link #numbers} field. */ - public static final String NUMBERS = "numbers"; - - /** The qualified column identifier for the {@link #numbers} field. */ - public static final ColumnExp NUMBERS_C = - new ColumnExp(TestRecord.class, NUMBERS); + public static final Class _R = TestRecord.class; + public static final ColumnExp RECORD_ID = colexp(_R, "recordId"); + public static final ColumnExp NAME = colexp(_R, "name"); + public static final ColumnExp AGE = colexp(_R, "age"); + public static final ColumnExp HOME_TOWN = colexp(_R, "homeTown"); + public static final ColumnExp CREATED = colexp(_R, "created"); + public static final ColumnExp LAST_MODIFIED = colexp(_R, "lastModified"); + public static final ColumnExp NUMBERS = colexp(_R, "numbers"); // AUTO-GENERATED: FIELDS END public static final int SCHEMA_VERSION = 3; @@ -100,6 +60,7 @@ public class TestRecord extends PersistentRecord public String homeTown; + @Index public Date created; public Timestamp lastModified; @@ -121,7 +82,7 @@ public class TestRecord extends PersistentRecord { return new Key( TestRecord.class, - new String[] { RECORD_ID }, + new ColumnExp[] { RECORD_ID }, new Comparable[] { recordId }); } // AUTO-GENERATED: METHODS END diff --git a/src/java/com/samskivert/depot/tests/TestRepository.java b/src/java/com/samskivert/depot/tests/TestRepository.java index 4606208..eed9499 100644 --- a/src/java/com/samskivert/depot/tests/TestRepository.java +++ b/src/java/com/samskivert/depot/tests/TestRepository.java @@ -119,7 +119,7 @@ public class TestRepository extends DepotRepository System.out.println("Delete none " + repo.deleteAll(TestRecord.class, none) + "."); // test collection caching - Where where = new Where(new Conditionals.GreaterThan(TestRecord.RECORD_ID_C, 100)); + Where where = new Where(new Conditionals.GreaterThan(TestRecord.RECORD_ID, 100)); System.out.println("100 and up: " + repo.findAll(TestRecord.class, where).size()); System.out.println("100 and up again: " + repo.findAll(TestRecord.class, where).size()); @@ -131,7 +131,7 @@ public class TestRepository extends DepotRepository System.out.println("Names " + repo.findAll(TestNameRecord.class) + "."); System.out.println("Have " + repo.findAll(TestRecord.class).size() + " records."); repo.deleteAll(TestRecord.class, new Where(new Conditionals.LessThan( - TestRecord.RECORD_ID_C, CREATE_RECORDS/2))); + TestRecord.RECORD_ID, CREATE_RECORDS/2))); System.out.println("Now have " + repo.findAll(TestRecord.class).size() + " records."); repo.deleteAll(TestRecord.class, new Where(new LiteralExp("true"))); // // TODO: try to break our In() clause diff --git a/src/java/com/samskivert/depot/tools/GenRecordTask.java b/src/java/com/samskivert/depot/tools/GenRecordTask.java index f23cf6c..28bb666 100644 --- a/src/java/com/samskivert/depot/tools/GenRecordTask.java +++ b/src/java/com/samskivert/depot/tools/GenRecordTask.java @@ -3,7 +3,7 @@ // // Depot library - a Java relational persistence library // Copyright (C) 2006-2008 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 @@ -37,6 +37,7 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; @@ -254,29 +255,24 @@ public class GenRecordTask extends Task // generate our fields section StringBuilder fsection = new StringBuilder(); + + // add our prototype declaration + VelocityContext ctx = new VelocityContext(); + ctx.put("record", rname); + fsection.append(mergeTemplate(PROTO_TMPL, ctx)); + + // add our ColumnExp constants for (int ii = 0; ii < flist.size(); ii++) { Field f = flist.get(ii); String fname = f.getName(); // create our velocity context - VelocityContext ctx = new VelocityContext(); - ctx.put("record", rname); - ctx.put("field", fname); - ctx.put("capfield", StringUtil.unStudlyName(fname).toUpperCase()); + VelocityContext fctx = (VelocityContext)ctx.clone(); + fctx.put("field", fname); + fctx.put("capfield", StringUtil.unStudlyName(fname).toUpperCase()); // now generate our bits - if (declared.contains(f)) { - if (fsection.length() > 0) { - fsection.append("\n"); - } - fsection.append(mergeTemplate(NAME_TMPL, ctx)); - } - if (!isAbstract) { - if (fsection.length() > 0) { - fsection.append("\n"); - } - fsection.append(mergeTemplate(COL_TMPL, ctx)); - } + fsection.append(mergeTemplate(COL_TMPL, fctx)); } // generate our methods section @@ -284,10 +280,6 @@ public class GenRecordTask extends Task // add a getKey() method, if applicable if (kflist.size() > 0) { - // create our velocity context - VelocityContext ctx = new VelocityContext(); - ctx.put("record", rname); - StringBuilder argList = new StringBuilder(); StringBuilder argNameList = new StringBuilder(); StringBuilder fieldNameList = new StringBuilder(); @@ -470,7 +462,7 @@ public class GenRecordTask extends Task protected Class _prclass; /** Specifies the path to the name code template. */ - protected static final String NAME_TMPL = "com/samskivert/depot/tools/record_name.tmpl"; + protected static final String PROTO_TMPL = "com/samskivert/depot/tools/record_proto.tmpl"; /** Specifies the path to the column code template. */ protected static final String COL_TMPL = "com/samskivert/depot/tools/record_column.tmpl"; diff --git a/src/java/com/samskivert/depot/tools/record_column.tmpl b/src/java/com/samskivert/depot/tools/record_column.tmpl index 82e236f..0445409 100644 --- a/src/java/com/samskivert/depot/tools/record_column.tmpl +++ b/src/java/com/samskivert/depot/tools/record_column.tmpl @@ -1,3 +1 @@ - /** The qualified column identifier for the {@link #$field} field. */ - public static final ColumnExp ${capfield}_C = - new ColumnExp(${record}.class, $capfield); + public static final ColumnExp ${capfield} = colexp(_R, "$field"); diff --git a/src/java/com/samskivert/depot/tools/record_key.tmpl b/src/java/com/samskivert/depot/tools/record_key.tmpl index c185b7d..79e7dd6 100644 --- a/src/java/com/samskivert/depot/tools/record_key.tmpl +++ b/src/java/com/samskivert/depot/tools/record_key.tmpl @@ -6,6 +6,6 @@ { return new Key<${record}>( ${record}.class, - new String[] { $fieldNameList }, + new ColumnExp[] { $fieldNameList }, new Comparable[] { $argNameList }); } diff --git a/src/java/com/samskivert/depot/tools/record_name.tmpl b/src/java/com/samskivert/depot/tools/record_name.tmpl deleted file mode 100644 index e212ce6..0000000 --- a/src/java/com/samskivert/depot/tools/record_name.tmpl +++ /dev/null @@ -1,2 +0,0 @@ - /** The column identifier for the {@link #$field} field. */ - public static final String $capfield = "$field"; diff --git a/src/java/com/samskivert/depot/tools/record_proto.tmpl b/src/java/com/samskivert/depot/tools/record_proto.tmpl new file mode 100644 index 0000000..760672b --- /dev/null +++ b/src/java/com/samskivert/depot/tools/record_proto.tmpl @@ -0,0 +1 @@ + public static final Class<${record}> _R = ${record}.class;