From 6889aa2e367d4658105c1bc2514afccb425770f3 Mon Sep 17 00:00:00 2001 From: James Moger Date: Mon, 6 Oct 2014 12:46:50 -0400 Subject: Support generic types for EnumId mapping --- src/main/java/com/iciql/Iciql.java | 65 +++++++++++----------- src/main/java/com/iciql/ModelUtils.java | 24 +++++--- src/main/java/com/iciql/TableDefinition.java | 82 +++++++++++++++------------- src/main/java/com/iciql/util/Utils.java | 65 +++++++++++++++++----- 4 files changed, 144 insertions(+), 92 deletions(-) (limited to 'src/main/java') diff --git a/src/main/java/com/iciql/Iciql.java b/src/main/java/com/iciql/Iciql.java index 9f73ffa..521e460 100644 --- a/src/main/java/com/iciql/Iciql.java +++ b/src/main/java/com/iciql/Iciql.java @@ -171,7 +171,7 @@ import java.lang.annotation.Target; *

* Automatic model generation: you may automatically generate model classes as * strings with the Db and DbInspector objects: - * + * *

  * Db db = Db.open("jdbc:h2:mem:", "sa", "sa");
  * DbInspector inspector = new DbInspector(db);
@@ -179,10 +179,10 @@ import java.lang.annotation.Target;
  *         inspector.generateModel(schema, table, packageName,
  *         annotateSchema, trimStrings)
  * 
- * + * * Or you may use the GenerateModels tool to generate and save your classes to * the file system: - * + * *
  * java -jar iciql.jar
  *      -url "jdbc:h2:mem:"
@@ -190,10 +190,10 @@ import java.lang.annotation.Target;
  *      -package packageName -folder destination
  *      -annotateSchema false -trimStrings true
  * 
- * + * * Model validation: you may validate your model class with DbInspector object. * The DbInspector will report errors, warnings, and suggestions: - * + * *
  * Db db = Db.open("jdbc:h2:mem:", "sa", "sa");
  * DbInspector inspector = new DbInspector(db);
@@ -208,7 +208,7 @@ public interface Iciql {
 	/**
 	 * An annotation for an iciql version.
 	 * 

- * + * * @IQVersion(1) */ @Retention(RetentionPolicy.RUNTIME) @@ -229,7 +229,7 @@ public interface Iciql { /** * An annotation for a schema. *

- * + * * @IQSchema("PUBLIC") */ @Retention(RetentionPolicy.RUNTIME) @@ -278,9 +278,9 @@ public interface Iciql { *

  • com.iciql.iciql.IndexType.HASH *
  • com.iciql.iciql.IndexType.UNIQUE_HASH * - * + * * HASH indexes may only be valid for single column indexes. - * + * */ IndexType type() default IndexType.STANDARD; @@ -307,21 +307,21 @@ public interface Iciql { public static enum ConstraintUpdateType { UNSET, CASCADE, RESTRICT, SET_NULL, NO_ACTION, SET_DEFAULT; } - + /** * Enumeration defining the deferrability. */ public static enum ConstraintDeferrabilityType { UNSET, DEFERRABLE_INITIALLY_DEFERRED, DEFERRABLE_INITIALLY_IMMEDIATE, NOT_DEFERRABLE; } - + /** * A foreign key constraint annotation. *

    *

      *
    • @IQContraintForeignKey( - * foreignColumns = { "idaccount"}, - * referenceName = "account", + * foreignColumns = { "idaccount"}, + * referenceName = "account", * referenceColumns = { "id" }, * deleteType = ConstrainDeleteType.CASCADE, * updateType = ConstraintUpdateType.NO_ACTION ) @@ -372,7 +372,7 @@ public interface Iciql { *
    */ ConstraintDeferrabilityType deferrabilityType() default ConstraintDeferrabilityType.UNSET; - + /** * The source table for the columns defined as foreign. */ @@ -391,7 +391,7 @@ public interface Iciql { * The reference table for the columns defined as references. */ String referenceName() default ""; - + /** * Columns defined as 'references'. *
      @@ -410,7 +410,7 @@ public interface Iciql { public @interface IQContraintsForeignKey { IQContraintForeignKey[] value() default {}; } - + /** * A unique constraint annotation. *

      @@ -447,7 +447,7 @@ public interface Iciql { public @interface IQContraintsUnique { IQContraintUnique[] value() default {}; } - + /** * Annotation to define a view. */ @@ -463,7 +463,7 @@ public interface Iciql { * model class is not annotated with IQView. Default: unspecified. */ String name() default ""; - + /** * The source table for the view. *

      @@ -491,7 +491,7 @@ public interface Iciql { */ boolean annotationsOnly() default true; } - + /** * String snippet defining SQL constraints for a field. Use "this" as * a placeholder for the column name. "this" will be substituted at @@ -507,8 +507,8 @@ public interface Iciql { public @interface IQConstraint { String value() default ""; - } - + } + /** * Annotation to specify multiple indexes. */ @@ -657,7 +657,7 @@ public interface Iciql { */ String defaultValue() default ""; - } + } /** * Interface for using the EnumType.ENUMID enumeration mapping strategy. @@ -665,19 +665,22 @@ public interface Iciql { * Enumerations wishing to use EnumType.ENUMID must implement this * interface. */ - public interface EnumId { - int enumId(); + public interface EnumId { + X enumId(); + Class enumIdClass(); } + + /** * Enumeration representing how to map a java.lang.Enum to a column. *

      *

        *
      • NAME - name() : string *
      • ORDINAL - ordinal() : int - *
      • ENUMID - enumId() : int + *
      • ENUMID - enumId() : X *
      - * + * * @see com.iciql.Iciql.EnumId interface */ public enum EnumType { @@ -700,14 +703,14 @@ public interface Iciql { * annotation. *

      * The default mapping is by NAME. - * + * *

       	 * IQEnum(EnumType.NAME)
       	 * 
      - * + * * A string mapping will generate either a VARCHAR, if IQColumn.length > 0 * or a TEXT column if IQColumn.length == 0 - * + * */ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD, ElementType.TYPE }) @@ -720,9 +723,9 @@ public interface Iciql { */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) - public @interface IQIgnore{ + public @interface IQIgnore{ } - + /** * This method is called to let the table define the primary key, indexes, * and the table name. diff --git a/src/main/java/com/iciql/ModelUtils.java b/src/main/java/com/iciql/ModelUtils.java index 56e6440..c3fd847 100644 --- a/src/main/java/com/iciql/ModelUtils.java +++ b/src/main/java/com/iciql/ModelUtils.java @@ -147,7 +147,7 @@ class ModelUtils { /** * Returns a SQL type mapping for a Java class. - * + * * @param fieldDef * the field to map * @return @@ -157,8 +157,14 @@ class ModelUtils { if (fieldClass.isEnum()) { switch (fieldDef.enumType) { case ORDINAL: - case ENUMID: return "INT"; + case ENUMID: + String sqlType = SUPPORTED_TYPES.get(fieldDef.enumTypeClass); + if (sqlType == null) { + throw new IciqlException("Unsupported enum mapping type {0} for {1}", + fieldDef.enumTypeClass, fieldDef.columnName); + } + return sqlType; case NAME: default: return "VARCHAR"; @@ -172,7 +178,7 @@ class ModelUtils { /** * Returns the Java class for a given SQL type. - * + * * @param sqlType * @param dateTimeClass * the preferred date class (java.util.Date or @@ -211,7 +217,7 @@ class ModelUtils { /** * Tries to create a convert a SQL table name to a camel case class name. - * + * * @param tableName * the SQL table name * @return the class name @@ -239,7 +245,7 @@ class ModelUtils { /** * Ensures that SQL column names don't collide with Java keywords. - * + * * @param columnName * the column name * @return the Java field name @@ -254,7 +260,7 @@ class ModelUtils { /** * Converts a DEFAULT clause value into an object. - * + * * @param field * definition * @return object @@ -360,7 +366,7 @@ class ModelUtils { /** * Converts the object into a DEFAULT clause value. - * + * * @param o * the default object * @return the value formatted for a DEFAULT clause @@ -395,7 +401,7 @@ class ModelUtils { /** * Checks the formatting of IQColumn.defaultValue(). - * + * * @param defaultValue * the default value * @return true if it is @@ -412,7 +418,7 @@ class ModelUtils { /** * Checks to see if the default value matches the class. - * + * * @param modelClass * the class * @param defaultValue diff --git a/src/main/java/com/iciql/TableDefinition.java b/src/main/java/com/iciql/TableDefinition.java index e0cc169..0cb1ad2 100644 --- a/src/main/java/com/iciql/TableDefinition.java +++ b/src/main/java/com/iciql/TableDefinition.java @@ -37,11 +37,11 @@ import com.iciql.Iciql.EnumId; import com.iciql.Iciql.EnumType; import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQConstraint; +import com.iciql.Iciql.IQContraintForeignKey; import com.iciql.Iciql.IQContraintUnique; +import com.iciql.Iciql.IQContraintsForeignKey; import com.iciql.Iciql.IQContraintsUnique; import com.iciql.Iciql.IQEnum; -import com.iciql.Iciql.IQContraintForeignKey; -import com.iciql.Iciql.IQContraintsForeignKey; import com.iciql.Iciql.IQIgnore; import com.iciql.Iciql.IQIndex; import com.iciql.Iciql.IQIndexes; @@ -58,7 +58,7 @@ import com.iciql.util.Utils; /** * A table definition contains the index definitions of a table, the field * definitions, the table name, and other meta data. - * + * * @param * the table type */ @@ -79,7 +79,7 @@ public class TableDefinition { /** * The meta data of a constraint on foreign key. */ - + public static class ConstraintForeignKeyDefinition { public String constraintName; @@ -90,18 +90,18 @@ public class TableDefinition { public ConstraintUpdateType updateType = ConstraintUpdateType.UNSET; public ConstraintDeferrabilityType deferrabilityType = ConstraintDeferrabilityType.UNSET; } - + /** * The meta data of a unique constraint. */ - + public static class ConstraintUniqueDefinition { public String constraintName; public List uniqueColumns; } - - + + /** * The meta data of a field. */ @@ -118,6 +118,7 @@ public class TableDefinition { boolean nullable; String defaultValue; EnumType enumType; + Class enumTypeClass; boolean isPrimitive; String constraint; @@ -161,12 +162,12 @@ public class TableDefinition { throw new IciqlException(e); } } - + @Override public int hashCode() { return columnName.hashCode(); } - + @Override public boolean equals(Object o) { if (o instanceof FieldDefinition) { @@ -228,7 +229,7 @@ public class TableDefinition { /** * Define a primary key by the specified model fields. - * + * * @param modelFields * the ordered list of model fields */ @@ -239,7 +240,7 @@ public class TableDefinition { /** * Define a primary key by the specified column names. - * + * * @param columnNames * the ordered list of column names */ @@ -270,7 +271,7 @@ public class TableDefinition { /** * Defines an index with the specified model fields. - * + * * @param name * the index name (optional) * @param type @@ -285,7 +286,7 @@ public class TableDefinition { /** * Defines an index with the specified column names. - * + * * @param type * the index type (STANDARD, HASH, UNIQUE, UNIQUE_HASH) * @param columnNames @@ -305,7 +306,7 @@ public class TableDefinition { /** * Defines an unique constraint with the specified model fields. - * + * * @param name * the constraint name (optional) * @param modelFields @@ -318,7 +319,7 @@ public class TableDefinition { /** * Defines an unique constraint. - * + * * @param name * @param columnNames */ @@ -335,7 +336,7 @@ public class TableDefinition { /** * Defines a foreign key constraint with the specified model fields. - * + * * @param name * the constraint name (optional) * @param modelFields @@ -426,7 +427,7 @@ public class TableDefinition { if (inheritColumns) { Class superClass = clazz.getSuperclass(); classFields.addAll(Arrays.asList(superClass.getDeclaredFields())); - + if (superClass.isAnnotationPresent(IQView.class)) { IQView superView = superClass.getAnnotation(IQView.class); if (superView.inheritColumns()) { @@ -461,6 +462,7 @@ public class TableDefinition { boolean trim = false; boolean nullable = !f.getType().isPrimitive(); EnumType enumType = null; + Class enumTypeClass = null; String defaultValue = ""; String constraint = ""; // configure Java -> SQL enum mapping @@ -476,6 +478,11 @@ public class TableDefinition { IQEnum iqenum = f.getAnnotation(IQEnum.class); enumType = iqenum.value(); } + + if (EnumId.class.isAssignableFrom(f.getType())) { + // custom enumid mapping + enumTypeClass = ((EnumId) f.getType().getEnumConstants()[0]).enumIdClass(); + } } // try using default object @@ -515,7 +522,7 @@ public class TableDefinition { defaultValue = col.defaultValue(); } } - + boolean hasConstraint = f.isAnnotationPresent(IQConstraint.class); if (hasConstraint) { IQConstraint con = f.getAnnotation(IQConstraint.class); @@ -539,13 +546,14 @@ public class TableDefinition { fieldDef.nullable = nullable; fieldDef.defaultValue = defaultValue; fieldDef.enumType = enumType; + fieldDef.enumTypeClass = enumTypeClass; fieldDef.dataType = ModelUtils.getDataType(fieldDef); fieldDef.constraint = constraint; uniqueFields.add(fieldDef); } } fields.addAll(uniqueFields); - + List primaryKey = Utils.newArrayList(); int primitiveBoolean = 0; for (FieldDefinition fieldDef : fields) { @@ -623,7 +631,7 @@ public class TableDefinition { if (!EnumId.class.isAssignableFrom(value.getClass())) { throw new IciqlException(field.field.getName() + " does not implement EnumId!"); } - EnumId enumid = (EnumId) value; + EnumId enumid = (EnumId) value; return enumid.enumId(); } } @@ -643,7 +651,7 @@ public class TableDefinition { // return the value unchanged return value; } - + PreparedStatement createInsertStatement(Db db, Object obj, boolean returnKey) { SQLStatement stat = new SQLStatement(db); StatementBuilder buff = new StatementBuilder("INSERT INTO "); @@ -896,7 +904,7 @@ public class TableDefinition { } } } - + // create foreign keys constraints for (ConstraintForeignKeyDefinition constraint : constraintsForeignKey) { stat = new SQLStatement(db); @@ -1004,17 +1012,17 @@ public class TableDefinition { viewTableName = parentView.tableName(); } } - + if (StringUtils.isNullOrEmpty(viewTableName)) { // still missing view table name throw new IciqlException("View model class \"{0}\" is missing a table name!", tableName); } } - + // allow control over createTableIfRequired() createIfRequired = viewAnnotation.create(); } - + if (clazz.isAnnotationPresent(IQIndex.class)) { // single table index IQIndex index = clazz.getAnnotation(IQIndex.class); @@ -1028,7 +1036,7 @@ public class TableDefinition { addIndex(index); } } - + if (clazz.isAnnotationPresent(IQContraintUnique.class)) { // single table unique constraint IQContraintUnique constraint = clazz.getAnnotation(IQContraintUnique.class); @@ -1042,7 +1050,7 @@ public class TableDefinition { addConstraintUnique(constraint); } } - + if (clazz.isAnnotationPresent(IQContraintForeignKey.class)) { // single table constraint IQContraintForeignKey constraint = clazz.getAnnotation(IQContraintForeignKey.class); @@ -1056,7 +1064,7 @@ public class TableDefinition { addConstraintForeignKey(constraint); } } - + } private void addConstraintForeignKey(IQContraintForeignKey constraint) { @@ -1064,15 +1072,15 @@ public class TableDefinition { List referenceColumns = Arrays.asList(constraint.referenceColumns()); addConstraintForeignKey(constraint.name(), foreignColumns, constraint.referenceName(), referenceColumns, constraint.deleteType(), constraint.updateType(), constraint.deferrabilityType()); } - + private void addConstraintUnique(IQContraintUnique constraint) { List uniqueColumns = Arrays.asList(constraint.uniqueColumns()); addConstraintUnique(constraint.name(), uniqueColumns); } - + /** * Defines a foreign key constraint with the specified parameters. - * + * * @param name * name of the constraint * @param foreignColumns @@ -1119,11 +1127,11 @@ public class TableDefinition { List getContraintsUnique() { return constraintsUnique; } - + List getContraintsForeignKey() { return constraintsForeignKey; } - + private void initObject(Object obj, Map map) { for (FieldDefinition def : fields) { Object newValue = def.initWithNewObject(obj); @@ -1152,13 +1160,13 @@ public class TableDefinition { * JaQu assumed that you can always use the integer index of the * reflectively mapped field definition to determine position in the result * set. - * + * * This is not always true. - * + * * iciql identifies when a select * query is executed and maps column names * to a column index from the result set. If the select statement is * explicit, then the standard assumed column index is used instead. - * + * * @param rs * @return */ diff --git a/src/main/java/com/iciql/util/Utils.java b/src/main/java/com/iciql/util/Utils.java index 64fe6b3..5b2914a 100644 --- a/src/main/java/com/iciql/util/Utils.java +++ b/src/main/java/com/iciql/util/Utils.java @@ -135,8 +135,8 @@ public class Utils { } } throw new IciqlException(e, - "Missing default constructor? Exception trying to instantiate {0}: {1}", - clazz.getName(), e.getMessage()); + "Missing default constructor? Exception trying to instantiate {0}: {1}", clazz.getName(), + e.getMessage()); } } }; @@ -214,8 +214,7 @@ public class Utils { } } } - throw new IciqlException(e, - "Missing default constructor?! Exception trying to instantiate {0}: {1}", + throw new IciqlException(e, "Missing default constructor?! Exception trying to instantiate {0}: {1}", clazz.getName(), e.getMessage()); } } @@ -337,7 +336,7 @@ public class Utils { if (!EnumId.class.isAssignableFrom(o.getClass())) { throw new IciqlException("Can not convert the enum {0} using ENUMID", o); } - EnumId enumid = (EnumId) o; + EnumId enumid = (EnumId) o; return enumid.enumId(); case NAME: default: @@ -367,17 +366,39 @@ public class Utils { } // find name match - for (Enum value : values) { - if (value.name().equalsIgnoreCase(name)) { - return value; + if (type.equals(EnumType.ENUMID)) { + // ENUMID mapping + for (Enum value : values) { + EnumId enumid = (EnumId) value; + if (enumid.enumId().equals(name)) { + return value; + } + } + } else if (type.equals(EnumType.NAME)) { + // standard Enum.name() mapping + for (Enum value : values) { + if (value.name().equalsIgnoreCase(name)) { + return value; + } } } } else if (String.class.isAssignableFrom(currentType)) { // VARCHAR field String name = (String) o; - for (Enum value : values) { - if (value.name().equalsIgnoreCase(name)) { - return value; + if (type.equals(EnumType.ENUMID)) { + // ENUMID mapping + for (Enum value : values) { + EnumId enumid = (EnumId) value; + if (enumid.enumId().equals(name)) { + return value; + } + } + } else if (type.equals(EnumType.NAME)) { + // standard Enum.name() mapping + for (Enum value : values) { + if (value.name().equalsIgnoreCase(name)) { + return value; + } } } } else if (Number.class.isAssignableFrom(currentType)) { @@ -397,8 +418,23 @@ public class Utils { } // ENUMID mapping for (Enum value : values) { - EnumId enumid = (EnumId) value; - if (enumid.enumId() == n) { + EnumId enumid = (EnumId) value; + if (enumid.enumId().equals(n)) { + return value; + } + } + } + } else { + // custom object mapping + if (type.equals(EnumType.ENUMID)) { + if (!EnumId.class.isAssignableFrom(targetType)) { + throw new IciqlException("Can not convert the value {0} from {1} to {2} using ENUMID", o, + currentType, targetType); + } + // ENUMID mapping + for (Enum value : values) { + EnumId enumid = (EnumId) value; + if (enumid.enumId().equals(o)) { return value; } } @@ -456,8 +492,7 @@ public class Utils { length = Integer.MAX_VALUE; } int block = Math.min(BUFFER_BLOCK_SIZE, length); - ByteArrayOutputStream out = new ByteArrayOutputStream(length == Integer.MAX_VALUE ? block - : length); + ByteArrayOutputStream out = new ByteArrayOutputStream(length == Integer.MAX_VALUE ? block : length); byte[] buff = new byte[block]; while (length > 0) { int len = Math.min(block, length); -- cgit v1.2.3