123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227 |
- /*
- * Copyright 2004-2011 H2 Group.
- * Copyright 2011 James Moger.
- * Copyright 2012 Frédéric Gaillard.
- *
- * 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;
-
- import java.lang.reflect.Field;
- import java.sql.PreparedStatement;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.IdentityHashMap;
- import java.util.LinkedHashSet;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
-
- import com.iciql.Iciql.ConstraintDeferrabilityType;
- import com.iciql.Iciql.ConstraintDeleteType;
- import com.iciql.Iciql.ConstraintUpdateType;
- import com.iciql.Iciql.DataTypeAdapter;
- import com.iciql.Iciql.EnumId;
- import com.iciql.Iciql.EnumType;
- import com.iciql.Iciql.IQColumn;
- import com.iciql.Iciql.IQConstraint;
- import com.iciql.Iciql.IQContraintForeignKey;
- import com.iciql.Iciql.IQContraintUnique;
- import com.iciql.Iciql.IQContraintsForeignKey;
- import com.iciql.Iciql.IQContraintsUnique;
- import com.iciql.Iciql.IQIgnore;
- import com.iciql.Iciql.IQIndex;
- import com.iciql.Iciql.IQIndexes;
- import com.iciql.Iciql.IQSchema;
- import com.iciql.Iciql.IQTable;
- import com.iciql.Iciql.IQVersion;
- import com.iciql.Iciql.IQView;
- import com.iciql.Iciql.IndexType;
- import com.iciql.util.IciqlLogger;
- import com.iciql.util.StatementBuilder;
- import com.iciql.util.StringUtils;
- import com.iciql.util.Utils;
-
- /**
- * A table definition contains the index definitions of a table, the field
- * definitions, the table name, and other meta data.
- *
- * @param <T>
- * the table type
- */
-
- public class TableDefinition<T> {
-
- /**
- * The meta data of an index.
- */
-
- public static class IndexDefinition {
- public IndexType type;
- public String indexName;
-
- public List<String> columnNames;
- }
-
- /**
- * The meta data of a constraint on foreign key.
- */
-
- public static class ConstraintForeignKeyDefinition {
-
- public String constraintName;
- public List<String> foreignColumns;
- public String referenceTable;
- public List<String> referenceColumns;
- public ConstraintDeleteType deleteType = ConstraintDeleteType.UNSET;
- public ConstraintUpdateType updateType = ConstraintUpdateType.UNSET;
- public ConstraintDeferrabilityType deferrabilityType = ConstraintDeferrabilityType.UNSET;
- }
-
- /**
- * The meta data of a unique constraint.
- */
-
- public static class ConstraintUniqueDefinition {
-
- public String constraintName;
- public List<String> uniqueColumns;
- }
-
-
- /**
- * The meta data of a field.
- */
-
- static class FieldDefinition {
- String columnName;
- Field field;
- String dataType;
- int length;
- int scale;
- boolean isPrimaryKey;
- boolean isAutoIncrement;
- boolean trim;
- boolean nullable;
- String defaultValue;
- EnumType enumType;
- Class<?> enumTypeClass;
- boolean isPrimitive;
- String constraint;
- Class<? extends DataTypeAdapter<?>> typeAdapter;
-
- Object getValue(Object obj) {
- try {
- return field.get(obj);
- } catch (Exception e) {
- throw new IciqlException(e);
- }
- }
-
- private Object initWithNewObject(Object obj) {
- Object o = Utils.newObject(field.getType());
- setValue(obj, o);
- return o;
- }
-
- private void setValue(Object obj, Object o) {
- try {
- if (!field.isAccessible()) {
- field.setAccessible(true);
- }
-
- if (field.getType().isPrimitive() && o == null) {
- // do not attempt to set a primitive to null
- return;
- }
-
- field.set(obj, o);
- } catch (IciqlException e) {
- throw e;
- } catch (Exception e) {
- throw new IciqlException(e);
- }
- }
-
- @Override
- public int hashCode() {
- return columnName.hashCode();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof FieldDefinition) {
- return o.hashCode() == hashCode();
- }
- return false;
- }
- }
-
- public ArrayList<FieldDefinition> fields = Utils.newArrayList();
- String schemaName;
- String tableName;
- String viewTableName;
- int tableVersion;
- List<String> primaryKeyColumnNames;
- boolean memoryTable;
- boolean multiplePrimitiveBools;
-
- private boolean createIfRequired = true;
- private Class<T> clazz;
- private IdentityHashMap<Object, FieldDefinition> fieldMap = Utils.newIdentityHashMap();
- private ArrayList<IndexDefinition> indexes = Utils.newArrayList();
- ArrayList<ConstraintForeignKeyDefinition> constraintsForeignKey = Utils.newArrayList();
- ArrayList<ConstraintUniqueDefinition> constraintsUnique = Utils.newArrayList();
-
- TableDefinition(Class<T> clazz) {
- this.clazz = clazz;
- schemaName = null;
- tableName = clazz.getSimpleName();
- }
-
- Class<T> getModelClass() {
- return clazz;
- }
-
- List<FieldDefinition> getFields() {
- return fields;
- }
-
- void defineSchemaName(String schemaName) {
- this.schemaName = schemaName;
- }
-
- void defineTableName(String tableName) {
- this.tableName = tableName;
- }
-
- void defineViewTableName(String viewTableName) {
- this.viewTableName = viewTableName;
- }
-
- void defineMemoryTable() {
- this.memoryTable = true;
- }
-
- void defineSkipCreate() {
- this.createIfRequired = false;
- }
-
- /**
- * Define a primary key by the specified model fields.
- *
- * @param modelFields
- * the ordered list of model fields
- */
- void definePrimaryKey(Object[] modelFields) {
- List<String> columnNames = mapColumnNames(modelFields);
- setPrimaryKey(columnNames);
- }
-
- /**
- * Define a primary key by the specified column names.
- *
- * @param columnNames
- * the ordered list of column names
- */
- private void setPrimaryKey(List<String> columnNames) {
- primaryKeyColumnNames = Utils.newArrayList(columnNames);
- List<String> pkNames = Utils.newArrayList();
- for (String name : columnNames) {
- pkNames.add(name.toLowerCase());
- }
- // set isPrimaryKey flag for all field definitions
- for (FieldDefinition fieldDefinition : fieldMap.values()) {
- fieldDefinition.isPrimaryKey = pkNames.contains(fieldDefinition.columnName.toLowerCase());
- }
- }
-
- private <A> String getColumnName(A fieldObject) {
- FieldDefinition def = fieldMap.get(fieldObject);
- return def == null ? null : def.columnName;
- }
-
- private ArrayList<String> mapColumnNames(Object[] columns) {
- ArrayList<String> columnNames = Utils.newArrayList();
- for (Object column : columns) {
- columnNames.add(getColumnName(column));
- }
- return columnNames;
- }
-
- /**
- * Defines an index with the specified model fields.
- *
- * @param name
- * the index name (optional)
- * @param type
- * the index type (STANDARD, HASH, UNIQUE, UNIQUE_HASH)
- * @param modelFields
- * the ordered list of model fields
- */
- void defineIndex(String name, IndexType type, Object[] modelFields) {
- List<String> columnNames = mapColumnNames(modelFields);
- addIndex(name, type, columnNames);
- }
-
- /**
- * Defines an index with the specified column names.
- *
- * @param type
- * the index type (STANDARD, HASH, UNIQUE, UNIQUE_HASH)
- * @param columnNames
- * the ordered list of column names
- */
- private void addIndex(String name, IndexType type, List<String> columnNames) {
- IndexDefinition index = new IndexDefinition();
- if (StringUtils.isNullOrEmpty(name)) {
- index.indexName = tableName + "_idx_" + indexes.size();
- } else {
- index.indexName = name;
- }
- index.columnNames = Utils.newArrayList(columnNames);
- index.type = type;
- indexes.add(index);
- }
-
- /**
- * Defines an unique constraint with the specified model fields.
- *
- * @param name
- * the constraint name (optional)
- * @param modelFields
- * the ordered list of model fields
- */
- void defineConstraintUnique(String name, Object[] modelFields) {
- List<String> columnNames = mapColumnNames(modelFields);
- addConstraintUnique(name, columnNames);
- }
-
- /**
- * Defines an unique constraint.
- *
- * @param name
- * @param columnNames
- */
- private void addConstraintUnique(String name, List<String> columnNames) {
- ConstraintUniqueDefinition constraint = new ConstraintUniqueDefinition();
- if (StringUtils.isNullOrEmpty(name)) {
- constraint.constraintName = tableName + "_unique_" + constraintsUnique.size();
- } else {
- constraint.constraintName = name;
- }
- constraint.uniqueColumns = Utils.newArrayList(columnNames);
- constraintsUnique.add(constraint);
- }
-
- /**
- * Defines a foreign key constraint with the specified model fields.
- *
- * @param name
- * the constraint name (optional)
- * @param modelFields
- * the ordered list of model fields
- */
- void defineForeignKey(String name, Object[] modelFields, String refTableName, Object[] refModelFields,
- ConstraintDeleteType deleteType, ConstraintUpdateType updateType,
- ConstraintDeferrabilityType deferrabilityType) {
- List<String> columnNames = mapColumnNames(modelFields);
- List<String> referenceColumnNames = mapColumnNames(refModelFields);
- addConstraintForeignKey(name, columnNames, refTableName, referenceColumnNames,
- deleteType, updateType, deferrabilityType);
- }
-
- void defineColumnName(Object column, String columnName) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.columnName = columnName;
- }
- }
-
- void defineAutoIncrement(Object column) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.isAutoIncrement = true;
- }
- }
-
- void defineLength(Object column, int length) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.length = length;
- }
- }
-
- void defineScale(Object column, int scale) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.scale = scale;
- }
- }
-
- void defineTrim(Object column) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.trim = true;
- }
- }
-
- void defineNullable(Object column, boolean isNullable) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.nullable = isNullable;
- }
- }
-
- void defineDefaultValue(Object column, String defaultValue) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.defaultValue = defaultValue;
- }
- }
-
- void defineConstraint(Object column, String constraint) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.constraint = constraint;
- }
- }
-
- void defineTypeAdapter(Object column, Class<? extends DataTypeAdapter<?>> typeAdapter) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.typeAdapter = typeAdapter;
- }
- }
-
- void mapFields(Db db) {
- boolean byAnnotationsOnly = false;
- boolean inheritColumns = false;
- if (clazz.isAnnotationPresent(IQTable.class)) {
- IQTable tableAnnotation = clazz.getAnnotation(IQTable.class);
- byAnnotationsOnly = tableAnnotation.annotationsOnly();
- inheritColumns = tableAnnotation.inheritColumns();
- }
-
- if (clazz.isAnnotationPresent(IQView.class)) {
- IQView viewAnnotation = clazz.getAnnotation(IQView.class);
- byAnnotationsOnly = viewAnnotation.annotationsOnly();
- inheritColumns = viewAnnotation.inheritColumns();
- }
-
- List<Field> classFields = classFields(inheritColumns);
-
- Set<FieldDefinition> uniqueFields = new LinkedHashSet<FieldDefinition>();
- T defaultObject = Db.instance(clazz);
- for (Field f : classFields) {
- // check if we should skip this field
- if (f.isAnnotationPresent(IQIgnore.class)) {
- continue;
- }
-
- // default to field name
- String columnName = f.getName();
- boolean isAutoIncrement = false;
- boolean isPrimaryKey = false;
- int length = 0;
- int scale = 0;
- boolean trim = false;
- boolean nullable = !f.getType().isPrimitive();
- String defaultValue = "";
- String constraint = "";
- String dataType = null;
- Class<? extends DataTypeAdapter<?>> typeAdapter = null;
-
- // configure Java -> SQL enum mapping
- EnumType enumType = Utils.getEnumType(f);
- Class<?> enumTypeClass = Utils.getEnumTypeClass(f);
-
- // 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);
- }
-
- // identify the type adapter
- typeAdapter = Utils.getDataTypeAdapter(f.getAnnotations());
- if (typeAdapter == null) {
- typeAdapter = Utils.getDataTypeAdapter(f.getType().getAnnotations());
- }
-
- if (typeAdapter != null) {
- DataTypeAdapter<?> dtt = db.getDialect().getAdapter(typeAdapter);
- dataType = dtt.getDataType();
- }
-
- boolean hasAnnotation = f.isAnnotationPresent(IQColumn.class);
- if (hasAnnotation) {
- IQColumn col = f.getAnnotation(IQColumn.class);
- if (!StringUtils.isNullOrEmpty(col.name())) {
- columnName = col.name();
- }
- isAutoIncrement = col.autoIncrement();
- isPrimaryKey = col.primaryKey();
- length = col.length();
- scale = col.scale();
- trim = col.trim();
- nullable = col.nullable();
-
- // annotation overrides
- if (!StringUtils.isNullOrEmpty(col.defaultValue())) {
- defaultValue = col.defaultValue();
- }
- }
-
- boolean hasConstraint = f.isAnnotationPresent(IQConstraint.class);
- if (hasConstraint) {
- IQConstraint con = f.getAnnotation(IQConstraint.class);
- // annotation overrides
- if (!StringUtils.isNullOrEmpty(con.value())) {
- constraint = con.value();
- }
- }
-
- boolean reflectiveMatch = !byAnnotationsOnly;
- if (reflectiveMatch || hasAnnotation || hasConstraint) {
- FieldDefinition fieldDef = new FieldDefinition();
- fieldDef.isPrimitive = f.getType().isPrimitive();
- fieldDef.field = f;
- fieldDef.columnName = columnName;
- fieldDef.isAutoIncrement = isAutoIncrement;
- fieldDef.isPrimaryKey = isPrimaryKey;
- fieldDef.length = length;
- fieldDef.scale = scale;
- fieldDef.trim = trim;
- fieldDef.nullable = nullable;
- fieldDef.defaultValue = defaultValue;
- fieldDef.enumType = enumType;
- fieldDef.enumTypeClass = enumTypeClass;
- fieldDef.dataType = StringUtils.isNullOrEmpty(dataType) ? ModelUtils.getDataType(fieldDef) : dataType;
- fieldDef.typeAdapter = typeAdapter;
- fieldDef.constraint = constraint;
- uniqueFields.add(fieldDef);
- }
- }
- fields.addAll(uniqueFields);
-
- List<String> primaryKey = Utils.newArrayList();
- int primitiveBoolean = 0;
- for (FieldDefinition fieldDef : fields) {
- if (fieldDef.isPrimaryKey) {
- primaryKey.add(fieldDef.columnName);
- }
- if (fieldDef.isPrimitive && fieldDef.field.getType().equals(boolean.class)) {
- primitiveBoolean++;
- }
- }
- if (primitiveBoolean > 1) {
- multiplePrimitiveBools = true;
- IciqlLogger
- .warn("Model {0} has multiple primitive booleans! Possible where,set,join clause problem!");
- }
- if (primaryKey.size() > 0) {
- setPrimaryKey(primaryKey);
- }
- }
-
- private List<Field> classFields(boolean inheritColumns) {
- List<Field> classFields = Utils.newArrayList();
- classFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
- Class<?> superClass = clazz;
- while (inheritColumns) {
- superClass = superClass.getSuperclass();
- classFields.addAll(Arrays.asList(superClass.getDeclaredFields()));
-
- if (superClass.isAnnotationPresent(IQView.class)) {
- IQView superView = superClass.getAnnotation(IQView.class);
- inheritColumns = superView.inheritColumns();
- } else if (superClass.isAnnotationPresent(IQTable.class)) {
- IQTable superTable = superClass.getAnnotation(IQTable.class);
- inheritColumns = superTable.inheritColumns();
- } else {
- inheritColumns = false;
- }
- }
- return classFields;
- }
-
- void checkMultipleBooleans() {
- if (multiplePrimitiveBools) {
- throw new IciqlException(
- "Can not explicitly reference a primitive boolean if there are multiple boolean fields in your model class!");
- }
- }
-
- void checkMultipleEnums(Object o) {
- if (o == null) {
- return;
- }
- Class<?> clazz = o.getClass();
- if (!clazz.isEnum()) {
- return;
- }
-
- int fieldCount = 0;
- for (FieldDefinition fieldDef : fields) {
- Class<?> targetType = fieldDef.field.getType();
- if (clazz.equals(targetType)) {
- fieldCount++;
- }
- }
-
- if (fieldCount > 1) {
- throw new IciqlException(
- "Can not explicitly reference {0} because there are {1} {0} fields in your model class!",
- clazz.getSimpleName(), fieldCount);
- }
- }
-
- /**
- * Optionally truncates strings to the maximum length and converts
- * java.lang.Enum types to Strings or Integers.
- */
- 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;
- switch (field.enumType) {
- case NAME:
- if (field.trim && field.length > 0) {
- if (iqenum.name().length() > field.length) {
- return iqenum.name().substring(0, field.length);
- }
- }
- return iqenum.name();
- case 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();
- }
- }
-
- if (field.trim && field.length > 0) {
- if (value instanceof String) {
- // clip strings
- String s = (String) value;
- if (s.length() > field.length) {
- return s.substring(0, field.length);
- }
- return s;
- }
- return value;
- }
-
- // return the value unchanged
- return value;
- }
-
- PreparedStatement createInsertStatement(Db db, Object obj, boolean returnKey) {
- SQLStatement stat = new SQLStatement(db);
- StatementBuilder buff = new StatementBuilder("INSERT INTO ");
- buff.append(db.getDialect().prepareTableName(schemaName, tableName)).append('(');
- for (FieldDefinition field : fields) {
- if (skipInsertField(field, obj)) {
- continue;
- }
- buff.appendExceptFirst(", ");
- buff.append(db.getDialect().prepareColumnName(field.columnName));
- }
- buff.append(") VALUES(");
- buff.resetCount();
- for (FieldDefinition field : fields) {
- if (skipInsertField(field, obj)) {
- continue;
- }
- 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());
- }
- Object parameter = db.getDialect().serialize(value, field.typeAdapter);
- stat.addParameter(parameter);
- }
- buff.append(')');
- stat.setSQL(buff.toString());
- IciqlLogger.insert(stat.getSQL());
- return stat.prepare(returnKey);
- }
-
- long insert(Db db, Object obj, boolean returnKey) {
- if (!StringUtils.isNullOrEmpty(viewTableName)) {
- throw new IciqlException("Iciql does not support inserting rows into views!");
- }
- SQLStatement stat = new SQLStatement(db);
- StatementBuilder buff = new StatementBuilder("INSERT INTO ");
- buff.append(db.getDialect().prepareTableName(schemaName, tableName)).append('(');
- for (FieldDefinition field : fields) {
- if (skipInsertField(field, obj)) {
- continue;
- }
- buff.appendExceptFirst(", ");
- buff.append(db.getDialect().prepareColumnName(field.columnName));
- }
- buff.append(") VALUES(");
- buff.resetCount();
- for (FieldDefinition field : fields) {
- if (skipInsertField(field, obj)) {
- continue;
- }
- 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());
- }
- Object parameter = db.getDialect().serialize(value, field.typeAdapter);
- stat.addParameter(parameter);
- }
- buff.append(')');
- stat.setSQL(buff.toString());
- IciqlLogger.insert(stat.getSQL());
- if (returnKey) {
- return stat.executeInsert();
- }
- return stat.executeUpdate();
- }
-
- private boolean skipInsertField(FieldDefinition field, Object obj) {
- if (field.isAutoIncrement) {
- Object value = getValue(obj, field);
- if (field.isPrimitive) {
- // skip uninitialized primitive autoincrement values
- if (value.toString().equals("0")) {
- return true;
- }
- } else if (value == null) {
- // 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;
- }
-
- int merge(Db db, Object obj) {
- if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {
- throw new IllegalStateException("No primary key columns defined for table " + obj.getClass()
- + " - no update possible");
- }
- SQLStatement stat = new SQLStatement(db);
- db.getDialect().prepareMerge(stat, schemaName, tableName, this, obj);
- IciqlLogger.merge(stat.getSQL());
- return stat.executeUpdate();
- }
-
- int update(Db db, Object obj) {
- if (!StringUtils.isNullOrEmpty(viewTableName)) {
- throw new IciqlException("Iciql does not support updating rows in views!");
- }
- if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {
- throw new IllegalStateException("No primary key columns defined for table " + obj.getClass()
- + " - no update possible");
- }
- SQLStatement stat = new SQLStatement(db);
- StatementBuilder buff = new StatementBuilder("UPDATE ");
- buff.append(db.getDialect().prepareTableName(schemaName, tableName)).append(" SET ");
- buff.resetCount();
-
- 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 parameter = db.getDialect().serialize(value, field.typeAdapter);
- stat.addParameter(parameter);
- }
- }
- Object alias = Utils.newObject(obj.getClass());
- Query<Object> query = Query.from(db, alias);
- boolean firstCondition = true;
- for (FieldDefinition field : fields) {
- if (field.isPrimaryKey) {
- Object fieldAlias = field.getValue(alias);
- Object value = field.getValue(obj);
- if (field.isPrimitive) {
- fieldAlias = query.getPrimitiveAliasByValue(fieldAlias);
- }
- if (!firstCondition) {
- query.addConditionToken(ConditionAndOr.AND);
- }
- firstCondition = false;
- query.addConditionToken(new Condition<Object>(fieldAlias, value, CompareType.EQUAL));
- }
- }
- stat.setSQL(buff.toString());
- query.appendWhere(stat);
- IciqlLogger.update(stat.getSQL());
- return stat.executeUpdate();
- }
-
- int delete(Db db, Object obj) {
- if (!StringUtils.isNullOrEmpty(viewTableName)) {
- throw new IciqlException("Iciql does not support deleting rows from views!");
- }
- if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {
- throw new IllegalStateException("No primary key columns defined for table " + obj.getClass()
- + " - no update possible");
- }
- SQLStatement stat = new SQLStatement(db);
- StatementBuilder buff = new StatementBuilder("DELETE FROM ");
- buff.append(db.getDialect().prepareTableName(schemaName, tableName));
- buff.resetCount();
- Object alias = Utils.newObject(obj.getClass());
- Query<Object> query = Query.from(db, alias);
- boolean firstCondition = true;
- for (FieldDefinition field : fields) {
- if (field.isPrimaryKey) {
- Object fieldAlias = field.getValue(alias);
- Object value = field.getValue(obj);
- if (field.isPrimitive) {
- fieldAlias = query.getPrimitiveAliasByValue(fieldAlias);
- }
- if (!firstCondition) {
- query.addConditionToken(ConditionAndOr.AND);
- }
- firstCondition = false;
- query.addConditionToken(new Condition<Object>(fieldAlias, value, CompareType.EQUAL));
- }
- }
- stat.setSQL(buff.toString());
- query.appendWhere(stat);
- IciqlLogger.delete(stat.getSQL());
- return stat.executeUpdate();
- }
-
- TableDefinition<T> createIfRequired(Db db) {
- // globally enable/disable check of create if required
- if (db.getSkipCreate()) {
- return this;
- }
- if (!createIfRequired) {
- // skip table and index creation
- // but still check for upgrades
- db.upgradeTable(this);
- return this;
- }
- if (db.hasCreated(clazz)) {
- return this;
- }
- SQLStatement stat = new SQLStatement(db);
- if (StringUtils.isNullOrEmpty(viewTableName)) {
- db.getDialect().prepareCreateTable(stat, this);
- } else {
- db.getDialect().prepareCreateView(stat, this);
- }
- IciqlLogger.create(stat.getSQL());
- try {
- stat.executeUpdate();
- } catch (IciqlException e) {
- if (e.getIciqlCode() != IciqlException.CODE_OBJECT_ALREADY_EXISTS) {
- throw e;
- }
- }
-
- // create indexes
- for (IndexDefinition index : indexes) {
- stat = new SQLStatement(db);
- db.getDialect().prepareCreateIndex(stat, schemaName, tableName, index);
- IciqlLogger.create(stat.getSQL());
- try {
- stat.executeUpdate();
- } catch (IciqlException e) {
- if (e.getIciqlCode() != IciqlException.CODE_OBJECT_ALREADY_EXISTS
- && e.getIciqlCode() != IciqlException.CODE_DUPLICATE_KEY) {
- throw e;
- }
- }
- }
-
- // tables are created using IF NOT EXISTS
- // but we may still need to upgrade
- db.upgradeTable(this);
- return this;
- }
-
- void mapObject(Object obj) {
- fieldMap.clear();
- initObject(obj, fieldMap);
-
- if (clazz.isAnnotationPresent(IQSchema.class)) {
- IQSchema schemaAnnotation = clazz.getAnnotation(IQSchema.class);
- // setup schema name mapping, if properly annotated
- if (!StringUtils.isNullOrEmpty(schemaAnnotation.value())) {
- schemaName = schemaAnnotation.value();
- }
- }
-
- if (clazz.isAnnotationPresent(IQTable.class)) {
- IQTable tableAnnotation = clazz.getAnnotation(IQTable.class);
-
- // setup table name mapping, if properly annotated
- if (!StringUtils.isNullOrEmpty(tableAnnotation.name())) {
- tableName = tableAnnotation.name();
- }
-
- // allow control over createTableIfRequired()
- createIfRequired = tableAnnotation.create();
-
- // model version
- if (clazz.isAnnotationPresent(IQVersion.class)) {
- IQVersion versionAnnotation = clazz.getAnnotation(IQVersion.class);
- if (versionAnnotation.value() > 0) {
- tableVersion = versionAnnotation.value();
- }
- }
-
- // setup the primary index, if properly annotated
- if (tableAnnotation.primaryKey().length > 0) {
- List<String> primaryKey = Utils.newArrayList();
- primaryKey.addAll(Arrays.asList(tableAnnotation.primaryKey()));
- setPrimaryKey(primaryKey);
- }
- }
-
- if (clazz.isAnnotationPresent(IQView.class)) {
- IQView viewAnnotation = clazz.getAnnotation(IQView.class);
-
- // setup view name mapping, if properly annotated
- // set this as the table name so it fits in seemlessly with iciql
- if (!StringUtils.isNullOrEmpty(viewAnnotation.name())) {
- tableName = viewAnnotation.name();
- } else {
- tableName = clazz.getSimpleName();
- }
-
- // setup source table name mapping, if properly annotated
- if (!StringUtils.isNullOrEmpty(viewAnnotation.tableName())) {
- viewTableName = viewAnnotation.tableName();
- } else {
- // check for IQTable annotation on super class
- Class<?> superClass = clazz.getSuperclass();
- if (superClass.isAnnotationPresent(IQTable.class)) {
- IQTable table = superClass.getAnnotation(IQTable.class);
- if (StringUtils.isNullOrEmpty(table.name())) {
- // super.SimpleClassName
- viewTableName = superClass.getSimpleName();
- } else {
- // super.IQTable.name()
- viewTableName = table.name();
- }
- } else if (superClass.isAnnotationPresent(IQView.class)) {
- // super class is a view
- IQView parentView = superClass.getAnnotation(IQView.class);
- if (StringUtils.isNullOrEmpty(parentView.tableName())) {
- // parent view does not define a tableName, must be inherited
- Class<?> superParent = superClass.getSuperclass();
- if (superParent != null && superParent.isAnnotationPresent(IQTable.class)) {
- IQTable superParentTable = superParent.getAnnotation(IQTable.class);
- if (StringUtils.isNullOrEmpty(superParentTable.name())) {
- // super.super.SimpleClassName
- viewTableName = superParent.getSimpleName();
- } else {
- // super.super.IQTable.name()
- viewTableName = superParentTable.name();
- }
- }
- } else {
- // super.IQView.tableName()
- viewTableName = parentView.tableName();
- }
- }
-
- if (StringUtils.isNullOrEmpty(viewTableName)) {
- // still missing view table name
- throw new IciqlException("View model class \"{0}\" is missing a table name!", tableName);
- }
- }
-
- // allow control over createTableIfRequired()
- createIfRequired = viewAnnotation.create();
- }
-
- if (clazz.isAnnotationPresent(IQIndex.class)) {
- // single table index
- IQIndex index = clazz.getAnnotation(IQIndex.class);
- addIndex(index);
- }
-
- if (clazz.isAnnotationPresent(IQIndexes.class)) {
- // multiple table indexes
- IQIndexes indexes = clazz.getAnnotation(IQIndexes.class);
- for (IQIndex index : indexes.value()) {
- addIndex(index);
- }
- }
-
- if (clazz.isAnnotationPresent(IQContraintUnique.class)) {
- // single table unique constraint
- IQContraintUnique constraint = clazz.getAnnotation(IQContraintUnique.class);
- addConstraintUnique(constraint);
- }
-
- if (clazz.isAnnotationPresent(IQContraintsUnique.class)) {
- // multiple table unique constraints
- IQContraintsUnique constraints = clazz.getAnnotation(IQContraintsUnique.class);
- for (IQContraintUnique constraint : constraints.value()) {
- addConstraintUnique(constraint);
- }
- }
-
- if (clazz.isAnnotationPresent(IQContraintForeignKey.class)) {
- // single table constraint
- IQContraintForeignKey constraint = clazz.getAnnotation(IQContraintForeignKey.class);
- addConstraintForeignKey(constraint);
- }
-
- if (clazz.isAnnotationPresent(IQContraintsForeignKey.class)) {
- // multiple table constraints
- IQContraintsForeignKey constraints = clazz.getAnnotation(IQContraintsForeignKey.class);
- for (IQContraintForeignKey constraint : constraints.value()) {
- addConstraintForeignKey(constraint);
- }
- }
-
- }
-
- private void addConstraintForeignKey(IQContraintForeignKey constraint) {
- List<String> foreignColumns = Arrays.asList(constraint.foreignColumns());
- List<String> referenceColumns = Arrays.asList(constraint.referenceColumns());
- addConstraintForeignKey(constraint.name(), foreignColumns, constraint.referenceName(), referenceColumns, constraint.deleteType(), constraint.updateType(), constraint.deferrabilityType());
- }
-
- private void addConstraintUnique(IQContraintUnique constraint) {
- List<String> uniqueColumns = Arrays.asList(constraint.uniqueColumns());
- addConstraintUnique(constraint.name(), uniqueColumns);
- }
-
- /**
- * Defines a foreign key constraint with the specified parameters.
- *
- * @param name
- * name of the constraint
- * @param foreignColumns
- * list of columns declared as foreign
- * @param referenceName
- * reference table name
- * @param referenceColumns
- * list of columns used in reference table
- * @param deleteType
- * action on delete
- * @param updateType
- * action on update
- * @param deferrabilityType
- * deferrability mode
- */
- private void addConstraintForeignKey(String name,
- List<String> foreignColumns, String referenceName,
- List<String> referenceColumns, ConstraintDeleteType deleteType,
- ConstraintUpdateType updateType, ConstraintDeferrabilityType deferrabilityType) {
- ConstraintForeignKeyDefinition constraint = new ConstraintForeignKeyDefinition();
- if (StringUtils.isNullOrEmpty(name)) {
- constraint.constraintName = tableName + "_fkey_" + constraintsForeignKey.size();
- } else {
- constraint.constraintName = name;
- }
- constraint.foreignColumns = Utils.newArrayList(foreignColumns);
- constraint.referenceColumns = Utils.newArrayList(referenceColumns);
- constraint.referenceTable = referenceName;
- constraint.deleteType = deleteType;
- constraint.updateType = updateType;
- constraint.deferrabilityType = deferrabilityType;
- constraintsForeignKey.add(constraint);
- }
-
- private void addIndex(IQIndex index) {
- List<String> columns = Arrays.asList(index.value());
- addIndex(index.name(), index.type(), columns);
- }
-
- List<IndexDefinition> getIndexes() {
- return indexes;
- }
-
- List<ConstraintUniqueDefinition> getContraintsUnique() {
- return constraintsUnique;
- }
-
- List<ConstraintForeignKeyDefinition> getContraintsForeignKey() {
- return constraintsForeignKey;
- }
-
- private void initObject(Object obj, Map<Object, FieldDefinition> map) {
- for (FieldDefinition def : fields) {
- Object newValue = def.initWithNewObject(obj);
- map.put(newValue, def);
- }
- }
-
- void initSelectObject(SelectTable<T> table, Object obj, Map<Object, SelectColumn<T>> map, boolean reuse) {
- for (FieldDefinition def : fields) {
- Object value;
- if (!reuse) {
- value = def.initWithNewObject(obj);
- } else {
- value = def.getValue(obj);
- }
- SelectColumn<T> column = new SelectColumn<T>(table, def);
- map.put(value, column);
- }
- }
-
- /**
- * Most queries executed by iciql have named select lists (select alpha,
- * beta where...) but sometimes a wildcard select is executed (select *).
- * When a wildcard query is executed on a table that has more columns than
- * are mapped in your model object, this creates a column mapping issue.
- * JaQu assumed that you can always use the integer index of the
- * reflectively mapped field definition to determine position in the result
- * set.
- *
- * This is not always true.
- *
- * iciql identifies when a select * query is executed and maps column names
- * to a column index from the result set. If the select statement is
- * explicit, then the standard assumed column index is used instead.
- *
- * @param rs
- * @return
- */
- int[] mapColumns(boolean wildcardSelect, ResultSet rs) {
- int[] columns = new int[fields.size()];
- for (int i = 0; i < fields.size(); i++) {
- try {
- FieldDefinition def = fields.get(i);
- int columnIndex;
- if (wildcardSelect) {
- // select *
- // create column index by field name
- columnIndex = rs.findColumn(def.columnName);
- } else {
- // select alpha, beta, gamma, etc
- // explicit select order
- columnIndex = i + 1;
- }
- columns[i] = columnIndex;
- } catch (SQLException s) {
- throw new IciqlException(s);
- }
- }
- return columns;
- }
-
- void readRow(SQLDialect dialect, Object item, ResultSet rs, int[] columns) {
- for (int i = 0; i < fields.size(); i++) {
- FieldDefinition def = fields.get(i);
- 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);
- }
- }
-
- void appendSelectList(SQLStatement stat) {
- for (int i = 0; i < fields.size(); i++) {
- if (i > 0) {
- stat.appendSQL(", ");
- }
- FieldDefinition def = fields.get(i);
- stat.appendColumn(def.columnName);
- }
- }
-
- <Y, X> void appendSelectList(SQLStatement stat, Query<Y> query, X x) {
- // select t0.col1, t0.col2, t0.col3...
- // select table1.col1, table1.col2, table1.col3...
- String selectDot = "";
- SelectTable<?> sel = query.getSelectTable(x);
- if (sel != null) {
- if (query.isJoin()) {
- selectDot = sel.getAs() + ".";
- } else {
- String sn = sel.getAliasDefinition().schemaName;
- String tn = sel.getAliasDefinition().tableName;
- selectDot = query.getDb().getDialect().prepareTableName(sn, tn) + ".";
- }
- }
-
- for (int i = 0; i < fields.size(); i++) {
- if (i > 0) {
- stat.appendSQL(", ");
- }
- stat.appendSQL(selectDot);
- FieldDefinition def = fields.get(i);
- if (def.isPrimitive) {
- Object obj = def.getValue(x);
- Object alias = query.getPrimitiveAliasByValue(obj);
- query.appendSQL(stat, x, alias);
- } else {
- Object obj = def.getValue(x);
- query.appendSQL(stat, x, obj);
- }
- }
- }
- }
|