diff options
author | James Moger <james.moger@gmail.com> | 2011-08-09 11:28:48 -0400 |
---|---|---|
committer | James Moger <james.moger@gmail.com> | 2011-08-09 11:28:48 -0400 |
commit | 1a2339a9f1f96ddca64ab392dd7f9e1621e6c201 (patch) | |
tree | 413631f6e46e2bb2826e67f98df5b5180468833b /src | |
parent | fec6df5eaac334540e220d7f7c05c9d21357f554 (diff) | |
download | iciql-1a2339a9f1f96ddca64ab392dd7f9e1621e6c201.tar.gz iciql-1a2339a9f1f96ddca64ab392dd7f9e1621e6c201.zip |
IQTable.primaryKey is now an array. Default values from objects.
Also fixed an error with allowing null objects. Noted rapid churn of
iciql in documentation. Changed build constants for next release.
Diffstat (limited to 'src')
-rw-r--r-- | src/com/iciql/Constants.java | 10 | ||||
-rw-r--r-- | src/com/iciql/Db.java | 2 | ||||
-rw-r--r-- | src/com/iciql/DbVersion.java | 2 | ||||
-rw-r--r-- | src/com/iciql/Iciql.java | 69 | ||||
-rw-r--r-- | src/com/iciql/IciqlException.java | 5 | ||||
-rw-r--r-- | src/com/iciql/ModelUtils.java | 45 | ||||
-rw-r--r-- | src/com/iciql/TableDefinition.java | 86 | ||||
-rw-r--r-- | src/com/iciql/TableInspector.java | 48 | ||||
-rw-r--r-- | src/com/iciql/util/Utils.java | 2 |
9 files changed, 171 insertions, 98 deletions
diff --git a/src/com/iciql/Constants.java b/src/com/iciql/Constants.java index 809c06c..917fcb0 100644 --- a/src/com/iciql/Constants.java +++ b/src/com/iciql/Constants.java @@ -25,18 +25,14 @@ public class Constants { // The build script extracts this exact line so be careful editing it
// and only use A-Z a-z 0-9 .-_ in the string.
- public static final String VERSION = "0.6.3";
+ public static final String VERSION = "0.6.4-SNAPSHOT";
// The build script extracts this exact line so be careful editing it
// and only use A-Z a-z 0-9 .-_ in the string.
- public static final String VERSION_DATE = "2011-08-08";
-
- // The build script extracts this exact line so be careful editing it
- // and only use A-Z a-z 0-9 .-_ in the string.
- public static final String API_CURRENT = "3";
+ public static final String VERSION_DATE = "PENDING";
// The build script extracts this exact line so be careful editing it
// and only use A-Z a-z 0-9 .-_ in the string.
- public static final String API_PREVIOUS = "2";
+ public static final String API_CURRENT = "4";
}
diff --git a/src/com/iciql/Db.java b/src/com/iciql/Db.java index 121e50c..621e825 100644 --- a/src/com/iciql/Db.java +++ b/src/com/iciql/Db.java @@ -114,7 +114,7 @@ public class Db { return TOKENS.get(x);
}
- private static <T> T instance(Class<T> clazz) {
+ static <T> T instance(Class<T> clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
diff --git a/src/com/iciql/DbVersion.java b/src/com/iciql/DbVersion.java index ff72ec0..2a8aa44 100644 --- a/src/com/iciql/DbVersion.java +++ b/src/com/iciql/DbVersion.java @@ -23,7 +23,7 @@ import com.iciql.Iciql.IQTable; /** * A system table to track database and table versions. */ -@IQTable(name = "_iq_versions", primaryKey = "schemaName tableName", memoryTable = true) +@IQTable(name = "_iq_versions", primaryKey = { "schemaName", "tableName"}, memoryTable = true) public class DbVersion { @IQColumn(name = "schemaName", length = 255, allowNull = false) diff --git a/src/com/iciql/Iciql.java b/src/com/iciql/Iciql.java index 64c2158..2eaf783 100644 --- a/src/com/iciql/Iciql.java +++ b/src/com/iciql/Iciql.java @@ -35,7 +35,9 @@ import java.lang.annotation.Target; * <p>
* Fully Supported Data Types:
* <table>
- * <tr><th colspan="2">All Databases</th></tr>
+ * <tr>
+ * <th colspan="2">All Databases</th>
+ * </tr>
* <tr>
* <td>java.lang.String</td>
* <td>VARCHAR (length > 0) or TEXT (length == 0)</td>
@@ -104,7 +106,9 @@ import java.lang.annotation.Target; * <td>INT<br/>
* EnumType.ENUMID</td>
* </tr>
- * <tr><th colspan="2">H2 Databases</th></tr>
+ * <tr>
+ * <th colspan="2">H2 Databases</th>
+ * </tr>
* <tr>
* <td>java.util.UUID</td>
* <td>UUID</td>
@@ -117,22 +121,38 @@ import java.lang.annotation.Target; * BUT these field types may not be used to specify compile-time clauses or
* constraints.
* <table>
- * <tr><td>byte []</td>
- * <td>BLOB</td></tr>
- * <tr><td>boolean</td>
- * <td>BIT</td></tr>
- * <tr><td>byte</td>
- * <td>TINYINT</td></tr>
- * <tr><td>short</td>
- * <td>SMALLINT</td></tr>
- * <tr><td>int</td>
- * <td>INT</td></tr>
- * <tr><td>long</td>
- * <td>BIGINT</td></tr>
- * <tr><td>float</td>
- * <td>REAL</td></tr>
- * <tr><td>double</td>
- * <td>DOUBLE</td></tr>
+ * <tr>
+ * <td>byte []</td>
+ * <td>BLOB</td>
+ * </tr>
+ * <tr>
+ * <td>boolean</td>
+ * <td>BIT</td>
+ * </tr>
+ * <tr>
+ * <td>byte</td>
+ * <td>TINYINT</td>
+ * </tr>
+ * <tr>
+ * <td>short</td>
+ * <td>SMALLINT</td>
+ * </tr>
+ * <tr>
+ * <td>int</td>
+ * <td>INT</td>
+ * </tr>
+ * <tr>
+ * <td>long</td>
+ * <td>BIGINT</td>
+ * </tr>
+ * <tr>
+ * <td>float</td>
+ * <td>REAL</td>
+ * </tr>
+ * <tr>
+ * <td>double</td>
+ * <td>DOUBLE</td>
+ * </tr>
* </table>
* <p>
* Table and field mapping: by default, the mapped table name is the class name
@@ -302,13 +322,13 @@ public interface Iciql { * then no primary key is set by the IQTable annotation. You may specify
* a composite primary key.
* <ul>
- * <li>primaryKey = "id, name"
- * <li>primaryKey = "id name"
+ * <li>single column primaryKey: value = "id"
+ * <li>compound primary key: value = { "id", "name" }
* </ul>
* The primary key may still be overridden in the define() method if the
* model class is not annotated with IQTable. Default: unspecified.
*/
- String primaryKey() default "";
+ String[] primaryKey() default {};
/**
* The inherit columns allows this model class to inherit columns from
@@ -406,6 +426,11 @@ public interface Iciql { * if the default value is specified, and auto increment is disabled,
* and primary key is disabled, then this value is included in the
* "DEFAULT ..." phrase of a column during the CREATE TABLE process.
+ * <p>
+ * Alternatively, you may specify a default object value on the field
+ * and this will be converted to a properly formatted DEFAULT expression
+ * during the CREATE TABLE process.
+ * <p>
* Default: unspecified (null).
*/
String defaultValue() default "";
@@ -435,7 +460,7 @@ public interface Iciql { */
public enum EnumType {
NAME, ORDINAL, ENUMID;
-
+
public static final EnumType DEFAULT_TYPE = NAME;
}
diff --git a/src/com/iciql/IciqlException.java b/src/com/iciql/IciqlException.java index 9f411d9..1e2a890 100644 --- a/src/com/iciql/IciqlException.java +++ b/src/com/iciql/IciqlException.java @@ -30,6 +30,11 @@ public class IciqlException extends RuntimeException { }
+ public IciqlException(Throwable t, String message, Object... parameters) {
+ super(parameters.length > 0 ? MessageFormat.format(message, parameters) : message, t);
+
+ }
+
public IciqlException(Throwable t) {
super(t);
}
diff --git a/src/com/iciql/ModelUtils.java b/src/com/iciql/ModelUtils.java index 579c7f6..2e42a7d 100644 --- a/src/com/iciql/ModelUtils.java +++ b/src/com/iciql/ModelUtils.java @@ -21,14 +21,16 @@ import static com.iciql.util.StringUtils.isNullOrEmpty; import java.lang.reflect.Method; import java.math.BigDecimal; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; import java.util.Arrays; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.regex.Pattern; -import com.iciql.Iciql.EnumType; import com.iciql.TableDefinition.FieldDefinition; import com.iciql.util.StringUtils; @@ -61,7 +63,7 @@ class ModelUtils { m.put(java.sql.Time.class, "TIME"); m.put(byte[].class, "BLOB"); m.put(UUID.class, "UUID"); - + // map primitives m.put(boolean.class, m.get(Boolean.class)); m.put(byte.class, m.get(Byte.class)); @@ -201,9 +203,9 @@ class ModelUtils { // do not map from SQL TYPE to primitive type continue; } - if (SUPPORTED_TYPES.get(clazz).equalsIgnoreCase(sqlType)) { + if (SUPPORTED_TYPES.get(clazz).equalsIgnoreCase(sqlType)) { mappedClass = clazz; - + break; } } @@ -253,6 +255,41 @@ class ModelUtils { } /** + * Converts the object into a DEFAULT clause value. + * + * @param o + * the default object + * @return the value formatted for a DEFAULT clause + */ + static String formatDefaultValue(Object o) { + Class<?> objectClass = o.getClass(); + String value = null; + if (Number.class.isAssignableFrom(objectClass)) { + // NUMBER + value = ((Number) o).toString(); + } else if (java.sql.Date.class.isAssignableFrom(objectClass)) { + // DATE + value = new SimpleDateFormat("yyyy-MM-dd").format((Date) o); + } else if (java.sql.Time.class.isAssignableFrom(objectClass)) { + // TIME + value = new SimpleDateFormat("HH:mm:ss").format((Date) o); + } else if (Date.class.isAssignableFrom(objectClass)) { + // DATETIME + value = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) o); + } else if (String.class.isAssignableFrom(objectClass)) { + // STRING + value = o.toString(); + } else if (Boolean.class.isAssignableFrom(objectClass)) { + // BOOLEAN + value = o.toString(); + } + if (value == null) { + return "''"; + } + return MessageFormat.format("''{0}''", value); + } + + /** * Checks the formatting of IQColumn.defaultValue(). * * @param defaultValue diff --git a/src/com/iciql/TableDefinition.java b/src/com/iciql/TableDefinition.java index 7b3e1e4..6e25c91 100644 --- a/src/com/iciql/TableDefinition.java +++ b/src/com/iciql/TableDefinition.java @@ -267,6 +267,7 @@ public class TableDefinition<T> { classFields.addAll(Arrays.asList(superClass.getDeclaredFields()));
}
+ T defaultObject = Db.instance(clazz);
for (Field f : classFields) {
// default to field name
String columnName = f.getName();
@@ -277,6 +278,21 @@ public class TableDefinition<T> { boolean allowNull = true;
EnumType enumType = null;
String defaultValue = "";
+ // configure Java -> SQL enum mapping
+ if (f.getType().isEnum()) {
+ enumType = EnumType.DEFAULT_TYPE;
+ if (f.getType().isAnnotationPresent(IQEnum.class)) {
+ // enum definition is annotated for all instances
+ IQEnum iqenum = f.getType().getAnnotation(IQEnum.class);
+ enumType = iqenum.value();
+ }
+ if (f.isAnnotationPresent(IQEnum.class)) {
+ // this instance of the enum is annotated
+ IQEnum iqenum = f.getAnnotation(IQEnum.class);
+ enumType = iqenum.value();
+ }
+ }
+
boolean hasAnnotation = f.isAnnotationPresent(IQColumn.class);
if (hasAnnotation) {
IQColumn col = f.getAnnotation(IQColumn.class);
@@ -288,21 +304,29 @@ public class TableDefinition<T> { maxLength = col.length();
trimString = col.trim();
allowNull = col.allowNull();
- defaultValue = col.defaultValue();
- }
- // configure Java -> SQL enum mapping
- if (f.getType().isEnum()) {
- enumType = EnumType.DEFAULT_TYPE;
- if (f.getType().isAnnotationPresent(IQEnum.class)) {
- // enum definition is annotated for all instances
- IQEnum iqenum = f.getType().getAnnotation(IQEnum.class);
- enumType = iqenum.value();
+ // try using default object
+ try {
+ f.setAccessible(true);
+ Object value = f.get(defaultObject);
+ if (value != null) {
+ if (value.getClass().isEnum()) {
+ // enum default, convert to target type
+ Enum<?> anEnum = (Enum<?>) value;
+ Object o = Utils.convertEnum(anEnum, enumType);
+ defaultValue = ModelUtils.formatDefaultValue(o);
+ } else {
+ // object default
+ defaultValue = ModelUtils.formatDefaultValue(value);
+ }
+ }
+ } catch (IllegalAccessException e) {
+ throw new IciqlException(e, "Failed to get default object for {0}", columnName);
}
- if (f.isAnnotationPresent(IQEnum.class)) {
- // this instance of the enum is annotated
- IQEnum iqenum = f.getAnnotation(IQEnum.class);
- enumType = iqenum.value();
+
+ // annotation overrides
+ if (!StringUtils.isNullOrEmpty(col.defaultValue())) {
+ defaultValue = col.defaultValue();
}
}
@@ -340,6 +364,9 @@ public class TableDefinition<T> { */
private Object getValue(Object obj, FieldDefinition field) {
Object value = field.getValue(obj);
+ if (value == null) {
+ return value;
+ }
if (field.enumType != null) {
// convert enumeration to INT or STRING
Enum<?> iqenum = (Enum<?>) value;
@@ -352,13 +379,13 @@ public class TableDefinition<T> { }
return iqenum.name();
case ORDINAL:
- return iqenum.ordinal();
+ return iqenum.ordinal();
case ENUMID:
if (!EnumId.class.isAssignableFrom(value.getClass())) {
throw new IciqlException(field.field.getName() + " does not implement EnumId!");
}
EnumId enumid = (EnumId) value;
- return enumid.enumId();
+ return enumid.enumId();
}
}
if (field.trimString && field.maxLength > 0) {
@@ -580,30 +607,6 @@ public class TableDefinition<T> { return this;
}
- /**
- * Retrieve list of columns from primary key definition.
- *
- * @param index
- * the primary key columns, separated by space
- * @return the column list
- */
- private List<String> getColumns(String index) {
- List<String> cols = Utils.newArrayList();
- if (index == null || index.length() == 0) {
- return null;
- }
- String[] cs = index.split("(,|\\s)");
- for (String c : cs) {
- if (c != null && c.trim().length() > 0) {
- cols.add(c.trim());
- }
- }
- if (cols.size() == 0) {
- return null;
- }
- return cols;
- }
-
void mapObject(Object obj) {
fieldMap.clear();
initObject(obj, fieldMap);
@@ -636,8 +639,9 @@ public class TableDefinition<T> { }
// setup the primary index, if properly annotated
- List<String> primaryKey = getColumns(tableAnnotation.primaryKey());
- if (primaryKey != null) {
+ if (tableAnnotation.primaryKey().length > 0) {
+ List<String> primaryKey = Utils.newArrayList();
+ primaryKey.addAll(Arrays.asList(tableAnnotation.primaryKey()));
setPrimaryKey(primaryKey);
}
}
diff --git a/src/com/iciql/TableInspector.java b/src/com/iciql/TableInspector.java index 080e20e..6ec848d 100644 --- a/src/com/iciql/TableInspector.java +++ b/src/com/iciql/TableInspector.java @@ -226,11 +226,15 @@ public class TableInspector { ap.addParameter("name", table); if (primaryKeys.size() > 1) { - StringBuilder pk = new StringBuilder(); + StatementBuilder pk = new StatementBuilder(); + pk.append("{ "); for (String key : primaryKeys) { - pk.append(key).append(' '); + pk.appendExceptFirst(", "); + pk.append("\""); + pk.append(key); + pk.append("\""); } - pk.trimToSize(); + pk.append(" }"); ap.addParameter("primaryKey", pk.toString()); } @@ -328,8 +332,8 @@ public class TableInspector { // Imports // don't import primitives, java.lang classes, or byte [] if (clazz.getPackage() == null) { - } else if (clazz.getPackage().getName().equals("java.lang")) { - } else if (clazz.equals(byte[].class)) { + } else if (clazz.getPackage().getName().equals("java.lang")) { + } else if (clazz.equals(byte[].class)) { } else { imports.add(clazz.getCanonicalName()); } @@ -508,13 +512,12 @@ public class TableInspector { remarks.add(warn( table, col, - format("{0}.maxLength={1}, ColumnMaxLength={2}", IQColumn.class.getSimpleName(), + format("{0}.length={1}, ColumnMaxLength={2}", IQColumn.class.getSimpleName(), fieldDef.maxLength, col.size))); } if (fieldDef.maxLength > 0 && !fieldDef.trimString) { - remarks.add(consider(table, col, - format("{0}.truncateToMaxLength=true" + " will prevent IciqlExceptions on" - + " INSERT or UPDATE, but will clip data!", IQColumn.class.getSimpleName()))); + remarks.add(consider(table, col, format("{0}.trim=true will prevent IciqlExceptions on" + + " INSERT or UPDATE, but will clip data!", IQColumn.class.getSimpleName()))); } } @@ -523,55 +526,58 @@ public class TableInspector { remarks.add(warn( table, col, - format("{0}.isAutoIncrement={1}" + " while Column autoIncrement={2}", + format("{0}.autoIncrement={1}" + " while Column autoIncrement={2}", IQColumn.class.getSimpleName(), fieldDef.isAutoIncrement, col.isAutoIncrement))); } // default value if (!col.isAutoIncrement && !col.isPrimaryKey) { + String defaultValue = null; + if (fieldDef.defaultValue != null && fieldDef.defaultValue instanceof String) { + defaultValue = fieldDef.defaultValue.toString(); + } // check Model.defaultValue format - if (!ModelUtils.isProperlyFormattedDefaultValue(fieldDef.defaultValue)) { + if (!ModelUtils.isProperlyFormattedDefaultValue(defaultValue)) { remarks.add(error( table, col, format("{0}.defaultValue=\"{1}\"" + " is improperly formatted!", - IQColumn.class.getSimpleName(), fieldDef.defaultValue)) - .throwError(throwError)); + IQColumn.class.getSimpleName(), defaultValue)).throwError(throwError)); // next field return; } // compare Model.defaultValue to Column.defaultValue - if (isNullOrEmpty(fieldDef.defaultValue) && !isNullOrEmpty(col.defaultValue)) { + if (isNullOrEmpty(defaultValue) && !isNullOrEmpty(col.defaultValue)) { // Model.defaultValue is NULL, Column.defaultValue is NOT NULL remarks.add(warn( table, col, format("{0}.defaultValue=\"\"" + " while column default=\"{1}\"", IQColumn.class.getSimpleName(), col.defaultValue))); - } else if (!isNullOrEmpty(fieldDef.defaultValue) && isNullOrEmpty(col.defaultValue)) { + } else if (!isNullOrEmpty(defaultValue) && isNullOrEmpty(col.defaultValue)) { // Column.defaultValue is NULL, Model.defaultValue is NOT NULL remarks.add(warn( table, col, format("{0}.defaultValue=\"{1}\"" + " while column default=\"\"", - IQColumn.class.getSimpleName(), fieldDef.defaultValue))); - } else if (!isNullOrEmpty(fieldDef.defaultValue) && !isNullOrEmpty(col.defaultValue)) { - if (!fieldDef.defaultValue.equals(col.defaultValue)) { + IQColumn.class.getSimpleName(), defaultValue))); + } else if (!isNullOrEmpty(defaultValue) && !isNullOrEmpty(col.defaultValue)) { + if (!defaultValue.equals(col.defaultValue)) { // Model.defaultValue != Column.defaultValue remarks.add(warn( table, col, format("{0}.defaultValue=\"{1}\"" + " while column default=\"{2}\"", - IQColumn.class.getSimpleName(), fieldDef.defaultValue, col.defaultValue))); + IQColumn.class.getSimpleName(), defaultValue, col.defaultValue))); } } // sanity check Model.defaultValue literal value - if (!ModelUtils.isValidDefaultValue(fieldDef.field.getType(), fieldDef.defaultValue)) { + if (!ModelUtils.isValidDefaultValue(fieldDef.field.getType(), defaultValue)) { remarks.add(error( table, col, format("{0}.defaultValue=\"{1}\" is invalid!", IQColumn.class.getSimpleName(), - fieldDef.defaultValue))); + defaultValue))); } } } diff --git a/src/com/iciql/util/Utils.java b/src/com/iciql/util/Utils.java index 3237742..fa794cf 100644 --- a/src/com/iciql/util/Utils.java +++ b/src/com/iciql/util/Utils.java @@ -116,7 +116,7 @@ public class Utils { }
}
}
- throw new IciqlException("Exception trying to create " + clazz.getName() + ": " + e, e);
+ throw new IciqlException("Missing default constructor? Exception trying to create " + clazz.getName() + ": " + e, e);
}
}
};
|