From 3d1e36c31e2a8354e03cdfe12565503190c2e957 Mon Sep 17 00:00:00 2001 From: James Moger Date: Thu, 4 Aug 2011 14:10:59 -0400 Subject: Overhauling annotations. * @IQSchema(name="public") -> @IQSchema("public") * @IQDatabase(version=2) -> @IQVersion(2) * @IQTable(version=2) -> @IQVersion(2) * @IQIndex annotation simplified to be used for one index definition and expanded to specify index name * added @IQIndexes annotation to specify multiple IQIndex annotations --- src/com/iciql/Db.java | 18 +++--- src/com/iciql/DbUpgrader.java | 4 +- src/com/iciql/Iciql.java | 85 +++++++++++-------------- src/com/iciql/TableDefinition.java | 72 ++++++++++----------- src/com/iciql/TableInspector.java | 105 +++++++++++++++++-------------- src/com/iciql/util/StatementBuilder.java | 8 +++ 6 files changed, 152 insertions(+), 140 deletions(-) (limited to 'src') diff --git a/src/com/iciql/Db.java b/src/com/iciql/Db.java index 3f86d15..f70a37e 100644 --- a/src/com/iciql/Db.java +++ b/src/com/iciql/Db.java @@ -34,7 +34,7 @@ import java.util.Set; import javax.sql.DataSource; import com.iciql.DbUpgrader.DefaultDbUpgrader; -import com.iciql.Iciql.IQDatabase; +import com.iciql.Iciql.IQVersion; import com.iciql.Iciql.IQTable; import com.iciql.SQLDialect.DefaultSQLDialect; import com.iciql.SQLDialect.H2Dialect; @@ -230,8 +230,8 @@ public class Db { // flag as checked immediately because calls are nested. upgradeChecked.add(dbUpgrader.getClass()); - IQDatabase model = dbUpgrader.getClass().getAnnotation(IQDatabase.class); - if (model.version() > 0) { + IQVersion model = dbUpgrader.getClass().getAnnotation(IQVersion.class); + if (model.value() > 0) { DbVersion v = new DbVersion(); DbVersion dbVersion = // (SCHEMA="" && TABLE="") == DATABASE @@ -239,17 +239,17 @@ public class Db { if (dbVersion == null) { // database has no version registration, but model specifies // version: insert DbVersion entry and return. - DbVersion newDb = new DbVersion(model.version()); + DbVersion newDb = new DbVersion(model.value()); insert(newDb); } else { // database has a version registration: // check to see if upgrade is required. - if ((model.version() > dbVersion.version) && (dbUpgrader != null)) { + if ((model.value() > dbVersion.version) && (dbUpgrader != null)) { // database is an older version than the model boolean success = dbUpgrader - .upgradeDatabase(this, dbVersion.version, model.version()); + .upgradeDatabase(this, dbVersion.version, model.value()); if (success) { - dbVersion.version = model.version(); + dbVersion.version = model.value(); update(dbVersion); } } @@ -316,8 +316,8 @@ public class Db { } public synchronized void setDbUpgrader(DbUpgrader upgrader) { - if (!upgrader.getClass().isAnnotationPresent(IQDatabase.class)) { - throw new IciqlException("DbUpgrader must be annotated with " + IQDatabase.class.getSimpleName()); + if (!upgrader.getClass().isAnnotationPresent(IQVersion.class)) { + throw new IciqlException("DbUpgrader must be annotated with " + IQVersion.class.getSimpleName()); } this.dbUpgrader = upgrader; upgradeChecked.clear(); diff --git a/src/com/iciql/DbUpgrader.java b/src/com/iciql/DbUpgrader.java index c4ab36b..1303f4e 100644 --- a/src/com/iciql/DbUpgrader.java +++ b/src/com/iciql/DbUpgrader.java @@ -17,7 +17,7 @@ package com.iciql; -import com.iciql.Iciql.IQDatabase; +import com.iciql.Iciql.IQVersion; /** * Interface which defines a class to handle table changes based on model @@ -65,7 +65,7 @@ public interface DbUpgrader { * The default database upgrader. It throws runtime exception instead of * handling upgrade requests. */ - @IQDatabase(version = 0) + @IQVersion(0) public static class DefaultDbUpgrader implements DbUpgrader { public boolean upgradeDatabase(Db db, int fromVersion, int toVersion) { diff --git a/src/com/iciql/Iciql.java b/src/com/iciql/Iciql.java index 7267d33..35ad8cc 100644 --- a/src/com/iciql/Iciql.java +++ b/src/com/iciql/Iciql.java @@ -140,11 +140,13 @@ import java.lang.annotation.Target; public interface Iciql { /** - * An annotation for a database. + * An annotation for an iciql version. + *

+ * @IQVersion(1) */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) - public @interface IQDatabase { + public @interface IQVersion { /** * If set to a non-zero value, iciql maintains a "_iq_versions" table @@ -153,12 +155,14 @@ public interface Iciql { * statements. Default: 0. You must specify a DbUpgrader on your Db * object to use this parameter. */ - int version() default 0; + int value() default 0; } /** * An annotation for a schema. + *

+ * @IQSchema("PUBLIC") */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @@ -167,7 +171,7 @@ public interface Iciql { /** * The schema may be optionally specified. Default: unspecified. */ - String name() default ""; + String value() default ""; } @@ -180,58 +184,54 @@ public interface Iciql { /** * An index annotation. + *

+ *

*/ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface IQIndex { /** - * Standard indexes may be optionally specified. - * - * Standard indexes may still be added in the define() method if the - * model class is not annotated with IQTable. Default: unspecified. + * Index name. If null or empty, iciql will generate one. */ - String[] standard() default {}; + String name() default ""; /** - * Unique indexes may be optionally specified. + * Type of the index. * - * Unique indexes may still be added in the define() method if the model - * class is not annotated with IQTable. Default: unspecified. + * + * HASH indexes may only be valid for single column indexes. + * */ - String[] unique() default {}; + IndexType type() default IndexType.STANDARD; /** - * Hash indexes may be optionally specified. + * Columns to include in index. * - * Hash indexes may still be added in the define() method if the model - * class is not annotated with IQTable. Default: unspecified. */ - String[] hash() default {}; - - /** - * Unique hash indexes may be optionally specified. - * - * Unique hash indexes may still be added in the define() method if the - * model class is not annotated with IQTable. Default: unspecified. - */ - String[] uniqueHash() default {}; + String[] value() default {}; + } + /** + * Annotation to specify multiple indexes. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface IQIndexes { + IQIndex[] value() default {}; } /** @@ -295,15 +295,6 @@ public interface Iciql { * databases. Default: false. */ boolean memoryTable() default false; - - /** - * If non-zero, iciql will maintain a "_iq_versions" table within your - * database. The version number is used to call to a registered - * DbUpgrader implementation to perform relevant ALTER statements. - * Default: 0. You must specify a DbUpgrader on your Db object to use - * this parameter. - */ - int version() default 0; } /** diff --git a/src/com/iciql/TableDefinition.java b/src/com/iciql/TableDefinition.java index 883ce33..456d0f4 100644 --- a/src/com/iciql/TableDefinition.java +++ b/src/com/iciql/TableDefinition.java @@ -27,11 +27,13 @@ import java.util.IdentityHashMap; import java.util.List; import java.util.Map; -import com.iciql.Iciql.IndexType; import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQIndex; +import com.iciql.Iciql.IQIndexes; import com.iciql.Iciql.IQSchema; import com.iciql.Iciql.IQTable; +import com.iciql.Iciql.IQVersion; +import com.iciql.Iciql.IndexType; import com.iciql.util.StatementBuilder; import com.iciql.util.StatementLogger; import com.iciql.util.StringUtils; @@ -133,9 +135,9 @@ class TableDefinition { List getFields() { return fields; } - + FieldDefinition getField(String name) { - for (FieldDefinition field:fields) { + for (FieldDefinition field : fields) { if (field.columnName.equalsIgnoreCase(name)) { return field; } @@ -199,7 +201,7 @@ class TableDefinition { */ void addIndex(IndexType type, Object[] modelFields) { List columnNames = mapColumnNames(modelFields); - addIndex(type, columnNames); + addIndex(null, type, columnNames); } /** @@ -210,9 +212,13 @@ class TableDefinition { * @param columnNames * the ordered list of column names */ - void addIndex(IndexType type, List columnNames) { + void addIndex(String name, IndexType type, List columnNames) { IndexDefinition index = new IndexDefinition(); - index.indexName = tableName + "_" + indexes.size(); + if (StringUtils.isNullOrEmpty(name)) { + index.indexName = tableName + "_" + indexes.size(); + } else { + index.indexName = name; + } index.columnNames = Utils.newArrayList(columnNames); index.type = type; indexes.add(index); @@ -376,7 +382,7 @@ class TableDefinition { } buff.append(')'); stat.setSQL(buff.toString()); - + StatementLogger.merge(stat.getSQL()); stat.executeUpdate(); } @@ -524,10 +530,10 @@ class TableDefinition { } /** - * Retrieve list of columns from index definition. + * Retrieve list of columns from primary key definition. * * @param index - * the index columns, separated by space + * the primary key columns, separated by space * @return the column list */ private List getColumns(String index) { @@ -554,8 +560,8 @@ class TableDefinition { if (clazz.isAnnotationPresent(IQSchema.class)) { IQSchema schemaAnnotation = clazz.getAnnotation(IQSchema.class); // setup schema name mapping, if properly annotated - if (!StringUtils.isNullOrEmpty(schemaAnnotation.name())) { - schemaName = schemaAnnotation.name(); + if (!StringUtils.isNullOrEmpty(schemaAnnotation.value())) { + schemaName = schemaAnnotation.value(); } } @@ -571,8 +577,11 @@ class TableDefinition { createTableIfRequired = tableAnnotation.createIfRequired(); // model version - if (tableAnnotation.version() > 0) { - tableVersion = tableAnnotation.version(); + if (clazz.isAnnotationPresent(IQVersion.class)) { + IQVersion versionAnnotation = clazz.getAnnotation(IQVersion.class); + if (versionAnnotation.value() > 0) { + tableVersion = versionAnnotation.value(); + } } // setup the primary index, if properly annotated @@ -583,34 +592,27 @@ class TableDefinition { } if (clazz.isAnnotationPresent(IQIndex.class)) { - IQIndex indexAnnotation = clazz.getAnnotation(IQIndex.class); - - // setup the indexes, if properly annotated - addIndexes(IndexType.STANDARD, indexAnnotation.standard()); - addIndexes(IndexType.UNIQUE, indexAnnotation.unique()); - addIndexes(IndexType.HASH, indexAnnotation.hash()); - addIndexes(IndexType.UNIQUE_HASH, indexAnnotation.uniqueHash()); + // single table index + IQIndex index = clazz.getAnnotation(IQIndex.class); + addIndex(index); } - } - void addIndexes(IndexType type, String[] indexes) { - for (String index : indexes) { - List validatedColumns = getColumns(index); - if (validatedColumns == null) { - return; + if (clazz.isAnnotationPresent(IQIndexes.class)) { + // multiple table indexes + IQIndexes indexes = clazz.getAnnotation(IQIndexes.class); + for (IQIndex index : indexes.value()) { + addIndex(index); } - addIndex(type, validatedColumns); } } - List getIndexes(IndexType type) { - List list = Utils.newArrayList(); - for (IndexDefinition def : indexes) { - if (def.type.equals(type)) { - list.add(def); - } - } - return list; + void addIndex(IQIndex index) { + List columns = Arrays.asList(index.value()); + addIndex(index.name(), index.type(), columns); + } + + List getIndexes() { + return indexes; } void initObject(Object obj, Map map) { diff --git a/src/com/iciql/TableInspector.java b/src/com/iciql/TableInspector.java index f920315..8e537b4 100644 --- a/src/com/iciql/TableInspector.java +++ b/src/com/iciql/TableInspector.java @@ -23,6 +23,7 @@ import static com.iciql.ValidationRemark.warn; import static com.iciql.util.JdbcUtils.closeSilently; import static com.iciql.util.StringUtils.isNullOrEmpty; import static java.text.MessageFormat.format; + import java.lang.reflect.Modifier; import java.sql.DatabaseMetaData; import java.sql.ResultSet; @@ -34,14 +35,16 @@ import java.util.List; import java.util.Map; import java.util.Set; -import com.iciql.Iciql.IndexType; import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQIndex; +import com.iciql.Iciql.IQIndexes; import com.iciql.Iciql.IQSchema; import com.iciql.Iciql.IQTable; +import com.iciql.Iciql.IndexType; import com.iciql.TableDefinition.FieldDefinition; import com.iciql.TableDefinition.IndexDefinition; import com.iciql.util.StatementBuilder; +import com.iciql.util.StringUtils; import com.iciql.util.Utils; /** @@ -175,8 +178,10 @@ public class TableInspector { Set imports = Utils.newHashSet(); imports.add(IQSchema.class.getCanonicalName()); imports.add(IQTable.class.getCanonicalName()); + imports.add(IQIndexes.class.getCanonicalName()); imports.add(IQIndex.class.getCanonicalName()); - imports.add(IQColumn.class.getCanonicalName()); + imports.add(IQColumn.class.getCanonicalName()); + imports.add(IndexType.class.getCanonicalName()); // fields StringBuilder fields = new StringBuilder(); @@ -207,7 +212,7 @@ public class TableInspector { model.append('@').append(IQSchema.class.getSimpleName()); model.append('('); AnnotationBuilder ap = new AnnotationBuilder(); - ap.addParameter("name", schema); + ap.addParameter(null, schema); model.append(ap); model.append(')').append(eol); } @@ -233,17 +238,11 @@ public class TableInspector { model.append(ap); model.append(')').append(eol); + // @IQIndexes // @IQIndex - ap = new AnnotationBuilder(); - generateIndexAnnotations(ap, "standard", IndexType.STANDARD); - generateIndexAnnotations(ap, "unique", IndexType.UNIQUE); - generateIndexAnnotations(ap, "hash", IndexType.HASH); - generateIndexAnnotations(ap, "uniqueHash", IndexType.UNIQUE_HASH); - if (ap.length() > 0) { - model.append('@').append(IQIndex.class.getSimpleName()); - model.append('('); - model.append(ap); - model.append(')').append(eol); + String indexAnnotations = generateIndexAnnotations(); + if (!StringUtils.isNullOrEmpty(indexAnnotations)) { + model.append(indexAnnotations); } // class declaration @@ -269,32 +268,50 @@ public class TableInspector { * * @param ap */ - void generateIndexAnnotations(AnnotationBuilder ap, String parameter, IndexType type) { - List list = getIndexes(type); - if (list.size() == 0) { + String generateIndexAnnotations() { + if (indexes == null || indexes.size() == 0) { // no matching indexes - return; + return null; } - if (list.size() == 1) { - ap.addParameter(parameter, list.get(0).getColumnsString()); + AnnotationBuilder ap = new AnnotationBuilder(); + if (indexes.size() == 1) { + // single index + ap.append(generateIndexAnnotation(indexes.get(0))); + ap.append(eol); } else { - List parameters = Utils.newArrayList(); - for (IndexInspector index : list) { - parameters.add(index.getColumnsString()); + // multiple indexes + ap.append('@').append(IQIndexes.class.getSimpleName()); + ap.append("({"); + ap.resetCount(); + for (IndexInspector index : indexes.values()) { + ap.appendExceptFirst(", "); + ap.append(generateIndexAnnotation(index)); } - ap.addParameter(parameter, parameters); + ap.append("})").append(eol); } - + return ap.toString(); } - private List getIndexes(IndexType type) { - List list = Utils.newArrayList(); - for (IndexInspector index : indexes.values()) { - if (index.type.equals(type)) { - list.add(index); - } + private String generateIndexAnnotation(IndexInspector index) { + AnnotationBuilder ap = new AnnotationBuilder(); + ap.append('@').append(IQIndex.class.getSimpleName()); + ap.append('('); + ap.resetCount(); + if (!StringUtils.isNullOrEmpty(index.name)) { + ap.addParameter("name", index.name); + } + if (!index.type.equals(IndexType.STANDARD)) { + ap.addParameter("type", IndexType.class.getSimpleName() + "." + index.type.name()); + } + if (ap.getCount() > 0) { + // multiple fields specified + ap.addParameter("values", index.columns); + } else { + // default value + ap.addParameter(null, index.columns); } - return list; + ap.append(')'); + return ap.toString(); } private StatementBuilder generateColumn(Set imports, ColumnInspector col, boolean trimStrings) { @@ -424,11 +441,10 @@ public class TableInspector { */ private void validate(List remarks, TableDefinition def, IndexInspector index, boolean throwError) { - List defIndexes = def.getIndexes(IndexType.STANDARD); - List dbIndexes = getIndexes(IndexType.STANDARD); - if (defIndexes.size() > dbIndexes.size()) { + List defIndexes = def.getIndexes(); + if (defIndexes.size() > indexes.size()) { remarks.add(warn(table, IndexType.STANDARD.name(), "More model indexes than database indexes")); - } else if (defIndexes.size() < dbIndexes.size()) { + } else if (defIndexes.size() < indexes.size()) { remarks.add(warn(table, IndexType.STANDARD.name(), "Model class is missing indexes")); } // TODO complete index validation. @@ -583,15 +599,6 @@ public class TableInspector { public void addColumn(ResultSet rs) throws SQLException { columns.add(rs.getString("COLUMN_NAME")); } - - public String getColumnsString() { - StatementBuilder sb = new StatementBuilder(); - for (String col : columns) { - sb.appendExceptFirst(", "); - sb.append(col); - } - return sb.toString().trim(); - } } /** @@ -629,19 +636,23 @@ public class TableInspector { * parameter list. */ private static class AnnotationBuilder extends StatementBuilder { + AnnotationBuilder() { super(); } void addParameter(String parameter) { + appendExceptFirst(", "); append(parameter); } void addParameter(String parameter, T value) { appendExceptFirst(", "); - append(parameter); - append('='); + if (!StringUtils.isNullOrEmpty(parameter)) { + append(parameter); + append('='); + } if (value instanceof List) { append("{ "); List list = (List) value; @@ -663,7 +674,7 @@ public class TableInspector { if (value instanceof String) { append('\"'); } - // TODO escape + // TODO escape append(value.toString().trim()); if (value instanceof String) { append('\"'); diff --git a/src/com/iciql/util/StatementBuilder.java b/src/com/iciql/util/StatementBuilder.java index c2974ae..65c0679 100644 --- a/src/com/iciql/util/StatementBuilder.java +++ b/src/com/iciql/util/StatementBuilder.java @@ -99,6 +99,14 @@ public class StatementBuilder { builder.append(x); return this; } + + /** + * Returns the current value of the loop counter. + * @return the loop counter + */ + public int getCount() { + return index; + } /** * Reset the loop counter. -- cgit v1.2.3