From 6830a631590ae63b6f80476c0a86b6050a91f953 Mon Sep 17 00:00:00 2001 From: James Moger Date: Wed, 21 Dec 2011 15:20:42 -0500 Subject: [PATCH] Attempt defaultValue instantiation if object is null && spec'd NOT NULL This is only attempted on db.insert and db.update. --- docs/05_releases.mkd | 4 ++ src/com/iciql/Constants.java | 4 +- src/com/iciql/ModelUtils.java | 109 ++++++++++++++++++++++++++++- src/com/iciql/TableDefinition.java | 23 +++++- 4 files changed, 136 insertions(+), 4 deletions(-) diff --git a/docs/05_releases.mkd b/docs/05_releases.mkd index ebc1fae..33a3e6a 100644 --- a/docs/05_releases.mkd +++ b/docs/05_releases.mkd @@ -6,6 +6,10 @@ **%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%* +- Iciql now tries to instantiate a default value from an annotated default value IFF the field object is null, it is specified *nullable = false*, and a defaultValue exists. This only applies to *db.insert* or *db.update*. + +**0.7.5**   *released 2011-12-12* + - Iciql now identifies wildcard queries and builds a dynamic column lookup. Otherwise, the original field-position-based approach is used. This corrects the performance regression released in 0.7.4 while still fixing the wildcard statement column mapping problem. **0.7.4**   *released 2011-12-06* diff --git a/src/com/iciql/Constants.java b/src/com/iciql/Constants.java index 4034b3b..b0c26d8 100644 --- a/src/com/iciql/Constants.java +++ b/src/com/iciql/Constants.java @@ -25,11 +25,11 @@ 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.7.5"; + public static final String VERSION = "0.7.6"; // 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-12-12"; + public static final String VERSION_DATE = "2011-12-21"; // The build script extracts this exact line so be careful editing it // and only use A-Z a-z 0-9 .-_ in the string. diff --git a/src/com/iciql/ModelUtils.java b/src/com/iciql/ModelUtils.java index 678e7af..72e9690 100644 --- a/src/com/iciql/ModelUtils.java +++ b/src/com/iciql/ModelUtils.java @@ -21,6 +21,7 @@ import static com.iciql.util.StringUtils.isNullOrEmpty; import java.lang.reflect.Method; import java.math.BigDecimal; +import java.text.DateFormat; import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.Arrays; @@ -223,7 +224,7 @@ class ModelUtils { // leading or trailing _ continue; } - String [] subchunks = StringUtils.arraySplit(chunk, ' ', false); + String[] subchunks = StringUtils.arraySplit(chunk, ' ', false); for (String subchunk : subchunks) { if (subchunk.length() == 0) { // leading or trailing space @@ -251,6 +252,112 @@ class ModelUtils { return lower; } + /** + * Converts a DEFAULT clause value into an object. + * + * @param field + * definition + * @return object + */ + static Object getDefaultValue(FieldDefinition def, Class dateTimeClass) { + Class valueType = getClassForSqlType(def.dataType, dateTimeClass); + if (String.class.isAssignableFrom(valueType)) { + if (StringUtils.isNullOrEmpty(def.defaultValue)) { + // literal default must be specified within single quotes + return null; + } + if (def.defaultValue.charAt(0) == '\'' + && def.defaultValue.charAt(def.defaultValue.length() - 1) == '\'') { + // strip leading and trailing single quotes + return def.defaultValue.substring(1, def.defaultValue.length() - 2); + } + return def.defaultValue; + } + + if (StringUtils.isNullOrEmpty(def.defaultValue)) { + // can not create object from empty string + return null; + } + + // strip leading and trailing single quotes + String content = def.defaultValue; + if (content.charAt(0) == '\'') { + content = content.substring(1); + } + if (content.charAt(content.length() - 1) == '\'') { + content = content.substring(0, content.length() - 2); + } + + if (StringUtils.isNullOrEmpty(content)) { + // can not create object from empty string + return null; + } + + if (Boolean.class.isAssignableFrom(valueType) || boolean.class.isAssignableFrom(valueType)) { + return Boolean.parseBoolean(content); + } + + if (Number.class.isAssignableFrom(valueType)) { + try { + // delegate to static valueOf() method to parse string + Method m = valueType.getMethod("valueOf", String.class); + return m.invoke(null, content); + } catch (NumberFormatException e) { + throw new IciqlException(e, "Failed to parse {0} as a number!", def.defaultValue); + } catch (Throwable t) { + } + } + + String dateRegex = "[0-9]{1,4}[-/\\.][0-9]{1,2}[-/\\.][0-9]{1,2}"; + String timeRegex = "[0-2]{1}[0-9]{1}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}"; + + if (java.sql.Date.class.isAssignableFrom(valueType)) { + // this may be a little loose.... + // 00-00-00 + // 00/00/00 + // 00.00.00 + Pattern pattern = Pattern.compile(dateRegex); + if (pattern.matcher(content).matches()) { + DateFormat df = DateFormat.getDateInstance(); + try { + return df.parse(content); + } catch (Exception e) { + throw new IciqlException(e, "Failed to parse {0} as a date!", def.defaultValue); + } + } + } + + if (java.sql.Time.class.isAssignableFrom(valueType)) { + // 00:00:00 + Pattern pattern = Pattern.compile(timeRegex); + if (pattern.matcher(content).matches()) { + DateFormat df = DateFormat.getTimeInstance(); + try { + return df.parse(content); + } catch (Exception e) { + throw new IciqlException(e, "Failed to parse {0} as a time!", def.defaultValue); + } + } + } + + if (java.util.Date.class.isAssignableFrom(valueType)) { + // this may be a little loose.... + // 00-00-00 00:00:00 + // 00/00/00T00:00:00 + // 00.00.00T00:00:00 + Pattern pattern = Pattern.compile(dateRegex + "." + timeRegex); + if (pattern.matcher(content).matches()) { + DateFormat df = DateFormat.getDateTimeInstance(); + try { + return df.parse(content); + } catch (Exception e) { + throw new IciqlException(e, "Failed to parse {0} as a datetimestamp!", def.defaultValue); + } + } + } + return content; + } + /** * Converts the object into a DEFAULT clause value. * diff --git a/src/com/iciql/TableDefinition.java b/src/com/iciql/TableDefinition.java index f8238e5..f7bbc13 100644 --- a/src/com/iciql/TableDefinition.java +++ b/src/com/iciql/TableDefinition.java @@ -486,6 +486,10 @@ public class TableDefinition { buff.appendExceptFirst(", "); buff.append('?'); Object value = getValue(obj, field); + if (value == null && !field.nullable) { + // try to interpret and instantiate a default value + value = ModelUtils.getDefaultValue(field, db.getDialect().getDateTimeClass()); + } stat.addParameter(value); } buff.append(')'); @@ -509,6 +513,19 @@ public class TableDefinition { // skip null object autoincrement values return true; } + } else { + // conditionally skip insert of null + Object value = getValue(obj, field); + if (value == null) { + if (field.nullable) { + // skip null assignment, field is nullable + return true; + } else if (StringUtils.isNullOrEmpty(field.defaultValue)) { + IciqlLogger.warn("no default value, skipping null insert assignment for {0}.{1}", + tableName, field.columnName); + return true; + } + } } return false; } @@ -536,10 +553,14 @@ public class TableDefinition { for (FieldDefinition field : fields) { if (!field.isPrimaryKey) { + Object value = getValue(obj, field); + if (value == null && !field.nullable) { + // try to interpret and instantiate a default value + value = ModelUtils.getDefaultValue(field, db.getDialect().getDateTimeClass()); + } buff.appendExceptFirst(", "); buff.append(db.getDialect().prepareColumnName(field.columnName)); buff.append(" = ?"); - Object value = getValue(obj, field); stat.addParameter(value); } } -- 2.39.5