Also fixed an error with allowing null objects. Noted rapid churn of iciql in documentation. Changed build constants for next release.tags/v0.6.4
@@ -65,21 +65,6 @@ | |||
</filterchain> | |||
</loadfile> | |||
<!-- extract iciql previous api version from source code --> | |||
<loadfile property="iq.apiPrevious" srcfile="${basedir}/src/com/iciql/Constants.java"> | |||
<filterchain> | |||
<linecontains> | |||
<contains value="public static final String API_PREVIOUS = " /> | |||
</linecontains> | |||
<striplinebreaks /> | |||
<tokenfilter> | |||
<replacestring from="public static final String API_PREVIOUS = "" to="" /> | |||
<replacestring from="";" to="" /> | |||
<trim /> | |||
</tokenfilter> | |||
</filterchain> | |||
</loadfile> | |||
<property name="library.jar" value="${basedir}/iciql-${iq.version}.jar" /> | |||
<property name="javadoc.jar" value="${basedir}/iciql-${iq.version}-javadoc.jar" /> | |||
<property name="sources.jar" value="${basedir}/iciql-${iq.version}-sources.jar" /> | |||
@@ -140,7 +125,8 @@ | |||
<!-- versioning --> | |||
<param name="-since"/> <param name="api/v1.xml"/> <param name="v1" /> | |||
<param name="-since"/> <param name="api/v${iq.apiPrevious}.xml"/> <param name="v${iq.apiPrevious}" /> | |||
<param name="-since"/> <param name="api/v2.xml"/> <param name="v2" /> | |||
<param name="-since"/> <param name="api/v3.xml"/> <param name="v3" /> | |||
<param name="-apiversion" value="v${iq.apiCurrent}"/> | |||
<param name="-apixml" value="api/v${iq.apiCurrent}.xml"/> |
@@ -46,6 +46,8 @@ iciql requires a Java 6 Runtime Environment (JRE) or a Java 6 Development Kit (J | |||
### Current Release | |||
<span class="warning">iciql is undergoing rapid development so api and configuration are subject to change from release to release</span> | |||
**%VERSION%** ([zip](http://code.google.com/p/iciql/downloads/detail?name=%ZIP%)|[jar](http://code.google.com/p/iciql/downloads/detail?name=%JAR%)) *released %BUILDDATE%* | |||
issues, binaries, & source @ [Google Code][googlecode]<br/> |
@@ -143,6 +143,7 @@ The recommended approach to setup a model class is to annotate the class and fie | |||
### advantages | |||
- annotated fields may have any scope | |||
- annotated fields may specify default values | |||
- annotated models support annotated field inheritance making it possible to design a single base class that defines the fields and then create table subclasses that specify the table mappings. | |||
- model runtime dependency is limited to the small, portable `com.iciql.Iciql` class file which contains the annotation definitions | |||
@@ -151,6 +152,24 @@ The recommended approach to setup a model class is to annotate the class and fie | |||
- more verbose model classes | |||
- indexes are defined using "fragile" string column names | |||
- compound primary keys are defined using "fragile" string column names | |||
### default values | |||
You may specify default values for an @IQColumn by either: | |||
1. specifying the default value string within your annotation<br/> | |||
%BEGINCODE% | |||
// notice the single ticks! | |||
@IQColumn(defaultValue="'2000-01-01 00:00:00'") | |||
Date myDate; | |||
%ENDCODE% | |||
2. setting a default object on the field<br/> | |||
%BEGINCODE% | |||
@IQColumn | |||
Date myDate = new Date(100, 0, 1); | |||
%ENDCODE% | |||
If you want to specify a database-specific variable or function as your default value (e.g. CURRENT_TIMESTAMP) you must do that within the annotation. Also note that the IQColumn.defaultValue must be a well-formatted SQL DEFAULT expression whereas object defaults will be automatically converted to an SQL DEFAULT expression. | |||
### Example Annotated Model | |||
%BEGINCODE% |
@@ -2,8 +2,26 @@ | |||
### Current Release | |||
<span class="warning">iciql is undergoing rapid development so api and configuration are subject to change from release to release</span> | |||
**%VERSION%** ([zip](http://code.google.com/p/iciql/downloads/detail?name=%ZIP%)|[jar](http://code.google.com/p/iciql/downloads/detail?name=%JAR%)) *released %BUILDDATE%* | |||
- api change release (API v4) | |||
- allow using objects to assign default values<br/> | |||
%BEGINCODE% | |||
// CREATE TABLE ... myDate DATETIME DEFAULT '2000-02-01 00:00:00' | |||
@IQColumn | |||
Date myDate = new Date(100, 1, 1); | |||
%ENDCODE% | |||
- changed @IQTable.primaryKey definition to use array of column names<br/> | |||
%BEGINCODE% | |||
@IQTable( primaryKey = {"name", "nickname"}) | |||
%ENDCODE% | |||
### Older Releases | |||
**0.6.3** *released 2011-08-08* | |||
- api change release (API v3) | |||
- finished enum support (issue 4) | |||
- added UUID type support (H2 databases only) | |||
@@ -11,8 +29,6 @@ | |||
- added *between(A y).and(A z)* condition syntax | |||
- moved dialects into separate package | |||
### Older Releases | |||
**0.6.2** *released 2011-08-05* | |||
- api change release (API v2) |
@@ -77,6 +77,11 @@ a:hover, a:visited, a:active { | |||
color: #880000; | |||
} | |||
span.warning { | |||
font-weight: bold; | |||
color: #f00000; | |||
} | |||
img.logo { | |||
margin-top:-8px; | |||
float: right; |
@@ -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"; | |||
} |
@@ -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) { |
@@ -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) |
@@ -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; | |||
} | |||
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} | |||
@@ -252,6 +254,41 @@ class ModelUtils { | |||
return lower; | |||
} | |||
/** | |||
* 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(). | |||
* |
@@ -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); | |||
} | |||
} |
@@ -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))); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} | |||
}; |
@@ -0,0 +1,61 @@ | |||
/* | |||
* Copyright 2011 James Moger. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.iciql.test; | |||
import static org.junit.Assert.assertTrue; | |||
import java.util.List; | |||
import org.junit.Test; | |||
import com.iciql.Db; | |||
import com.iciql.DbInspector; | |||
import com.iciql.ValidationRemark; | |||
import com.iciql.test.models.DefaultValuesModel; | |||
/** | |||
* Tests default object values. | |||
*/ | |||
public class DefaultValuesTest { | |||
@Test | |||
public void testDefaultObjectValues() { | |||
Db db = Db.open("jdbc:h2:mem:", "sa", "sa"); | |||
// insert random model | |||
DefaultValuesModel model = new DefaultValuesModel(); | |||
db.insert(model); | |||
DefaultValuesModel v = new DefaultValuesModel(); | |||
// retrieve model and compare | |||
DefaultValuesModel retrievedModel = db.from(v).selectFirst(); | |||
assertTrue(model.myInteger.equals(retrievedModel.myInteger)); | |||
assertTrue(model.myDate.equals(retrievedModel.myDate)); | |||
assertTrue(model.myEnumIdTree.equals(retrievedModel.myEnumIdTree)); | |||
assertTrue(model.myNameTree.equals(retrievedModel.myNameTree)); | |||
assertTrue(model.myOrdinalTree.equals(retrievedModel.myOrdinalTree)); | |||
assertTrue(retrievedModel.myNullTree == null); | |||
DbInspector inspector = new DbInspector(db); | |||
List<ValidationRemark> remarks = inspector.validateModel(model, false); | |||
db.close(); | |||
for (ValidationRemark remark : remarks) { | |||
System.out.println(remark.toString()); | |||
} | |||
} | |||
} |
@@ -0,0 +1,58 @@ | |||
/* | |||
* Copyright 2011 James Moger. | |||
* | |||
* Licensed under the Apache License, Version 2.0 (the "License"); | |||
* you may not use this file except in compliance with the License. | |||
* You may obtain a copy of the License at | |||
* | |||
* http://www.apache.org/licenses/LICENSE-2.0 | |||
* | |||
* Unless required by applicable law or agreed to in writing, software | |||
* distributed under the License is distributed on an "AS IS" BASIS, | |||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||
* See the License for the specific language governing permissions and | |||
* limitations under the License. | |||
*/ | |||
package com.iciql.test.models; | |||
import java.util.Date; | |||
import com.iciql.Iciql.EnumType; | |||
import com.iciql.Iciql.IQColumn; | |||
import com.iciql.Iciql.IQEnum; | |||
import com.iciql.Iciql.IQTable; | |||
import com.iciql.test.models.EnumModels.Tree; | |||
/** | |||
* Default values model. | |||
*/ | |||
@IQTable(name = "DefaultValuesTest") | |||
public class DefaultValuesModel { | |||
@IQColumn(primaryKey = true, autoIncrement = true) | |||
public Long myLong; | |||
@SuppressWarnings("deprecation") | |||
@IQColumn | |||
public Date myDate = new Date(100, 7, 1); | |||
@IQColumn | |||
public Integer myInteger = 12345; | |||
@IQColumn | |||
public Tree myEnumIdTree = Tree.WALNUT; | |||
@IQColumn | |||
@IQEnum(EnumType.NAME) | |||
public Tree myNameTree = Tree.MAPLE; | |||
@IQColumn | |||
@IQEnum(EnumType.ORDINAL) | |||
public Tree myOrdinalTree = Tree.PINE; | |||
@IQColumn(allowNull = true) | |||
public Tree myNullTree; | |||
public DefaultValuesModel() { | |||
} | |||
} |