diff --git a/src/java/com/samskivert/depot/annotation/Index.java b/src/java/com/samskivert/depot/annotation/Index.java index e5811d4..9aa1b45 100644 --- a/src/java/com/samskivert/depot/annotation/Index.java +++ b/src/java/com/samskivert/depot/annotation/Index.java @@ -32,15 +32,21 @@ import java.lang.annotation.Target; @Retention(value=RetentionPolicy.RUNTIME) public @interface Index { - /** 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: + /** + * Defines the name of the index.

+ * + * If this annotation is on a field, an index is created for all fields annotated with this + * name in the order of the fields in the class.

+ * + * 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 ()
+     * public static List<Tuple<SQLExpression, OrderBy.Order>> 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. + * 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 ""; diff --git a/src/java/com/samskivert/depot/impl/DepotMarshaller.java b/src/java/com/samskivert/depot/impl/DepotMarshaller.java index a330dd2..bb2b8ca 100644 --- a/src/java/com/samskivert/depot/impl/DepotMarshaller.java +++ b/src/java/com/samskivert/depot/impl/DepotMarshaller.java @@ -23,6 +23,9 @@ package com.samskivert.depot.impl; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; @@ -30,10 +33,9 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; -import java.util.List; -import java.util.Map; -import java.util.Set; - +import com.google.common.base.Preconditions; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -44,6 +46,7 @@ import com.samskivert.util.Tuple; import com.samskivert.jdbc.ColumnDefinition; import com.samskivert.jdbc.DatabaseLiaison; + import com.samskivert.depot.DatabaseException; import com.samskivert.depot.Key; import com.samskivert.depot.PersistenceContext; @@ -60,11 +63,10 @@ import com.samskivert.depot.annotation.Index; import com.samskivert.depot.annotation.TableGenerator; import com.samskivert.depot.annotation.Transient; import com.samskivert.depot.annotation.UniqueConstraint; -import com.samskivert.depot.clause.OrderBy.Order; import com.samskivert.depot.clause.QueryClause; +import com.samskivert.depot.clause.OrderBy.Order; import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.expression.SQLExpression; - import com.samskivert.depot.impl.clause.CreateIndexClause; import static com.samskivert.depot.Log.log; @@ -112,8 +114,12 @@ public class DepotMarshaller boolean seenIdentityGenerator = false; - // introspect on the class and create marshallers for persistent fields + // introspect on the class and create marshallers and indices for persistent fields List fields = Lists.newArrayList(); + ListMultimap> namedFieldIndices = + ArrayListMultimap.create(); + ListMultimap> uniqueNamedFieldIndices = + ArrayListMultimap.create(); for (Field field : _pClass.getFields()) { int mods = field.getModifiers(); @@ -136,7 +142,8 @@ public class DepotMarshaller FieldMarshaller fm = FieldMarshaller.createMarshaller(field); _fields.put(field.getName(), fm); - fields.add(new ColumnExp(_pClass, field.getName())); + ColumnExp fieldColumn = new ColumnExp(_pClass, field.getName()); + fields.add(fieldColumn); // check to see if this is our primary key if (field.getAnnotation(Id.class) != null) { @@ -197,19 +204,35 @@ 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()))); + String name = index.name().equals("") ? field.getName() + "Index" : index.name(); + Tuple entry = + new Tuple(fieldColumn, Order.ASC); + if (index.unique()) { + Preconditions.checkArgument(!namedFieldIndices.containsKey(index.name()), + "All @Index for a particular name must be unique or non-unique"); + uniqueNamedFieldIndices.put(name, entry); + } else { + Preconditions.checkArgument( + !uniqueNamedFieldIndices.containsKey(index.name()), + "All @Index for a particular name must be unique or non-unique"); + namedFieldIndices.put(name, entry); + } } // 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()))); + _indexes.add(buildIndex(field.getName() + "Index", true, fieldColumn)); } } + for (String indexName : namedFieldIndices.keySet()) { + _indexes.add(buildIndex(indexName, false, namedFieldIndices.get(indexName))); + } + for (String indexName : uniqueNamedFieldIndices.keySet()) { + _indexes.add(buildIndex(indexName, true, uniqueNamedFieldIndices.get(indexName))); + } + // if we did not find a schema version field, freak out (but not for computed records, for // whom there is no table) if (_tableName != null && _schemaVersion <= 0) {