@@ -38,7 +38,6 @@ import java.util.regex.Matcher; | |||
import java.util.regex.Pattern; | |||
import com.iciql.Iciql.DataTypeAdapter; | |||
import com.iciql.Iciql.TypeAdapter; | |||
import com.iciql.util.JdbcUtils; | |||
import com.iciql.util.StringUtils; | |||
import com.iciql.util.Utils; | |||
@@ -175,18 +174,9 @@ final class DaoProxy<X extends Dao> implements InvocationHandler, Dao { | |||
|| java.util.Date.class.isAssignableFrom(returnType) | |||
|| byte[].class.isAssignableFrom(returnType); | |||
// determine the return type adapter, if any | |||
DataTypeAdapter<?> adapter = null; | |||
for (Annotation annotation : method.getAnnotations()) { | |||
if (annotation.annotationType() == TypeAdapter.class) { | |||
TypeAdapter typeAdapter = (TypeAdapter) annotation; | |||
adapter = db.getDialect().getAdapter(typeAdapter.value()); | |||
break; | |||
} else if (annotation.annotationType().isAnnotationPresent(TypeAdapter.class)) { | |||
TypeAdapter typeAdapter = annotation.annotationType().getAnnotation(TypeAdapter.class); | |||
adapter = db.getDialect().getAdapter(typeAdapter.value()); | |||
break; | |||
} | |||
Class<? extends DataTypeAdapter<?>> adapter = Utils.getDataTypeAdapter(method.getAnnotations()); | |||
if (adapter == null) { | |||
adapter = Utils.getDataTypeAdapter(returnType.getAnnotations()); | |||
} | |||
/* | |||
@@ -209,17 +199,7 @@ final class DaoProxy<X extends Dao> implements InvocationHandler, Dao { | |||
while (rs.next()) { | |||
Object o = rs.getObject(1); | |||
Object value; | |||
if (adapter == null) { | |||
// use internal Iciql type conversion | |||
value = Utils.convert(o, returnType); | |||
} else { | |||
// use the type adapter to convert the JDBC object to a domain type | |||
value = adapter.deserialize(o); | |||
} | |||
Object value = db.getDialect().deserialize(rs, 1, returnType, adapter); | |||
objects.add(value); | |||
if (!isArray) { | |||
@@ -704,7 +684,7 @@ final class DaoProxy<X extends Dao> implements InvocationHandler, Dao { | |||
} | |||
// prepare the parameter | |||
parameters[i] = prepareParameter(db, value, typeAdapter); | |||
parameters[i] = db.getDialect().serialize(value, typeAdapter); | |||
} | |||
@@ -712,32 +692,6 @@ final class DaoProxy<X extends Dao> implements InvocationHandler, Dao { | |||
} | |||
/** | |||
* Prepares a method argument to an sql parameter for transmission to a | |||
* database. | |||
* | |||
* @param db | |||
* @param arg | |||
* @param typeAdapter | |||
* @return a prepared parameter | |||
*/ | |||
private Object prepareParameter(Db db, Object arg, Class<? extends DataTypeAdapter<?>> typeAdapter) { | |||
if (typeAdapter != null) { | |||
// use a type adapter to serialize the method argument | |||
Object o = db.getDialect().serialize(arg, typeAdapter); | |||
return o; | |||
} else { | |||
// use the method argument | |||
return arg; | |||
} | |||
} | |||
@Override | |||
public String toString() { | |||
return sql; |
@@ -18,8 +18,6 @@ | |||
package com.iciql; | |||
import java.lang.reflect.Field; | |||
import java.sql.Blob; | |||
import java.sql.Clob; | |||
import java.sql.ResultSet; | |||
import java.sql.SQLException; | |||
import java.util.ArrayList; | |||
@@ -28,6 +26,7 @@ import java.util.HashMap; | |||
import java.util.IdentityHashMap; | |||
import java.util.List; | |||
import com.iciql.Iciql.DataTypeAdapter; | |||
import com.iciql.Iciql.EnumType; | |||
import com.iciql.NestedConditions.And; | |||
import com.iciql.NestedConditions.Or; | |||
@@ -115,7 +114,6 @@ public class Query<T> { | |||
return select(true); | |||
} | |||
@SuppressWarnings("unchecked") | |||
public <X, Z> X selectFirst(Z x) { | |||
List<X> list = limit(1).select(x); | |||
return list.isEmpty() ? null : list.get(0); | |||
@@ -424,18 +422,10 @@ public class Query<T> { | |||
appendFromWhere(stat); | |||
ResultSet rs = stat.executeQuery(); | |||
List<X> result = Utils.newArrayList(); | |||
Class<? extends DataTypeAdapter<?>> typeAdapter = Utils.getDataTypeAdapter(x.getClass().getAnnotations()); | |||
try { | |||
while (rs.next()) { | |||
X value; | |||
Object o = rs.getObject(1); | |||
// Convert CLOB and BLOB now because we close the resultset | |||
if (Clob.class.isAssignableFrom(o.getClass())) { | |||
value = (X) Utils.convert(o, String.class); | |||
} else if (Blob.class.isAssignableFrom(o.getClass())) { | |||
value = (X) Utils.convert(o, byte[].class); | |||
} else { | |||
value = (X) o; | |||
} | |||
X value = (X) db.getDialect().deserialize(rs, 1, x.getClass(), typeAdapter); | |||
result.add(value); | |||
} | |||
} catch (Exception e) { | |||
@@ -850,7 +840,8 @@ public class Query<T> { | |||
stat.addParameter(y); | |||
} else if (col != null) { | |||
// object | |||
Object parameter = db.getDialect().serialize(value, col.getFieldDefinition().typeAdapter); | |||
Class<? extends DataTypeAdapter<?>> typeAdapter = col.getFieldDefinition().typeAdapter; | |||
Object parameter = db.getDialect().serialize(value, typeAdapter); | |||
stat.addParameter(parameter); | |||
} else { | |||
// primitive |
@@ -18,6 +18,8 @@ | |||
package com.iciql; | |||
import java.sql.ResultSet; | |||
import com.iciql.Iciql.DataTypeAdapter; | |||
import com.iciql.TableDefinition.IndexDefinition; | |||
@@ -54,11 +56,13 @@ public interface SQLDialect { | |||
/** | |||
* Deserialize the object received from the database into a Java type. | |||
* | |||
* @param value | |||
* @param rs | |||
* @param columnIndex | |||
* @param targetType | |||
* @param typeAdapter | |||
* @return the deserialized object | |||
*/ | |||
Object deserialize(Object value, Class<? extends DataTypeAdapter<?>> typeAdapter); | |||
Object deserialize(ResultSet rs, int columnIndex, Class<?> targetType, Class<? extends DataTypeAdapter<?>> typeAdapter); | |||
/** | |||
* Configure the dialect. |
@@ -18,8 +18,11 @@ | |||
package com.iciql; | |||
import java.sql.Blob; | |||
import java.sql.Clob; | |||
import java.sql.Connection; | |||
import java.sql.DatabaseMetaData; | |||
import java.sql.ResultSet; | |||
import java.sql.SQLException; | |||
import java.text.MessageFormat; | |||
import java.text.SimpleDateFormat; | |||
@@ -476,14 +479,40 @@ public class SQLDialectDefault implements SQLDialect { | |||
} | |||
@Override | |||
public Object deserialize(Object value, Class<? extends DataTypeAdapter<?>> typeAdapter) { | |||
public Object deserialize(ResultSet rs, int columnIndex, Class<?> targetType, Class<? extends DataTypeAdapter<?>> typeAdapter) { | |||
if (typeAdapter == null) { | |||
// pass-through | |||
// standard object deserialization | |||
Object value = null; | |||
try { | |||
// use internal Iciql type conversion | |||
Object o = rs.getObject(columnIndex); | |||
if (Clob.class.isAssignableFrom(o.getClass())) { | |||
value = Utils.convert(o, String.class); | |||
} else if (Blob.class.isAssignableFrom(o.getClass())) { | |||
value = Utils.convert(o, byte[].class); | |||
} else { | |||
value = Utils.convert(o, targetType); | |||
} | |||
} catch (SQLException e) { | |||
throw new IciqlException(e, "Can not convert the value at column {0} to {1}", | |||
columnIndex, targetType.getName()); | |||
} | |||
return value; | |||
} | |||
} else { | |||
// custom object deserialization with a DataTypeAdapter | |||
DataTypeAdapter<?> dta = getAdapter(typeAdapter); | |||
Object object = null; | |||
try { | |||
object = rs.getObject(columnIndex); | |||
} catch (SQLException e) { | |||
throw new IciqlException(e, "Can not convert the value at column {0} to {1}", | |||
columnIndex, targetType.getName()); | |||
} | |||
DataTypeAdapter<?> dta = getAdapter(typeAdapter); | |||
return dta.deserialize(value); | |||
Object value = dta.deserialize(object); | |||
return value; | |||
} | |||
} | |||
@Override |
@@ -42,7 +42,6 @@ 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.IQIgnore; | |||
import com.iciql.Iciql.IQIndex; | |||
import com.iciql.Iciql.IQIndexes; | |||
@@ -134,25 +133,17 @@ public class TableDefinition<T> { | |||
private Object initWithNewObject(Object obj) { | |||
Object o = Utils.newObject(field.getType()); | |||
setValue(null, obj, o); | |||
setValue(obj, o); | |||
return o; | |||
} | |||
private void setValue(SQLDialect dialect, Object obj, Object o) { | |||
private void setValue(Object obj, Object o) { | |||
try { | |||
if (!field.isAccessible()) { | |||
field.setAccessible(true); | |||
} | |||
Class<?> targetType = field.getType(); | |||
if (targetType.isEnum()) { | |||
o = Utils.convertEnum(o, targetType, enumType); | |||
} else if (dialect != null && typeAdapter != null) { | |||
o = dialect.deserialize(o, typeAdapter); | |||
} else { | |||
o = Utils.convert(o, targetType); | |||
} | |||
if (targetType.isPrimitive() && o == null) { | |||
if (field.getType().isPrimitive() && o == null) { | |||
// do not attempt to set a primitive to null | |||
return; | |||
} | |||
@@ -165,14 +156,6 @@ public class TableDefinition<T> { | |||
} | |||
} | |||
private Object read(ResultSet rs, int columnIndex) { | |||
try { | |||
return rs.getObject(columnIndex); | |||
} catch (SQLException e) { | |||
throw new IciqlException(e); | |||
} | |||
} | |||
@Override | |||
public int hashCode() { | |||
return columnName.hashCode(); | |||
@@ -457,32 +440,14 @@ public class TableDefinition<T> { | |||
int scale = 0; | |||
boolean trim = false; | |||
boolean nullable = !f.getType().isPrimitive(); | |||
EnumType enumType = null; | |||
Class<?> enumTypeClass = null; | |||
String defaultValue = ""; | |||
String constraint = ""; | |||
String dataType = null; | |||
Class<? extends DataTypeAdapter<?>> typeAdapter = null; | |||
// 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(); | |||
} | |||
if (EnumId.class.isAssignableFrom(f.getType())) { | |||
// custom enumid mapping | |||
enumTypeClass = ((EnumId<?>) f.getType().getEnumConstants()[0]).enumIdClass(); | |||
} | |||
} | |||
EnumType enumType = Utils.getEnumType(f); | |||
Class<?> enumTypeClass = Utils.getEnumTypeClass(f); | |||
// try using default object | |||
try { | |||
@@ -1201,9 +1166,20 @@ public class TableDefinition<T> { | |||
void readRow(SQLDialect dialect, Object item, ResultSet rs, int[] columns) { | |||
for (int i = 0; i < fields.size(); i++) { | |||
FieldDefinition def = fields.get(i); | |||
int index = columns[i]; | |||
Object o = def.read(rs, index); | |||
def.setValue(dialect, item, o); | |||
Class<?> targetType = def.field.getType(); | |||
Object o; | |||
if (targetType.isEnum()) { | |||
Object obj; | |||
try { | |||
obj = rs.getObject(columns[i]); | |||
} catch (SQLException e) { | |||
throw new IciqlException(e); | |||
} | |||
o = Utils.convertEnum(obj, targetType, def.enumType); | |||
} else { | |||
o = dialect.deserialize(rs, columns[i], targetType, def.typeAdapter); | |||
} | |||
def.setValue(item, o); | |||
} | |||
} | |||
@@ -24,6 +24,7 @@ import java.io.Reader; | |||
import java.io.StringWriter; | |||
import java.lang.annotation.Annotation; | |||
import java.lang.reflect.Constructor; | |||
import java.lang.reflect.Field; | |||
import java.math.BigDecimal; | |||
import java.math.BigInteger; | |||
import java.sql.Blob; | |||
@@ -44,6 +45,7 @@ import java.util.concurrent.atomic.AtomicLong; | |||
import com.iciql.Iciql.DataTypeAdapter; | |||
import com.iciql.Iciql.EnumId; | |||
import com.iciql.Iciql.EnumType; | |||
import com.iciql.Iciql.IQEnum; | |||
import com.iciql.Iciql.TypeAdapter; | |||
import com.iciql.IciqlException; | |||
@@ -352,6 +354,59 @@ public class Utils { | |||
throw new IciqlException("Can not convert the value {0} from {1} to {2}", o, currentType, targetType); | |||
} | |||
/** | |||
* Identify the EnumType for the field. | |||
* | |||
* @param f | |||
* @return null or the EnumType | |||
*/ | |||
public static EnumType getEnumType(Field f) { | |||
EnumType enumType = null; | |||
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(); | |||
} | |||
} | |||
return enumType; | |||
} | |||
/** | |||
* Identify the EnumType from the annotations. | |||
* | |||
* @param annotations | |||
* @return null or the EnumType | |||
*/ | |||
public static EnumType getEnumType(Annotation [] annotations) { | |||
EnumType enumType = null; | |||
if (annotations != null) { | |||
for (Annotation annotation : annotations) { | |||
if (annotation instanceof IQEnum) { | |||
enumType = ((IQEnum) annotation).value(); | |||
break; | |||
} | |||
} | |||
} | |||
return enumType; | |||
} | |||
public static Class<?> getEnumTypeClass(Field f) { | |||
if (f.getType().isEnum()) { | |||
if (EnumId.class.isAssignableFrom(f.getType())) { | |||
// custom enumid mapping | |||
return ((EnumId<?>) f.getType().getEnumConstants()[0]).enumIdClass(); | |||
} | |||
} | |||
return null; | |||
} | |||
public static Object convertEnum(Enum<?> o, EnumType type) { | |||
if (o == null) { | |||
return null; | |||
@@ -540,7 +595,7 @@ public class Utils { | |||
* Identify the data type adapter class in the annotations. | |||
* | |||
* @param annotations | |||
* @return null or the dtaa type adapter class | |||
* @return null or the data type adapter class | |||
*/ | |||
public static Class<? extends DataTypeAdapter<?>> getDataTypeAdapter(Annotation [] annotations) { | |||
Class<? extends DataTypeAdapter<?>> typeAdapter = null; |