package com.j256.ormlite.field;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Map;
import com.j256.ormlite.dao.BaseDaoImpl;
import com.j256.ormlite.dao.BaseForeignCollection;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.dao.EagerForeignCollection;
import com.j256.ormlite.dao.ForeignCollection;
import com.j256.ormlite.dao.LazyForeignCollection;
import com.j256.ormlite.db.DatabaseType;
import com.j256.ormlite.field.types.VoidType;
import com.j256.ormlite.misc.BaseDaoEnabled;
import com.j256.ormlite.misc.SqlExceptionUtil;
import com.j256.ormlite.stmt.mapped.MappedQueryForId;
import com.j256.ormlite.support.ConnectionSource;
import com.j256.ormlite.support.DatabaseConnection;
import com.j256.ormlite.support.DatabaseResults;
import com.j256.ormlite.table.DatabaseTableConfig;
import com.j256.ormlite.table.TableInfo;
/**
* Per field information configured from the {@link DatabaseField} annotation and the associated {@link Field} in the
* class. Use the {@link #createFieldType} static method to instantiate the class.
*
* @author graywatson
*/
public class FieldType {
/** default suffix added to fields that are id fields of foreign objects */
public static final String FOREIGN_ID_FIELD_SUFFIX = "_id";
// default values
private static boolean DEFAULT_VALUE_BOOLEAN;
private static byte DEFAULT_VALUE_BYTE;
private static char DEFAULT_VALUE_CHAR;
private static short DEFAULT_VALUE_SHORT;
private static int DEFAULT_VALUE_INT;
private static long DEFAULT_VALUE_LONG;
private static float DEFAULT_VALUE_FLOAT;
private static double DEFAULT_VALUE_DOUBLE;
private final ConnectionSource connectionSource;
private final Field field;
private final String dbColumnName;
private final DatabaseFieldConfig fieldConfig;
private final boolean isId;
private final boolean isGeneratedId;
private final String generatedIdSequence;
private final Method fieldGetMethod;
private final Method fieldSetMethod;
private DataPersister dataPersister;
private Object defaultValue;
private Object dataTypeConfigObj;
private FieldConverter fieldConverter;
private FieldType foreignIdField;
private Constructor<?> foreignConstructor;
private FieldType foreignFieldType;
private Dao<?, ?> foreignDao;
private MappedQueryForId<Object, Object> mappedQueryForId;
private static final ThreadLocal<LevelCounters> threadLevelCounters = new ThreadLocal<LevelCounters>();
/**
* You should use {@link FieldType#createFieldType} to instantiate one of these field if you have a {@link Field}.
*/
public FieldType(ConnectionSource connectionSource, String tableName, Field field, DatabaseFieldConfig fieldConfig,
Class<?> parentClass) throws SQLException {
this.connectionSource = connectionSource;
DatabaseType databaseType = connectionSource.getDatabaseType();
this.field = field;
Class<?> clazz = field.getType();
DataPersister dataPersister;
if (fieldConfig.getDataPersister() == null) {
Class<? extends DataPersister> persisterClass = fieldConfig.getPersisterClass();
if (persisterClass == null || persisterClass == VoidType.class) {
dataPersister = DataPersisterManager.lookupForField(field);
} else {
Method method;
try {
method = persisterClass.getDeclaredMethod("getSingleton");
} catch (Exception e) {
throw SqlExceptionUtil.create("Could not find getSingleton static method on class "
+ persisterClass, e);
}
Object result;
try {
result = (DataPersister) method.invoke(null);
} catch (InvocationTargetException e) {
throw SqlExceptionUtil.create("Could not run getSingleton method on class " + persisterClass,
e.getTargetException());
} catch (Exception e) {
throw SqlExceptionUtil.create("Could not run getSingleton method on class " + persisterClass, e);
}
if (result == null) {
throw new SQLException("Static getSingleton method should not return null on class "
+ persisterClass);
}
try {
dataPersister = (DataPersister) result;
} catch (Exception e) {
throw new SQLException(
"Could not cast result of static getSingleton method to DataPersister from class "
+ persisterClass);
}
}
} else {
dataPersister = fieldConfig.getDataPersister();
if (!dataPersister.isValidForField(field)) {
throw new IllegalArgumentException("Field class " + clazz + " for field " + this
+ " is not valid for data persister " + dataPersister);
}
}
String defaultFieldName = field.getName();
if (fieldConfig.isForeign() || fieldConfig.isForeignAutoRefresh()) {
if (dataPersister != null && dataPersister.isPrimitive()) {
throw new IllegalArgumentException("Field " + this + " is a primitive class " + clazz
+ " but marked as foreign");
}
defaultFieldName = defaultFieldName + FOREIGN_ID_FIELD_SUFFIX;
} else if (fieldConfig.isForeignCollection()) {
if (clazz != Collection.class && !ForeignCollection.class.isAssignableFrom(clazz)) {
throw new SQLException("Field class for '" + field.getName() + "' must be of class "
+ ForeignCollection.class.getSimpleName() + " or Collection.");
}
Type type = field.getGenericType();
if (!(type instanceof ParameterizedType)) {
throw new SQLException("Field class for '" + field.getName() + "' must be a parameterized Collection.");
}
Type[] genericArguments = ((ParameterizedType) type).getActualTypeArguments();
if (genericArguments.length == 0) {
// i doubt this will ever be reached
throw new SQLException("Field class for '" + field.getName()
+ "' must be a parameterized Collection with at least 1 type.");
}
} else if (dataPersister == null && (!fieldConfig.isForeignCollection())) {
if (byte[].class.isAssignableFrom(clazz)) {
throw new SQLException("ORMLite can't store unknown class " + clazz + " for field '" + field.getName()
+ "'. byte[] fields must specify dataType=DataType.BYTE_ARRAY or SERIALIZABLE");
} else if (Serializable.class.isAssignableFrom(clazz)) {
throw new SQLException("ORMLite can't store unknown class " + clazz + " for field '" + field.getName()
+ "'. Serializable fields must specify dataType=DataType.SERIALIZABLE");
} else {
throw new IllegalArgumentException("ORMLite does not know how to store field class " + clazz
+ " for field " + this);
}
}
if (fieldConfig.getColumnName() == null) {
this.dbColumnName = defaultFieldName;
} else {
this.dbColumnName = fieldConfig.getColumnName();
}
this.fieldConfig = fieldConfig;
if (fieldConfig.isId()) {
if (fieldConfig.isGeneratedId() || fieldConfig.getGeneratedIdSequence() != null) {
throw new IllegalArgumentException("Must specify one of id, generatedId, and generatedIdSequence with "
+ field.getName());
}
this.isId = true;
this.isGeneratedId = false;
this.generatedIdSequence = null;
} else if (fieldConfig.isGeneratedId()) {
if (fieldConfig.getGeneratedIdSequence() != null) {
throw new IllegalArgumentException("Must specify one of id, generatedId, and generatedIdSequence with "
+ field.getName());
}
this.isId = true;
this.isGeneratedId = true;
if (databaseType.isIdSequenceNeeded()) {
this.generatedIdSequence = databaseType.generateIdSequenceName(tableName, this);
} else {
this.generatedIdSequence = null;
}
} else if (fieldConfig.getGeneratedIdSequence() != null) {
this.isId = true;
this.isGeneratedId = true;
String seqName = fieldConfig.getGeneratedIdSequence();
if (databaseType.isEntityNamesMustBeUpCase()) {
seqName = seqName.toUpperCase();
}
this.generatedIdSequence = seqName;
} else {
this.isId = false;
this.isGeneratedId = false;
this.generatedIdSequence = null;
}
if (this.isId && (fieldConfig.isForeign() || fieldConfig.isForeignAutoRefresh())) {
throw new IllegalArgumentException("Id field " + field.getName() + " cannot also be a foreign object");
}
if (fieldConfig.isUseGetSet()) {
this.fieldGetMethod = DatabaseFieldConfig.findGetMethod(field, true);
this.fieldSetMethod = DatabaseFieldConfig.findSetMethod(field, true);
} else {
this.fieldGetMethod = null;
this.fieldSetMethod = null;
}
assignDataType(databaseType, dataPersister);
}
/**
* Because we go recursive in a lot of situations if we construct DAOs inside of the FieldType constructor, we have
* to do this 2nd pass initialization so we can better use the DAO caches.
*
* @see BaseDaoImpl#initialize()
*/
public void configDaoInformation(ConnectionSource connectionSource, Class<?> parentClass) throws SQLException {
Class<?> clazz = field.getType();
DatabaseType databaseType = connectionSource.getDatabaseType();
TableInfo<?, ?> foreignTableInfo;
final FieldType foreignIdField;
final Constructor<?> foreignConstructor;
final FieldType foreignFieldType;
final Dao<?, ?> foreignDao;
final MappedQueryForId<Object, Object> mappedQueryForId;
if (fieldConfig.isForeignAutoRefresh()) {
DatabaseTableConfig<?> tableConfig = fieldConfig.getForeignTableConfig();
if (tableConfig == null) {
// NOTE: the cast is necessary for maven
foreignDao = (BaseDaoImpl<?, ?>) DaoManager.createDao(connectionSource, clazz);
foreignTableInfo = ((BaseDaoImpl<?, ?>) foreignDao).getTableInfo();
} else {
tableConfig.extractFieldTypes(connectionSource);
// NOTE: the cast is necessary for maven
foreignDao = (BaseDaoImpl<?, ?>) DaoManager.createDao(connectionSource, tableConfig);
foreignTableInfo = ((BaseDaoImpl<?, ?>) foreignDao).getTableInfo();
}
foreignIdField = foreignTableInfo.getIdField();
if (foreignIdField == null) {
throw new IllegalArgumentException("Foreign field " + clazz + " does not have id field");
}
@SuppressWarnings("unchecked")
MappedQueryForId<Object, Object> castMappedQueryForId =
(MappedQueryForId<Object, Object>) MappedQueryForId.build(databaseType, foreignTableInfo);
mappedQueryForId = castMappedQueryForId;
foreignConstructor = foreignTableInfo.getConstructor();
foreignFieldType = null;
} else if (fieldConfig.isForeign()) {
/*
* If we are a foreign-field or if the foreign-auto-refresh was in too deep then we configure this as a
* foreign field. This is <= instead of < because we go one more level than the foreign auto-refresh.
*/
if (this.dataPersister != null && this.dataPersister.isPrimitive()) {
throw new IllegalArgumentException("Field " + this + " is a primitive class " + clazz
+ " but marked as foreign");
}
DatabaseTableConfig<?> tableConfig = fieldConfig.getForeignTableConfig();
if (tableConfig != null) {
tableConfig.extractFieldTypes(connectionSource);
// NOTE: the cast is necessary for maven
foreignDao = (BaseDaoImpl<?, ?>) DaoManager.createDao(connectionSource, tableConfig);
foreignTableInfo = ((BaseDaoImpl<?, ?>) foreignDao).getTableInfo();
foreignIdField = foreignTableInfo.getIdField();
foreignConstructor = foreignTableInfo.getConstructor();
} else if (BaseDaoEnabled.class.isAssignableFrom(clazz)) {
// NOTE: the cast is necessary for maven
foreignDao = (BaseDaoImpl<?, ?>) DaoManager.createDao(connectionSource, clazz);
foreignTableInfo = ((BaseDaoImpl<?, ?>) foreignDao).getTableInfo();
foreignIdField = foreignTableInfo.getIdField();
foreignConstructor = foreignTableInfo.getConstructor();
} else {
foreignDao = null;
foreignIdField =
DatabaseTableConfig.extractIdFieldType(connectionSource, clazz,
DatabaseTableConfig.extractTableName(clazz));
foreignConstructor = DatabaseTableConfig.findNoArgConstructor(clazz);
}
if (foreignIdField == null) {
throw new IllegalArgumentException("Foreign field " + clazz + " does not have id field");
}
foreignFieldType = null;
mappedQueryForId = null;
} else if (fieldConfig.isForeignCollection()) {
if (clazz != Collection.class && !ForeignCollection.class.isAssignableFrom(clazz)) {
throw new SQLException("Field class for '" + field.getName() + "' must be of class "
+ ForeignCollection.class.getSimpleName() + " or Collection.");
}
Type type = field.getGenericType();
if (!(type instanceof ParameterizedType)) {
throw new SQLException("Field class for '" + field.getName() + "' must be a parameterized Collection.");
}
Type[] genericArguments = ((ParameterizedType) type).getActualTypeArguments();
if (genericArguments.length == 0) {
// i doubt this will ever be reached
throw new SQLException("Field class for '" + field.getName()
+ "' must be a parameterized Collection with at least 1 type.");
}
clazz = (Class<?>) genericArguments[0];
DatabaseTableConfig<?> tableConfig = fieldConfig.getForeignTableConfig();
Dao<Object, Object> foundDao;
if (tableConfig == null) {
@SuppressWarnings("unchecked")
Dao<Object, Object> castDao = (Dao<Object, Object>) DaoManager.createDao(connectionSource, clazz);
foundDao = castDao;
} else {
@SuppressWarnings("unchecked")
Dao<Object, Object> castDao = (Dao<Object, Object>) DaoManager.createDao(connectionSource, tableConfig);
foundDao = castDao;
}
FieldType findForeignFieldType = null;
for (FieldType fieldType : ((BaseDaoImpl<?, ?>) foundDao).getTableInfo().getFieldTypes()) {
if (fieldType.getFieldType() == parentClass) {
findForeignFieldType = fieldType;
break;
}
}
if (findForeignFieldType == null) {
throw new SQLException("Foreign collection object " + clazz + " for field '" + field.getName()
+ "' does not contain a foreign field of class " + parentClass);
}
if (!findForeignFieldType.fieldConfig.isForeign()
&& !findForeignFieldType.fieldConfig.isForeignAutoRefresh()) {
// this may never be reached
throw new SQLException("Foreign collection object " + clazz + " for field '" + field.getName()
+ "' contains a field of class " + parentClass + " but it's not foreign");
}
foreignDao = foundDao;
foreignFieldType = findForeignFieldType;
foreignIdField = null;
foreignConstructor = null;
mappedQueryForId = null;
} else {
foreignConstructor = null;
foreignIdField = null;
foreignFieldType = null;
foreignDao = null;
mappedQueryForId = null;
}
this.mappedQueryForId = mappedQueryForId;
this.foreignConstructor = foreignConstructor;
this.foreignFieldType = foreignFieldType;
this.foreignDao = foreignDao;
this.foreignIdField = foreignIdField;
if (foreignIdField != null) {
assignDataType(databaseType, foreignIdField.getDataPersister());
}
}
public Field getField() {
return field;
}
public String getFieldName() {
return field.getName();
}
/**
* Return the class of the field associated with this field type.
*/
public Class<?> getFieldType() {
return field.getType();
}
public String getDbColumnName() {
return dbColumnName;
}
public DataPersister getDataPersister() {
return dataPersister;
}
public Object getDataTypeConfigObj() {
return dataTypeConfigObj;
}
public SqlType getSqlType() {
return fieldConverter.getSqlType();
}
public Object getDefaultValue() {
return defaultValue;
}
public int getWidth() {
return fieldConfig.getWidth();
}
public boolean isCanBeNull() {
return fieldConfig.isCanBeNull();
}
/**
* Return whether the field is an id field. It is an id if {@link DatabaseField#id},
* {@link DatabaseField#generatedId}, OR {@link DatabaseField#generatedIdSequence} are enabled.
*/
public boolean isId() {
return isId;
}
/**
* Return whether the field is a generated-id field. This is true if {@link DatabaseField#generatedId} OR
* {@link DatabaseField#generatedIdSequence} are enabled.
*/
public boolean isGeneratedId() {
return isGeneratedId;
}
/**
* Return whether the field is a generated-id-sequence field. This is true if
* {@link DatabaseField#generatedIdSequence} is specified OR if {@link DatabaseField#generatedId} is enabled and the
* {@link DatabaseType#isIdSequenceNeeded} is enabled. If the latter is true then the sequence name will be
* auto-generated.
*/
public boolean isGeneratedIdSequence() {
return generatedIdSequence != null;
}
/**
* Return the generated-id-sequence associated with the field or null if {@link #isGeneratedIdSequence} is false.
*/
public String getGeneratedIdSequence() {
return generatedIdSequence;
}
public boolean isForeign() {
return fieldConfig.isForeign();
}
/**
* Assign to the data object the val corresponding to the fieldType.
*/
public void assignField(Object data, Object val) throws SQLException {
// if this is a foreign object then val is the foreign object's id val
if (foreignIdField != null && val != null) {
// get the current field value which is the foreign-id
Object foreignId = extractJavaFieldValue(data);
/*
* See if we don't need to create a new foreign object. If we are refreshing and the id field has not
* changed then there is no need to create a new foreign object and maybe lose previously refreshed field
* information.
*/
if (foreignId != null && foreignId.equals(val)) {
return;
}
Object foreignObject;
/*
* If we don't have a mappedQueryForId or if we have recursed the proper number of times, just return a
* shell with the id set.
*/
LevelCounters levelCounters = getLevelCounters();
if (mappedQueryForId == null
|| levelCounters.autoRefreshlevel >= fieldConfig.getMaxForeignAutoRefreshLevel()) {
// create a shell and assign its id field
foreignObject = TableInfo.createObject(foreignConstructor, foreignDao);
foreignIdField.assignField(foreignObject, val);
} else {
levelCounters.autoRefreshlevel++;
try {
// do we need to auto-refresh the field?
DatabaseConnection databaseConnection = connectionSource.getReadOnlyConnection();
try {
foreignObject = mappedQueryForId.execute(databaseConnection, val);
} finally {
connectionSource.releaseConnection(databaseConnection);
}
} finally {
levelCounters.autoRefreshlevel--;
}
}
// the value we are to assign to our field is now the foreign object itself
val = foreignObject;
}
if (fieldSetMethod == null) {
boolean accessible = field.isAccessible();
if (!accessible) {
field.setAccessible(true);
}
try {
field.set(data, val);
} catch (IllegalArgumentException e) {
throw SqlExceptionUtil.create("Could not assign object '" + val + "' to field " + this, e);
} catch (IllegalAccessException e) {
throw SqlExceptionUtil.create("Could not assign object '" + val + "' to field " + this, e);
} finally {
if (!accessible) {
// restore the accessibility of the field
field.setAccessible(false);
}
}
} else {
try {
fieldSetMethod.invoke(data, val);
} catch (Exception e) {
throw SqlExceptionUtil.create("Could not call " + fieldSetMethod + " on object with '" + val + "' for "
+ this, e);
}
}
}
/**
* Assign an ID value to this field.
*/
public Object assignIdValue(Object data, Number val) throws SQLException {
Object idVal = dataPersister.convertIdNumber(val);
if (idVal == null) {
throw new SQLException("Invalid class " + dataPersister + " for sequence-id " + this);
} else {
assignField(data, idVal);
return idVal;
}
}
/**
* Return the value from the field in the object that is defined by this FieldType.
*/
public <FV> FV extractJavaFieldValue(Object object) throws SQLException {
Object val;
if (fieldGetMethod == null) {
boolean accessible = field.isAccessible();
try {
if (!accessible) {
field.setAccessible(true);
}
// field object may not be a T yet
val = field.get(object);
} catch (Exception e) {
throw SqlExceptionUtil.create("Could not get field value for " + this, e);
} finally {
if (!accessible) {
field.setAccessible(false);
}
}
} else {
try {
val = fieldGetMethod.invoke(object);
} catch (Exception e) {
throw SqlExceptionUtil.create("Could not call " + fieldGetMethod + " for " + this, e);
}
}
if (val == null) {
return null;
}
// if this is a foreign object then we want its id field
if (foreignIdField != null) {
val = foreignIdField.extractJavaFieldValue(val);
}
@SuppressWarnings("unchecked")
FV converted = (FV) val;
return converted;
}
/**
* Extract a field from an object and convert to something suitable to be passed to SQL as an argument.
*/
public Object extractJavaFieldToSqlArgValue(Object object) throws SQLException {
return convertJavaFieldToSqlArgValue(extractJavaFieldValue(object));
}
/**
* Convert a field value to something suitable to be stored in the database.
*/
public <FV> FV convertJavaFieldToSqlArgValue(Object fieldVal) throws SQLException {
if (fieldVal == null) {
return null;
} else {
fieldVal = fieldConverter.javaToSqlArg(this, fieldVal);
@SuppressWarnings("unchecked")
FV converted = (FV) fieldVal;
return converted;
}
}
/**
* Return the id field associated with the foreign object or null if none.
*/
public FieldType getForeignIdField() throws SQLException {
return foreignIdField;
}
/**
* Call through to {@link DataPersister#isEscapedValue()}
*/
public boolean isEscapedValue() {
return dataPersister.isEscapedValue();
}
public Enum<?> getUnknownEnumVal() {
return fieldConfig.getUnknownEnumvalue();
}
/**
* Return the format of the field.
*/
public String getFormat() {
return fieldConfig.getFormat();
}
public boolean isUnique() {
return fieldConfig.isUnique();
}
public boolean isUniqueCombo() {
return fieldConfig.isUniqueCombo();
}
public String getIndexName() {
return fieldConfig.getIndexName();
}
public String getUniqueIndexName() {
return fieldConfig.getUniqueIndexName();
}
/**
* Call through to {@link DataPersister#isEscapedDefaultValue()}
*/
public boolean isEscapedDefaultValue() {
return dataPersister.isEscapedDefaultValue();
}
/**
* Call through to {@link DataPersister#isComparable()}
*/
public boolean isComparable() {
return dataPersister.isComparable();
}
/**
* Call through to {@link DataPersister#isSelectArgRequired()}
*/
public boolean isSelectArgRequired() {
return dataPersister.isSelectArgRequired();
}
/**
* Call through to {@link DatabaseFieldConfig#isForeignCollection()}
*/
public boolean isForeignCollection() {
return fieldConfig.isForeignCollection();
}
/**
* Build and return a foreign collection based on the field settings that matches the id argument. This can return
* null in certain circumstances.
*
* @param id
* The id of the foreign object we will look for. This can be null if we are creating an empty
* collection.
* @param forceEager
* Set to true to force this to be an eager collection.
*/
public <FT, FID> BaseForeignCollection<FT, FID> buildForeignCollection(Object id, boolean forceEager)
throws SQLException {
// this can happen if we have a foreign-auto-refresh scenario
if (foreignFieldType == null) {
return null;
}
@SuppressWarnings("unchecked")
Dao<FT, FID> castDao = (Dao<FT, FID>) foreignDao;
if (!fieldConfig.isForeignCollectionEager() && !forceEager) {
// we know this won't go recursive so no need for the counters
return new LazyForeignCollection<FT, FID>(castDao, foreignFieldType.dbColumnName, id,
fieldConfig.getForeignCollectionOrderColumn());
}
LevelCounters levelCounters = getLevelCounters();
// are we over our level limit?
if (levelCounters.foreignCollectionLevel >= fieldConfig.getMaxEagerForeignCollectionLevel()) {
// then return a lazy collection instead
return new LazyForeignCollection<FT, FID>(castDao, foreignFieldType.dbColumnName, id,
fieldConfig.getForeignCollectionOrderColumn());
}
levelCounters.foreignCollectionLevel++;
try {
return new EagerForeignCollection<FT, FID>(castDao, foreignFieldType.dbColumnName, id,
fieldConfig.getForeignCollectionOrderColumn());
} finally {
levelCounters.foreignCollectionLevel--;
}
}
/**
* Get the result object from the results. A call through to {@link FieldConverter#resultToJava}.
*/
public <T> T resultToJava(DatabaseResults results, Map<String, Integer> columnPositions) throws SQLException {
Integer dbColumnPos = columnPositions.get(dbColumnName);
if (dbColumnPos == null) {
dbColumnPos = results.findColumn(dbColumnName);
columnPositions.put(dbColumnName, dbColumnPos);
}
@SuppressWarnings("unchecked")
T converted = (T) fieldConverter.resultToJava(this, results, dbColumnPos);
if (dataPersister.isPrimitive()) {
if (fieldConfig.isThrowIfNull() && results.wasNull(dbColumnPos)) {
throw new SQLException("Results value for primitive field '" + field.getName()
+ "' was an invalid null value");
}
} else if (!fieldConverter.isStreamType() && results.wasNull(dbColumnPos)) {
// we can't check if we have a null if this is a stream type
return null;
}
return converted;
}
/**
* Call through to {@link DataPersister#isSelfGeneratedId()}
*/
public boolean isSelfGeneratedId() {
return dataPersister.isSelfGeneratedId();
}
/**
* Call through to {@link DataPersister#generatedId()}
*/
public Object generatedId() {
return dataPersister.generatedId();
}
/**
* Return the value of field in the data argument if it is not the default value for the class. If it is the default
* then null is returned.
*/
public <FV> FV getFieldValueIfNotDefault(Object object) throws SQLException {
@SuppressWarnings("unchecked")
FV fieldValue = (FV) extractJavaFieldValue(object);
if (fieldValue == null) {
return null;
}
boolean isDefault;
if (field.getType() == boolean.class) {
isDefault = fieldValue.equals(DEFAULT_VALUE_BOOLEAN);
} else if (field.getType() == byte.class || field.getType() == Byte.class) {
isDefault = fieldValue.equals(DEFAULT_VALUE_BYTE);
} else if (field.getType() == char.class || field.getType() == Character.class) {
isDefault = fieldValue.equals(DEFAULT_VALUE_CHAR);
} else if (field.getType() == short.class || field.getType() == Short.class) {
isDefault = fieldValue.equals(DEFAULT_VALUE_SHORT);
} else if (field.getType() == int.class || field.getType() == Integer.class) {
isDefault = fieldValue.equals(DEFAULT_VALUE_INT);
} else if (field.getType() == long.class || field.getType() == Long.class) {
isDefault = fieldValue.equals(DEFAULT_VALUE_LONG);
} else if (field.getType() == float.class || field.getType() == Float.class) {
isDefault = fieldValue.equals(DEFAULT_VALUE_FLOAT);
} else if (field.getType() == double.class || field.getType() == Double.class) {
isDefault = fieldValue.equals(DEFAULT_VALUE_DOUBLE);
} else {
isDefault = false;
}
if (isDefault) {
return null;
} else {
return fieldValue;
}
}
/**
* Return An instantiated {@link FieldType} or null if the field does not have a {@link DatabaseField} annotation.
*/
public static FieldType createFieldType(ConnectionSource connectionSource, String tableName, Field field,
Class<?> parentClass) throws SQLException {
DatabaseType databaseType = connectionSource.getDatabaseType();
DatabaseFieldConfig fieldConfig = DatabaseFieldConfig.fromField(databaseType, tableName, field);
if (fieldConfig == null) {
return null;
} else {
return new FieldType(connectionSource, tableName, field, fieldConfig, parentClass);
}
}
@Override
public boolean equals(Object arg) {
if (arg == null || arg.getClass() != this.getClass()) {
return false;
}
FieldType other = (FieldType) arg;
return field.equals(other.field);
}
@Override
public int hashCode() {
return field.hashCode();
}
@Override
public String toString() {
return getClass().getSimpleName() + ":name=" + field.getName() + ",class="
+ field.getDeclaringClass().getSimpleName();
}
/**
* Configure our data persister and any dependent fields. We have to do this here because both the constructor and
* {@link #configDaoInformation} method can set the data-type.
*/
private void assignDataType(DatabaseType databaseType, DataPersister dataPersister) throws SQLException {
this.dataPersister = dataPersister;
if (dataPersister == null) {
return;
}
this.fieldConverter = databaseType.getFieldConverter(dataPersister);
if (this.isGeneratedId && !dataPersister.isValidGeneratedType()) {
StringBuilder sb = new StringBuilder();
sb.append("Generated-id field '").append(field.getName());
sb.append("' in ").append(field.getDeclaringClass().getSimpleName());
sb.append(" can't be type ").append(this.dataPersister.getSqlType());
sb.append(". Must be one of: ");
for (DataType dataType : DataType.values()) {
DataPersister persister = dataType.getDataPersister();
if (persister != null && persister.isValidGeneratedType()) {
sb.append(dataType).append(' ');
}
}
throw new IllegalArgumentException(sb.toString());
}
if (fieldConfig.isThrowIfNull() && !dataPersister.isPrimitive()) {
throw new SQLException("Field " + field.getName() + " must be a primitive if set with throwIfNull");
}
if (this.isId && !dataPersister.isAppropriateId()) {
throw new SQLException("Field '" + field.getName() + "' is of data type " + dataPersister
+ " which cannot be the ID field");
}
this.dataTypeConfigObj = dataPersister.makeConfigObject(this);
String defaultStr = fieldConfig.getDefaultValue();
if (defaultStr == null || defaultStr.equals("")) {
this.defaultValue = null;
} else if (this.isGeneratedId) {
throw new SQLException("Field '" + field.getName() + "' cannot be a generatedId and have a default value '"
+ defaultStr + "'");
} else {
this.defaultValue = this.fieldConverter.parseDefaultString(this, defaultStr);
}
}
private LevelCounters getLevelCounters() {
LevelCounters levelCounters = threadLevelCounters.get();
if (levelCounters == null) {
levelCounters = new LevelCounters();
threadLevelCounters.set(levelCounters);
}
return levelCounters;
}
private static class LevelCounters {
int autoRefreshlevel;
int foreignCollectionLevel;
}
}