@@ -5,7 +5,7 @@ r21: { | |||
title: ${project.name} ${project.version} released | |||
id: ${project.version} | |||
date: ${project.buildDate} | |||
note: ~ | |||
note: "If you are upgrading and using EnumId mapping you will have to update your models to define the target class for the enumid mapping." | |||
html: ~ | |||
text: ~ | |||
security: ~ | |||
@@ -13,11 +13,13 @@ r21: { | |||
- Return null NPE in selectFirst() if list is empty (pr-5) | |||
- Fix Moxie toolkit download URL (pr-6) | |||
- Be more careful with primitive numeric type rollovers | |||
changes: ~ | |||
changes: | |||
- Revised EnumId interface to support generic types | |||
additions: | |||
- Add syntax for IN and NOT IN (pr-7) | |||
- Add support for nested AND/OR conditions (pr-8) | |||
- Add support for mapping SQL BOOLEAN to primitive numeric types, not just object numeric types | |||
- Add support for customizing EnumId mapping, you can now map enum constants to values of your choice as long as they are a standard type | |||
dependencyChanges: | |||
- H2 1.4 | |||
- HSQLDB 2.3 |
@@ -171,7 +171,7 @@ import java.lang.annotation.Target; | |||
* <p> | |||
* Automatic model generation: you may automatically generate model classes as | |||
* strings with the Db and DbInspector objects: | |||
* | |||
* | |||
* <pre> | |||
* 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) | |||
* </pre> | |||
* | |||
* | |||
* Or you may use the GenerateModels tool to generate and save your classes to | |||
* the file system: | |||
* | |||
* | |||
* <pre> | |||
* 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 | |||
* </pre> | |||
* | |||
* | |||
* Model validation: you may validate your model class with DbInspector object. | |||
* The DbInspector will report errors, warnings, and suggestions: | |||
* | |||
* | |||
* <pre> | |||
* 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. | |||
* <p> | |||
* | |||
* | |||
* @IQVersion(1) | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@@ -229,7 +229,7 @@ public interface Iciql { | |||
/** | |||
* An annotation for a schema. | |||
* <p> | |||
* | |||
* | |||
* @IQSchema("PUBLIC") | |||
*/ | |||
@Retention(RetentionPolicy.RUNTIME) | |||
@@ -278,9 +278,9 @@ public interface Iciql { | |||
* <li>com.iciql.iciql.IndexType.HASH | |||
* <li>com.iciql.iciql.IndexType.UNIQUE_HASH | |||
* </ul> | |||
* | |||
* | |||
* 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. | |||
* <p> | |||
* <ul> | |||
* <li>@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 { | |||
* </ul> | |||
*/ | |||
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'. | |||
* <ul> | |||
@@ -410,7 +410,7 @@ public interface Iciql { | |||
public @interface IQContraintsForeignKey { | |||
IQContraintForeignKey[] value() default {}; | |||
} | |||
/** | |||
* A unique constraint annotation. | |||
* <p> | |||
@@ -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. | |||
* <p> | |||
@@ -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> { | |||
X enumId(); | |||
Class<X> enumIdClass(); | |||
} | |||
/** | |||
* Enumeration representing how to map a java.lang.Enum to a column. | |||
* <p> | |||
* <ul> | |||
* <li>NAME - name() : string | |||
* <li>ORDINAL - ordinal() : int | |||
* <li>ENUMID - enumId() : int | |||
* <li>ENUMID - enumId() : X | |||
* </ul> | |||
* | |||
* | |||
* @see com.iciql.Iciql.EnumId interface | |||
*/ | |||
public enum EnumType { | |||
@@ -700,14 +703,14 @@ public interface Iciql { | |||
* annotation. | |||
* <p> | |||
* The default mapping is by NAME. | |||
* | |||
* | |||
* <pre> | |||
* IQEnum(EnumType.NAME) | |||
* </pre> | |||
* | |||
* | |||
* 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. |
@@ -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 |
@@ -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 <T> | |||
* the table type | |||
*/ | |||
@@ -79,7 +79,7 @@ public class TableDefinition<T> { | |||
/** | |||
* The meta data of a constraint on foreign key. | |||
*/ | |||
public static class ConstraintForeignKeyDefinition { | |||
public String constraintName; | |||
@@ -90,18 +90,18 @@ public class TableDefinition<T> { | |||
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<String> uniqueColumns; | |||
} | |||
/** | |||
* The meta data of a field. | |||
*/ | |||
@@ -118,6 +118,7 @@ public class TableDefinition<T> { | |||
boolean nullable; | |||
String defaultValue; | |||
EnumType enumType; | |||
Class<?> enumTypeClass; | |||
boolean isPrimitive; | |||
String constraint; | |||
@@ -161,12 +162,12 @@ public class TableDefinition<T> { | |||
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<T> { | |||
/** | |||
* Define a primary key by the specified model fields. | |||
* | |||
* | |||
* @param modelFields | |||
* the ordered list of model fields | |||
*/ | |||
@@ -239,7 +240,7 @@ public class TableDefinition<T> { | |||
/** | |||
* Define a primary key by the specified column names. | |||
* | |||
* | |||
* @param columnNames | |||
* the ordered list of column names | |||
*/ | |||
@@ -270,7 +271,7 @@ public class TableDefinition<T> { | |||
/** | |||
* Defines an index with the specified model fields. | |||
* | |||
* | |||
* @param name | |||
* the index name (optional) | |||
* @param type | |||
@@ -285,7 +286,7 @@ public class TableDefinition<T> { | |||
/** | |||
* 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<T> { | |||
/** | |||
* Defines an unique constraint with the specified model fields. | |||
* | |||
* | |||
* @param name | |||
* the constraint name (optional) | |||
* @param modelFields | |||
@@ -318,7 +319,7 @@ public class TableDefinition<T> { | |||
/** | |||
* Defines an unique constraint. | |||
* | |||
* | |||
* @param name | |||
* @param columnNames | |||
*/ | |||
@@ -335,7 +336,7 @@ public class TableDefinition<T> { | |||
/** | |||
* 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<T> { | |||
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<T> { | |||
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<T> { | |||
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<T> { | |||
defaultValue = col.defaultValue(); | |||
} | |||
} | |||
boolean hasConstraint = f.isAnnotationPresent(IQConstraint.class); | |||
if (hasConstraint) { | |||
IQConstraint con = f.getAnnotation(IQConstraint.class); | |||
@@ -539,13 +546,14 @@ public class TableDefinition<T> { | |||
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<String> primaryKey = Utils.newArrayList(); | |||
int primitiveBoolean = 0; | |||
for (FieldDefinition fieldDef : fields) { | |||
@@ -623,7 +631,7 @@ public class TableDefinition<T> { | |||
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<T> { | |||
// 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<T> { | |||
} | |||
} | |||
} | |||
// create foreign keys constraints | |||
for (ConstraintForeignKeyDefinition constraint : constraintsForeignKey) { | |||
stat = new SQLStatement(db); | |||
@@ -1004,17 +1012,17 @@ public class TableDefinition<T> { | |||
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<T> { | |||
addIndex(index); | |||
} | |||
} | |||
if (clazz.isAnnotationPresent(IQContraintUnique.class)) { | |||
// single table unique constraint | |||
IQContraintUnique constraint = clazz.getAnnotation(IQContraintUnique.class); | |||
@@ -1042,7 +1050,7 @@ public class TableDefinition<T> { | |||
addConstraintUnique(constraint); | |||
} | |||
} | |||
if (clazz.isAnnotationPresent(IQContraintForeignKey.class)) { | |||
// single table constraint | |||
IQContraintForeignKey constraint = clazz.getAnnotation(IQContraintForeignKey.class); | |||
@@ -1056,7 +1064,7 @@ public class TableDefinition<T> { | |||
addConstraintForeignKey(constraint); | |||
} | |||
} | |||
} | |||
private void addConstraintForeignKey(IQContraintForeignKey constraint) { | |||
@@ -1064,15 +1072,15 @@ public class TableDefinition<T> { | |||
List<String> referenceColumns = Arrays.asList(constraint.referenceColumns()); | |||
addConstraintForeignKey(constraint.name(), foreignColumns, constraint.referenceName(), referenceColumns, constraint.deleteType(), constraint.updateType(), constraint.deferrabilityType()); | |||
} | |||
private void addConstraintUnique(IQContraintUnique constraint) { | |||
List<String> 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<T> { | |||
List<ConstraintUniqueDefinition> getContraintsUnique() { | |||
return constraintsUnique; | |||
} | |||
List<ConstraintForeignKeyDefinition> getContraintsForeignKey() { | |||
return constraintsForeignKey; | |||
} | |||
private void initObject(Object obj, Map<Object, FieldDefinition> map) { | |||
for (FieldDefinition def : fields) { | |||
Object newValue = def.initWithNewObject(obj); | |||
@@ -1152,13 +1160,13 @@ public class TableDefinition<T> { | |||
* 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 | |||
*/ |
@@ -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); |
@@ -31,6 +31,7 @@ import com.iciql.test.models.EnumModels; | |||
import com.iciql.test.models.EnumModels.EnumIdModel; | |||
import com.iciql.test.models.EnumModels.EnumOrdinalModel; | |||
import com.iciql.test.models.EnumModels.EnumStringModel; | |||
import com.iciql.test.models.EnumModels.Genus; | |||
import com.iciql.test.models.EnumModels.Tree; | |||
/** | |||
@@ -58,6 +59,7 @@ public class EnumsTest { | |||
testIntEnums(new EnumIdModel()); | |||
testIntEnums(new EnumOrdinalModel()); | |||
testStringEnums(new EnumStringModel()); | |||
testStringEnumIds(new EnumStringModel()); | |||
} | |||
private void testIntEnums(EnumModels e) { | |||
@@ -110,7 +112,31 @@ public class EnumsTest { | |||
list = db.from(e).where(e.tree()).between(Tree.MAPLE).and(Tree.PINE).select(); | |||
assertEquals(3, list.size()); | |||
} | |||
private void testStringEnumIds(EnumModels e) { | |||
// ensure all records inserted | |||
long count = db.from(e).selectCount(); | |||
assertEquals(5, count); | |||
// special case: | |||
// value is first enum constant which is also the alias object. | |||
// the first enum constant is used as the alias because we can not | |||
// instantiate an enum reflectively. | |||
EnumModels firstEnumValue = db.from(e).where(e.genus()).is(Genus.PINUS).selectFirst(); | |||
assertEquals(Tree.PINE, firstEnumValue.tree()); | |||
assertEquals(Genus.PINUS, firstEnumValue.genus()); | |||
EnumModels model = db.from(e).where(e.genus()).is(Genus.JUGLANS).selectFirst(); | |||
assertEquals(400, model.id.intValue()); | |||
assertEquals(Tree.WALNUT, model.tree()); | |||
assertEquals(Genus.JUGLANS, model.genus()); | |||
List<EnumModels> list = db.from(e).where(e.genus()).isNot(Genus.BETULA).select(); | |||
assertEquals(count - 1, list.size()); | |||
} | |||
@Test | |||
public void testMultipleEnumInstances() { | |||
BadEnums b = new BadEnums(); | |||
@@ -121,7 +147,7 @@ public class EnumsTest { | |||
assertTrue(e.getMessage(), e.getMessage().startsWith("Can not explicitly reference Tree")); | |||
} | |||
} | |||
public static class BadEnums { | |||
Tree tree1 = Tree.BIRCH; | |||
Tree tree2 = Tree.MAPLE; |
@@ -34,14 +34,14 @@ public abstract class EnumModels { | |||
/** | |||
* Test of @IQEnum annotated enumeration. This strategy is the default | |||
* strategy for all fields of the Tree enum. | |||
* | |||
* | |||
* Individual Tree field declarations can override this strategy by | |||
* specifying a different @IQEnum annotation. | |||
* | |||
* | |||
* Here ORDINAL specifies that this enum will be mapped to an INT column. | |||
*/ | |||
@IQEnum(EnumType.ENUMID) | |||
public enum Tree implements EnumId { | |||
public enum Tree implements EnumId<Integer> { | |||
PINE(10), OAK(20), BIRCH(30), WALNUT(40), MAPLE(50); | |||
private int enumid; | |||
@@ -51,9 +51,39 @@ public abstract class EnumModels { | |||
} | |||
@Override | |||
public int enumId() { | |||
public Integer enumId() { | |||
return enumid; | |||
} | |||
@Override | |||
public Class<Integer> enumIdClass() { | |||
return Integer.class; | |||
} | |||
} | |||
/** | |||
* Enum for testing custom ENUMID mapping. | |||
*/ | |||
@IQEnum(EnumType.ENUMID) | |||
public enum Genus implements EnumId<String> { | |||
PINUS("pinaceae"), QUERCUS("fagaceae"), BETULA("betulaceae"), JUGLANS("juglandaceae"), ACER("aceraceae"); | |||
private String family; | |||
Genus(String id) { | |||
this.family = id; | |||
} | |||
@Override | |||
public String enumId() { | |||
return family; | |||
} | |||
@Override | |||
public Class<String> enumIdClass() { | |||
return String.class; | |||
} | |||
} | |||
@IQColumn(primaryKey = true) | |||
@@ -61,6 +91,8 @@ public abstract class EnumModels { | |||
public abstract Tree tree(); | |||
public abstract Genus genus(); | |||
/** | |||
* Test model for enum-as-enumid. | |||
*/ | |||
@@ -72,12 +104,18 @@ public abstract class EnumModels { | |||
@IQColumn | |||
private Tree tree; | |||
// no need to specify ENUMID type as the enumeration definition | |||
// specifies it. | |||
@IQColumn | |||
private Genus genus; | |||
public EnumIdModel() { | |||
} | |||
public EnumIdModel(int id, Tree tree) { | |||
public EnumIdModel(int id, Tree tree, Genus genus) { | |||
this.id = id; | |||
this.tree = tree; | |||
this.genus = genus; | |||
} | |||
@Override | |||
@@ -85,10 +123,17 @@ public abstract class EnumModels { | |||
return tree; | |||
} | |||
@Override | |||
public Genus genus() { | |||
return genus; | |||
} | |||
public static List<EnumIdModel> createList() { | |||
return Arrays.asList(new EnumIdModel(400, Tree.WALNUT), new EnumIdModel(200, Tree.OAK), | |||
new EnumIdModel(500, Tree.MAPLE), new EnumIdModel(300, Tree.BIRCH), new EnumIdModel(100, | |||
Tree.PINE)); | |||
return Arrays.asList(new EnumIdModel(400, Tree.WALNUT, Genus.JUGLANS), | |||
new EnumIdModel(200, Tree.OAK, Genus.QUERCUS), | |||
new EnumIdModel(500, Tree.MAPLE, Genus.ACER), | |||
new EnumIdModel(300, Tree.BIRCH, Genus.BETULA), | |||
new EnumIdModel(100, Tree.PINE, Genus.PINUS)); | |||
} | |||
} | |||
@@ -103,10 +148,13 @@ public abstract class EnumModels { | |||
@IQColumn | |||
private Tree tree; | |||
@IQColumn | |||
private Genus genus; | |||
public EnumOrdinalModel() { | |||
} | |||
public EnumOrdinalModel(int id, Tree tree) { | |||
public EnumOrdinalModel(int id, Tree tree, Genus genus) { | |||
this.id = id; | |||
this.tree = tree; | |||
} | |||
@@ -116,10 +164,17 @@ public abstract class EnumModels { | |||
return tree; | |||
} | |||
@Override | |||
public Genus genus() { | |||
return genus; | |||
} | |||
public static List<EnumOrdinalModel> createList() { | |||
return Arrays.asList(new EnumOrdinalModel(400, Tree.WALNUT), new EnumOrdinalModel(200, Tree.OAK), | |||
new EnumOrdinalModel(500, Tree.MAPLE), new EnumOrdinalModel(300, Tree.BIRCH), | |||
new EnumOrdinalModel(100, Tree.PINE)); | |||
return Arrays.asList(new EnumOrdinalModel(400, Tree.WALNUT, Genus.JUGLANS), | |||
new EnumOrdinalModel(200, Tree.OAK, Genus.QUERCUS), | |||
new EnumOrdinalModel(500, Tree.MAPLE, Genus.ACER), | |||
new EnumOrdinalModel(300, Tree.BIRCH, Genus.BETULA), | |||
new EnumOrdinalModel(100, Tree.PINE, Genus.PINUS)); | |||
} | |||
} | |||
@@ -135,12 +190,16 @@ public abstract class EnumModels { | |||
@IQColumn(length = 25) | |||
private Tree tree; | |||
@IQColumn(trim = true, length = 25) | |||
private Genus genus; | |||
public EnumStringModel() { | |||
} | |||
public EnumStringModel(int id, Tree tree) { | |||
public EnumStringModel(int id, Tree tree, Genus genus) { | |||
this.id = id; | |||
this.tree = tree; | |||
this.genus = genus; | |||
} | |||
@Override | |||
@@ -148,10 +207,17 @@ public abstract class EnumModels { | |||
return tree; | |||
} | |||
@Override | |||
public Genus genus() { | |||
return genus; | |||
} | |||
public static List<EnumStringModel> createList() { | |||
return Arrays.asList(new EnumStringModel(400, Tree.WALNUT), new EnumStringModel(200, Tree.OAK), | |||
new EnumStringModel(500, Tree.MAPLE), new EnumStringModel(300, Tree.BIRCH), | |||
new EnumStringModel(100, Tree.PINE)); | |||
return Arrays.asList(new EnumStringModel(400, Tree.WALNUT, Genus.JUGLANS), | |||
new EnumStringModel(200, Tree.OAK, Genus.QUERCUS), | |||
new EnumStringModel(500, Tree.MAPLE, Genus.ACER), | |||
new EnumStringModel(300, Tree.BIRCH, Genus.BETULA), | |||
new EnumStringModel(100, Tree.PINE, Genus.PINUS)); | |||
} | |||
} | |||
} |