diff --git a/src/main/java/com/avaje/ebean/annotation/Index.java b/src/main/java/com/avaje/ebean/annotation/Index.java index 5ea6d1b50..2806293db 100644 --- a/src/main/java/com/avaje/ebean/annotation/Index.java +++ b/src/main/java/com/avaje/ebean/annotation/Index.java @@ -10,15 +10,24 @@ import java.lang.annotation.Target; * * @author rvbiljouw */ -@Target({ElementType.FIELD}) +@Target({ElementType.TYPE, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) public @interface Index { /** - * Name of the index - * - * @return index name + * Name of the index. If left blank a name is derived using the built in naming convention. */ - String value() default ""; + String name() default ""; + + /** + * If set true indicates this is a unique index. + */ + boolean unique() default false; + + /** + * When placed on the class (rather than field) you can specify the columns + * to include in the index in order. + */ + String[] columnNames() default {}; } diff --git a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java index c07bec38e..1354f5550 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java +++ b/src/main/java/com/avaje/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java @@ -324,14 +324,13 @@ public class BaseTableDdl implements TableDdl { protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException { - String tableName = createTable.getName(); - List uniqueConstraints = createTable.getUniqueConstraint(); for (UniqueConstraint uniqueConstraint : uniqueConstraints) { + String uqName = uniqueConstraint.getName(); String[] columns = toColumnNamesSplit(uniqueConstraint.getColumnNames()); - apply - .append(platformDdl.alterTableAddUniqueConstraint(tableName, uniqueConstraint.getName(), columns)) - .endOfStatement(); + apply.append(",").newLine(); + apply.append(" constraint ").append(uqName).append(" unique"); + appendColumns(columns, apply); } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java index 0384e6278..5b3e4a112 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MIndex.java @@ -26,6 +26,17 @@ public class MIndex { this.columns.add(columnName); } + /** + * Create a multi column non unique index. + */ + public MIndex(String indexName, String tableName, String[] columnNames) { + this.tableName = tableName; + this.indexName = indexName; + for (int i = 0; i < columnNames.length; i++) { + this.columns.add(columnNames[i]); + } + } + public MIndex(CreateIndex createIndex) { this.indexName = createIndex.getIndexName(); this.tableName = createIndex.getTableName(); diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java index 6f989b78a..1fc50e448 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/MTable.java @@ -9,6 +9,7 @@ import com.avaje.ebean.dbmigration.migration.DropColumn; import com.avaje.ebean.dbmigration.migration.DropHistoryTable; import com.avaje.ebean.dbmigration.migration.DropTable; import com.avaje.ebean.dbmigration.migration.IdentityType; +import com.avaje.ebean.dbmigration.migration.UniqueConstraint; import java.math.BigInteger; import java.util.ArrayList; @@ -169,6 +170,21 @@ public class MTable { createTable.getForeignKey().add(compoundKey.createForeignKey()); } + for (MCompoundUniqueConstraint constraint : compoundUniqueConstraints) { + UniqueConstraint uq = new UniqueConstraint(); + uq.setName(constraint.getName()); + String[] columns = constraint.getColumns(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < columns.length; i++) { + if (i > 0) { + sb.append(","); + } + sb.append(columns[i]); + } + uq.setColumnNames(sb.toString()); + createTable.getUniqueConstraint().add(uq); + } + return createTable; } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java index dd71eca4e..db911864d 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/ModelContainer.java @@ -224,4 +224,12 @@ public class ModelContainer { indexes.put(indexName, new MIndex(indexName, tableName, columnName)); } + + /** + * Add a multi column index. + */ + public void addIndex(String indexName, String tableName, String[] columnNames) { + + indexes.put(indexName, new MIndex(indexName, tableName, columnNames)); + } } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java index aa27311c7..0b3c2de07 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildContext.java @@ -75,6 +75,10 @@ public class ModelBuildContext { model.addIndex(indexName, tableName, columnName); } + public void addIndex(String indexName, String tableName, String[] columnNames) { + model.addIndex(indexName, tableName, columnNames); + } + private String maxLength(String constraintName, int indexCount) { return maxLength.maxLength(constraintName, indexCount); } diff --git a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java index b3817d13e..abc67be4a 100644 --- a/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java +++ b/src/main/java/com/avaje/ebean/dbmigration/model/build/ModelBuildPropertyVisitor.java @@ -9,7 +9,7 @@ import com.avaje.ebeaninternal.server.deploy.BeanProperty; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocMany; import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne; import com.avaje.ebeaninternal.server.deploy.BeanPropertyCompound; -import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint; import com.avaje.ebeaninternal.server.deploy.TableJoinColumn; import com.avaje.ebeaninternal.server.deploy.id.ImportedId; @@ -36,24 +36,39 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor { private int countCheck; - public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table, CompoundUniqueContraint[] compoundUniqueConstraints) { + public ModelBuildPropertyVisitor(ModelBuildContext ctx, MTable table, CompoundUniqueConstraint[] constraints) { this.ctx = ctx; this.table = table; - addCompoundUniqueConstraint(compoundUniqueConstraints); + addCompoundUniqueConstraint(constraints); } /** * Add unique constraints defined via JPA UniqueConstraint annotations. */ - private void addCompoundUniqueConstraint(CompoundUniqueContraint[] compoundUniqueConstraints) { + private void addCompoundUniqueConstraint(CompoundUniqueConstraint[] constraints) { - if (compoundUniqueConstraints != null) { - for (int i = 0; i < compoundUniqueConstraints.length; i++) { - String[] columns = compoundUniqueConstraints[i].getColumns(); - String uqName = determineUniqueConstraintName(columns); - table.addCompoundUniqueConstraint(columns, false, uqName); + if (constraints != null) { + for (int i = 0; i < constraints.length; i++) { + CompoundUniqueConstraint constraint = constraints[i]; + String[] columns = constraint.getColumns(); indexSet.add(columns); + + if (constraint.isUnique()) { + String uqName = constraint.getName(); + if (uqName == null || uqName.trim().isEmpty()) { + uqName = determineUniqueConstraintName(columns); + } + table.addCompoundUniqueConstraint(columns, false, uqName); + + } else { + // 'just' an index (not a unique constraint) + String idxName = constraint.getName(); + if (idxName == null || idxName.trim().isEmpty()) { + idxName = determineIndexName(columns); + } + ctx.addIndex(idxName, table.getName(), columns); + } } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index 779fc4b34..22cf7c42f 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -133,7 +133,7 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { */ private final ConcurrencyMode concurrencyMode; - private final CompoundUniqueContraint[] compoundUniqueConstraints; + private final CompoundUniqueConstraint[] compoundUniqueConstraints; /** * The base database table. @@ -1352,7 +1352,7 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { @Override public Object getBeanId(T bean) { - return getId((EntityBean)bean); + return getId((EntityBean) bean); } /** @@ -1620,7 +1620,7 @@ public class BeanDescriptor implements MetaBeanInfo, SpiBeanType { /** * Return the compound unique constraints. */ - public CompoundUniqueContraint[] getCompoundUniqueConstraints() { + public CompoundUniqueConstraint[] getCompoundUniqueConstraints() { return compoundUniqueConstraints; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueConstraint.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueConstraint.java new file mode 100644 index 000000000..2613c6a34 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueConstraint.java @@ -0,0 +1,50 @@ +package com.avaje.ebeaninternal.server.deploy; + +/** + * Holds multiple column unique constraints defined for an entity. + */ +public class CompoundUniqueConstraint { + + private final String[] columns; + + private final String name; + + private final boolean unique; + + public CompoundUniqueConstraint(String[] columns, String name, boolean unique) { + this.columns = columns; + this.unique = unique; + this.name = name; + } + + /** + * Create a unique constraint given the column names. + */ + public CompoundUniqueConstraint(String[] columns) { + this.columns = columns; + this.unique = true; + this.name = null; + } + + /** + * Return true if this is a unique constraint. + */ + public boolean isUnique() { + return unique; + } + + /** + * Return the index name (can be null). + */ + public String getName() { + return name; + } + + /** + * Return the columns that make up this unique constraint. + */ + public String[] getColumns() { + return columns; + } + +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueContraint.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueContraint.java deleted file mode 100644 index a38ee9c57..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/CompoundUniqueContraint.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.avaje.ebeaninternal.server.deploy; - -/** - * Holds multiple column unique constraints defined for an entity. - */ -public class CompoundUniqueContraint { - - private final String[] columns; - - public CompoundUniqueContraint(String[] columns) { - this.columns = columns; - } - - /** - * Return the columns that make up this unique constraint. - */ - public String[] getColumns() { - return columns; - } - -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java index 67ee61d10..e478f7644 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java @@ -16,7 +16,7 @@ import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistController; import com.avaje.ebeaninternal.server.deploy.ChainedBeanPersistListener; import com.avaje.ebeaninternal.server.deploy.ChainedBeanPostLoad; import com.avaje.ebeaninternal.server.deploy.ChainedBeanQueryAdapter; -import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint; import com.avaje.ebeaninternal.server.deploy.DRawSqlMeta; import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; @@ -107,7 +107,7 @@ public class DeployBeanDescriptor { private boolean updateChangesOnly; - private List compoundUniqueConstraints; + private List compoundUniqueConstraints; /** * The base database table. @@ -371,9 +371,9 @@ public class DeployBeanDescriptor { /** * Add a compound unique constraint. */ - public void addCompoundUniqueConstraint(CompoundUniqueContraint c) { + public void addCompoundUniqueConstraint(CompoundUniqueConstraint c) { if (compoundUniqueConstraints == null) { - compoundUniqueConstraints = new ArrayList(); + compoundUniqueConstraints = new ArrayList(); } compoundUniqueConstraints.add(c); } @@ -381,11 +381,11 @@ public class DeployBeanDescriptor { /** * Return the compound unique constraints (can be null). */ - public CompoundUniqueContraint[] getCompoundUniqueConstraints() { + public CompoundUniqueConstraint[] getCompoundUniqueConstraints() { if (compoundUniqueConstraints == null) { return null; } else { - return compoundUniqueConstraints.toArray(new CompoundUniqueContraint[compoundUniqueConstraints.size()]); + return compoundUniqueConstraints.toArray(new CompoundUniqueConstraint[compoundUniqueConstraints.size()]); } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java index 947241b1a..901e2b85b 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationClass.java @@ -11,13 +11,14 @@ import com.avaje.ebean.annotation.CacheStrategy; import com.avaje.ebean.annotation.CacheTuning; import com.avaje.ebean.annotation.EntityConcurrencyMode; import com.avaje.ebean.annotation.History; +import com.avaje.ebean.annotation.Index; import com.avaje.ebean.annotation.NamedUpdate; import com.avaje.ebean.annotation.NamedUpdates; import com.avaje.ebean.annotation.UpdateMode; import com.avaje.ebean.config.TableName; import com.avaje.ebeaninternal.server.core.CacheOptions; import com.avaje.ebeaninternal.server.deploy.BeanDescriptor.EntityType; -import com.avaje.ebeaninternal.server.deploy.CompoundUniqueContraint; +import com.avaje.ebeaninternal.server.deploy.CompoundUniqueConstraint; import com.avaje.ebeaninternal.server.deploy.DeployNamedQuery; import com.avaje.ebeaninternal.server.deploy.DeployNamedUpdate; import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanProperty; @@ -76,9 +77,14 @@ public class AnnotationClass extends AnnotationParser { descriptor.setName("Embeddable:" + cls.getSimpleName()); } + Index index = cls.getAnnotation(Index.class); + if (index != null) { + descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(index.columnNames(), index.name(), index.unique())); + } + UniqueConstraint uc = cls.getAnnotation(UniqueConstraint.class); if (uc != null) { - descriptor.addCompoundUniqueConstraint(new CompoundUniqueContraint(uc.columnNames())); + descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(uc.columnNames())); } Table table = cls.getAnnotation(Table.class); @@ -86,7 +92,7 @@ public class AnnotationClass extends AnnotationParser { UniqueConstraint[] uniqueConstraints = table.uniqueConstraints(); if (uniqueConstraints != null) { for (UniqueConstraint c : uniqueConstraints) { - descriptor.addCompoundUniqueConstraint(new CompoundUniqueContraint(c.columnNames())); + descriptor.addCompoundUniqueConstraint(new CompoundUniqueConstraint(c.columnNames())); } } } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java index d579ff62d..6169cfff0 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/parse/AnnotationFields.java @@ -287,7 +287,7 @@ public class AnnotationFields extends AnnotationParser { throw new RuntimeException("Can't use Index on foreign key relationships."); } prop.setIndexed(); - prop.setIndexName(index.value()); + prop.setIndexName(index.name()); } } diff --git a/src/test/java/com/avaje/tests/model/basic/Contact.java b/src/test/java/com/avaje/tests/model/basic/Contact.java index 5227ca0eb..67f7cccf5 100644 --- a/src/test/java/com/avaje/tests/model/basic/Contact.java +++ b/src/test/java/com/avaje/tests/model/basic/Contact.java @@ -12,7 +12,9 @@ import javax.persistence.Version; import com.avaje.ebean.annotation.CacheStrategy; import com.avaje.ebean.annotation.ChangeLog; import com.avaje.ebean.annotation.CreatedTimestamp; +import com.avaje.ebean.annotation.Index; +@Index(columnNames = {"last_name","first_name"}) @ChangeLog @Entity @CacheStrategy(naturalKey="email")