\r
<property name="library.jar" value="iciql-${iq.version}.jar" />\r
<property name="javadoc.jar" value="iciql-${iq.version}-javadoc.jar" />\r
- <property name="sources.jar" value="/iciql-${iq.version}-sources.jar" />\r
+ <property name="sources.jar" value="iciql-${iq.version}-sources.jar" />\r
<property name="distribution.zip" value="iciql-${iq.version}.zip" />\r
</target>\r
\r
/*\r
* Copyright 2004-2011 H2 Group.\r
* Copyright 2011 James Moger.\r
+ * Copyright 2012 Frédéric Gaillard.\r
*\r
* Licensed under the Apache License, Version 2.0 (the "License");\r
* you may not use this file except in compliance with the License.\r
currentTableDefinition.defineIndex(name, type, columns);\r
}\r
\r
+ public static void constraintUnique(String name, Object... columns) {\r
+ checkInDefine();\r
+ currentTableDefinition.defineConstraintUnique(name, columns);\r
+ }\r
+ \r
+ /*\r
+ * The variable argument type Object can't be used twice :-)\r
+ */\r
+// public static void constraintForeignKey(String name, String refTableName,\r
+// ConstraintDeleteType deleteType, ConstraintUpdateType updateType,\r
+// ConstraintDeferrabilityType deferrabilityType, Object... columns, Object... refColumns) {\r
+// checkInDefine();\r
+// currentTableDefinition.defineForeignKey(name, columns, refTableName, Columns, deleteType, updateType, deferrabilityType);\r
+// }\r
+ \r
public static void primaryKey(Object... columns) {\r
checkInDefine();\r
currentTableDefinition.definePrimaryKey(columns);\r
/*\r
* Copyright 2004-2011 H2 Group.\r
* Copyright 2011 James Moger.\r
+ * Copyright 2012 Frédéric Gaillard.\r
*\r
* Licensed under the Apache License, Version 2.0 (the "License");\r
* you may not use this file except in compliance with the License.\r
String[] value() default {};\r
}\r
\r
+ /**\r
+ * Enumeration defining the ON DELETE actions.\r
+ */\r
+ public static enum ConstraintDeleteType {\r
+ UNSET, CASCADE, RESTRICT, SET_NULL, NO_ACTION, SET_DEFAULT;\r
+ }\r
+\r
+ /**\r
+ * Enumeration defining the ON UPDATE actions.\r
+ */\r
+ public static enum ConstraintUpdateType {\r
+ UNSET, CASCADE, RESTRICT, SET_NULL, NO_ACTION, SET_DEFAULT;\r
+ }\r
+ \r
+ /**\r
+ * Enumeration defining the deferrability.\r
+ */\r
+ public static enum ConstraintDeferrabilityType {\r
+ UNSET, DEFERRABLE_INITIALLY_DEFERRED, DEFERRABLE_INITIALLY_IMMEDIATE, NOT_DEFERRABLE;\r
+ }\r
+ \r
+ /**\r
+ * A foreign key constraint annotation.\r
+ * <p>\r
+ * <ul>\r
+ * <li>@IQContraintForeignKey(\r
+ * foreignColumns = { "idaccount"}, \r
+ * referenceName = "account", \r
+ * referenceColumns = { "id" },\r
+ * deleteType = ConstrainDeleteType.CASCADE,\r
+ * updateType = ConstraintUpdateType.NO_ACTION )\r
+ * </ul>\r
+ * Note : reference columns should have a unique constraint defined in referenceName table,\r
+ * some database used to define a unique index instead of a unique constraint\r
+ */\r
+ @Retention(RetentionPolicy.RUNTIME)\r
+ @Target(ElementType.TYPE)\r
+ public @interface IQContraintForeignKey {\r
+\r
+ /**\r
+ * Constraint name. If null or empty, iciql will generate one.\r
+ */\r
+ String name() default "";\r
+\r
+ /**\r
+ * Type of the action on delete, default to unspecified.\r
+ * <ul>\r
+ * <li>com.iciql.iciql.ConstrainDeleteType.CASCADE\r
+ * <li>com.iciql.iciql.ConstrainDeleteType.RESTRICT\r
+ * <li>com.iciql.iciql.ConstrainDeleteType.SET_NULL\r
+ * <li>com.iciql.iciql.ConstrainDeleteType.NO_ACTION\r
+ * <li>com.iciql.iciql.ConstrainDeleteType.SET_DEFAULT\r
+ * </ul>\r
+ */\r
+ ConstraintDeleteType deleteType() default ConstraintDeleteType.UNSET;\r
+\r
+ /**\r
+ * Type of the action on update, default to unspecified.\r
+ * <ul>\r
+ * <li>com.iciql.iciql.ConstrainUpdateType.CASCADE\r
+ * <li>com.iciql.iciql.ConstrainUpdateType.RESTRICT\r
+ * <li>com.iciql.iciql.ConstrainUpdateType.SET_NULL\r
+ * <li>com.iciql.iciql.ConstrainUpdateType.NO_ACTION\r
+ * <li>com.iciql.iciql.ConstrainUpdateType.SET_DEFAULT\r
+ * </ul>\r
+ */\r
+ ConstraintUpdateType updateType() default ConstraintUpdateType.UNSET;\r
+\r
+ /**\r
+ * Type of the deferrability mode, default to unspecified\r
+ * <ul>\r
+ * <li>com.iciql.iciql.ConstrainUpdateType.CASCADE\r
+ * <li>ConstraintDeferrabilityType.DEFERRABLE_INITIALLY_DEFERRED\r
+ * <li>ConstraintDeferrabilityType.DEFERRABLE_INITIALLY_IMMEDIATE\r
+ * <li>ConstraintDeferrabilityType.NOT_DEFERRABLE\r
+ * </ul>\r
+ */\r
+ ConstraintDeferrabilityType deferrabilityType() default ConstraintDeferrabilityType.UNSET;\r
+ \r
+ /**\r
+ * The source table for the columns defined as foreign.\r
+ */\r
+ String tableName() default "";\r
+\r
+ /**\r
+ * Columns defined as 'foreign'.\r
+ * <ul>\r
+ * <li>single column : foreignColumns = "id"\r
+ * <li>multiple column : foreignColumns = { "id", "name", "date" }\r
+ * </ul>\r
+ */\r
+ String[] foreignColumns() default {};\r
+\r
+ /**\r
+ * The reference table for the columns defined as references.\r
+ */\r
+ String referenceName() default "";\r
+ \r
+ /**\r
+ * Columns defined as 'references'.\r
+ * <ul>\r
+ * <li>single column : referenceColumns = "id"\r
+ * <li>multiple column : referenceColumns = { "id", "name", "date" }\r
+ * </ul>\r
+ */\r
+ String[] referenceColumns() default {};\r
+ }\r
+\r
+ /**\r
+ * Annotation to specify multiple foreign keys constraints.\r
+ */\r
+ @Retention(RetentionPolicy.RUNTIME)\r
+ @Target(ElementType.TYPE)\r
+ public @interface IQContraintsForeignKey {\r
+ IQContraintForeignKey[] value() default {};\r
+ }\r
+ \r
+ /**\r
+ * A unique constraint annotation.\r
+ * <p>\r
+ * <ul>\r
+ * <li>@IQContraintUnique(uniqueColumns = { "street", "city" })\r
+ * <li>@IQContraintUnique(name="streetconstraint", uniqueColumns = { "street", "city" })\r
+ * </ul>\r
+ */\r
+ @Retention(RetentionPolicy.RUNTIME)\r
+ @Target(ElementType.TYPE)\r
+ public @interface IQContraintUnique {\r
+\r
+ /**\r
+ * Constraint name. If null or empty, iciql will generate one.\r
+ */\r
+ String name() default "";\r
+\r
+ /**\r
+ * Columns defined as 'unique'.\r
+ * <ul>\r
+ * <li>single column : uniqueColumns = "id"\r
+ * <li>multiple column : uniqueColumns = { "id", "name", "date" }\r
+ * </ul>\r
+ */\r
+ String[] uniqueColumns() default {};\r
+\r
+ }\r
+\r
+ /**\r
+ * Annotation to specify multiple unique constraints.\r
+ */\r
+ @Retention(RetentionPolicy.RUNTIME)\r
+ @Target(ElementType.TYPE)\r
+ public @interface IQContraintsUnique {\r
+ IQContraintUnique[] value() default {};\r
+ }\r
+ \r
/**\r
* Annotation to define a view.\r
*/\r
/*
* 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.
import java.sql.DatabaseMetaData;
+import com.iciql.TableDefinition.ConstraintForeignKeyDefinition;
+import com.iciql.TableDefinition.ConstraintUniqueDefinition;
import com.iciql.TableDefinition.IndexDefinition;
/**
* Get the CREATE VIEW statement.
*
* @param stat
+ * return the SQL statement
* @param def
+ * table definition
*/
<T> void prepareCreateView(SQLStatement stat, TableDefinition<T> def);
* Get the CREATE VIEW statement.
*
* @param stat
+ * return the SQL statement
* @param def
+ * table definition
* @param fromWhere
*/
<T> void prepareCreateView(SQLStatement stat, TableDefinition<T> def, String fromWhere);
* Get the DROP VIEW statement.
*
* @param stat
+ * return the SQL statement
* @param def
+ * table definition
*/
<T> void prepareDropView(SQLStatement stat, TableDefinition<T> def);
/**
* Get the CREATE INDEX statement.
*
+ * @param stat
+ * return the SQL statement
* @param schemaName
* the schema name
* @param tableName
* the table name
* @param index
* the index definition
- * @return the SQL statement
*/
void prepareCreateIndex(SQLStatement stat, String schemaName, String tableName, IndexDefinition index);
+ /**
+ * Get the ALTER statement.
+ *
+ * @param stat
+ * return the SQL statement
+ * @param schemaName
+ * the schema name
+ * @param tableName
+ * the table name
+ * @param constraint
+ * the constraint definition
+ */
+ void prepareCreateConstraintForeignKey(SQLStatement stat, String schemaName, String tableName, ConstraintForeignKeyDefinition constraint);
+
+ /**
+ * Get the ALTER statement.
+ *
+ * @param stat
+ * return the SQL statement
+ * @param schemaName
+ * the schema name
+ * @param tableName
+ * the table name
+ * @param constraint
+ * the constraint definition
+ * return the SQL statement
+ */
+ void prepareCreateConstraintUnique(SQLStatement stat, String schemaName, String tableName, ConstraintUniqueDefinition constraint);
+
/**
* Get a MERGE or REPLACE INTO statement.
*
+ * @param stat
+ * return the SQL statement
* @param schemaName
* the schema name
* @param tableName
* the table name
- * @param index
- * the index definition
+ * @param def
+ * the table definition
+ * @param obj
+ * values
*/
<T> void prepareMerge(SQLStatement stat, String schemaName, String tableName, TableDefinition<T> def,
Object obj);
/*\r
* Copyright 2004-2011 H2 Group.\r
* Copyright 2011 James Moger.\r
+ * Copyright 2012 Frédéric Gaillard.\r
*\r
* Licensed under the Apache License, Version 2.0 (the "License");\r
* you may not use this file except in compliance with the License.\r
import java.text.MessageFormat;\r
import java.text.SimpleDateFormat;\r
\r
+import com.iciql.Iciql.ConstraintDeleteType;\r
+import com.iciql.Iciql.ConstraintUpdateType;\r
+import com.iciql.TableDefinition.ConstraintForeignKeyDefinition;\r
+import com.iciql.TableDefinition.ConstraintUniqueDefinition;\r
import com.iciql.TableDefinition.FieldDefinition;\r
import com.iciql.TableDefinition.IndexDefinition;\r
import com.iciql.util.IciqlLogger;\r
buff.append("INDEX ");\r
buff.append(index.indexName);\r
buff.append(" ON ");\r
+ // FIXME maybe we can use schemaName ?\r
+ // buff.append(prepareTableName(schemaName, tableName));\r
buff.append(tableName);\r
buff.append("(");\r
for (String col : index.columnNames) {\r
}\r
return o.toString();\r
}\r
+\r
+ @SuppressWarnings("incomplete-switch")\r
+ @Override\r
+ public void prepareCreateConstraintForeignKey(SQLStatement stat, String schemaName, String tableName, ConstraintForeignKeyDefinition constraint) {\r
+ StatementBuilder buff = new StatementBuilder();\r
+ buff.append("ALTER TABLE ");\r
+ buff.append(prepareTableName(schemaName, tableName));\r
+ buff.append(" ADD CONSTRAINT ");\r
+ buff.append(constraint.constraintName);\r
+ buff.append(" FOREIGN KEY ");\r
+ buff.append(" (");\r
+ for (String col : constraint.foreignColumns) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(prepareColumnName(col));\r
+ }\r
+ buff.append(") ");\r
+ buff.append(" REFERENCES ");\r
+ buff.append(constraint.referenceTable);\r
+ buff.append(" (");\r
+ buff.resetCount();\r
+ for (String col : constraint.referenceColumns) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(prepareColumnName(col));\r
+ }\r
+ buff.append(") ");\r
+ if (constraint.deleteType != ConstraintDeleteType.UNSET) {\r
+ buff.append(" ON DELETE ");\r
+ switch (constraint.deleteType) {\r
+ case CASCADE:\r
+ buff.append("CASCADE ");\r
+ break;\r
+ case RESTRICT:\r
+ buff.append("RESTRICT ");\r
+ break;\r
+ case SET_NULL:\r
+ buff.append("SET NULL ");\r
+ break;\r
+ case NO_ACTION:\r
+ buff.append("NO ACTION ");\r
+ break;\r
+ case SET_DEFAULT:\r
+ buff.append("SET DEFAULT ");\r
+ break;\r
+ }\r
+ }\r
+ if (constraint.updateType != ConstraintUpdateType.UNSET) {\r
+ buff.append(" ON UPDATE ");\r
+ switch (constraint.updateType) {\r
+ case CASCADE:\r
+ buff.append("CASCADE ");\r
+ break;\r
+ case RESTRICT:\r
+ buff.append("RESTRICT ");\r
+ break;\r
+ case SET_NULL:\r
+ buff.append("SET NULL ");\r
+ break;\r
+ case NO_ACTION:\r
+ buff.append("NO ACTION ");\r
+ break;\r
+ case SET_DEFAULT:\r
+ buff.append("SET DEFAULT ");\r
+ break;\r
+ }\r
+ }\r
+ switch (constraint.deferrabilityType) {\r
+ case DEFERRABLE_INITIALLY_DEFERRED:\r
+ buff.append("DEFERRABLE INITIALLY DEFERRED ");\r
+ break;\r
+ case DEFERRABLE_INITIALLY_IMMEDIATE:\r
+ buff.append("DEFERRABLE INITIALLY IMMEDIATE ");\r
+ break;\r
+ case NOT_DEFERRABLE:\r
+ buff.append("NOT DEFERRABLE ");\r
+ break;\r
+ case UNSET:\r
+ break;\r
+ }\r
+ stat.setSQL(buff.toString().trim());\r
+ }\r
+\r
+ @Override\r
+ public void prepareCreateConstraintUnique(SQLStatement stat, String schemaName, String tableName, ConstraintUniqueDefinition constraint) {\r
+ StatementBuilder buff = new StatementBuilder();\r
+ buff.append("ALTER TABLE ");\r
+ buff.append(prepareTableName(schemaName, tableName));\r
+ buff.append(" ADD CONSTRAINT ");\r
+ buff.append(constraint.constraintName);\r
+ buff.append(" UNIQUE ");\r
+ buff.append(" (");\r
+ for (String col : constraint.uniqueColumns) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(prepareColumnName(col));\r
+ }\r
+ buff.append(") ");\r
+ stat.setSQL(buff.toString().trim());\r
+ }\r
+\r
}
\ No newline at end of file
/*\r
* Copyright 2004-2011 H2 Group.\r
* Copyright 2011 James Moger.\r
+ * Copyright 2012 Frédéric Gaillard.\r
*\r
* Licensed under the Apache License, Version 2.0 (the "License");\r
* you may not use this file except in compliance with the License.\r
import java.util.Map;\r
import java.util.Set;\r
\r
+import com.iciql.Iciql.ConstraintDeferrabilityType;\r
+import com.iciql.Iciql.ConstraintDeleteType;\r
+import com.iciql.Iciql.ConstraintUpdateType;\r
import com.iciql.Iciql.EnumId;\r
import com.iciql.Iciql.EnumType;\r
import com.iciql.Iciql.IQColumn;\r
import com.iciql.Iciql.IQConstraint;\r
+import com.iciql.Iciql.IQContraintUnique;\r
+import com.iciql.Iciql.IQContraintsUnique;\r
import com.iciql.Iciql.IQEnum;\r
+import com.iciql.Iciql.IQContraintForeignKey;\r
+import com.iciql.Iciql.IQContraintsForeignKey;\r
import com.iciql.Iciql.IQIgnore;\r
import com.iciql.Iciql.IQIndex;\r
import com.iciql.Iciql.IQIndexes;\r
public List<String> columnNames;\r
}\r
\r
+ /**\r
+ * The meta data of a constraint on foreign key.\r
+ */\r
+ \r
+ public static class ConstraintForeignKeyDefinition {\r
+\r
+ public String constraintName;\r
+ public List<String> foreignColumns;\r
+ public String referenceTable;\r
+ public List<String> referenceColumns;\r
+ public ConstraintDeleteType deleteType = ConstraintDeleteType.UNSET;\r
+ public ConstraintUpdateType updateType = ConstraintUpdateType.UNSET;\r
+ public ConstraintDeferrabilityType deferrabilityType = ConstraintDeferrabilityType.UNSET;\r
+ }\r
+ \r
+ /**\r
+ * The meta data of a unique constraint.\r
+ */\r
+ \r
+ public static class ConstraintUniqueDefinition {\r
+\r
+ public String constraintName;\r
+ public List<String> uniqueColumns;\r
+ }\r
+ \r
+ \r
/**\r
* The meta data of a field.\r
*/\r
private Class<T> clazz;\r
private IdentityHashMap<Object, FieldDefinition> fieldMap = Utils.newIdentityHashMap();\r
private ArrayList<IndexDefinition> indexes = Utils.newArrayList();\r
+ private ArrayList<ConstraintForeignKeyDefinition> constraintsForeignKey = Utils.newArrayList();\r
+ private ArrayList<ConstraintUniqueDefinition> constraintsUnique = Utils.newArrayList();\r
\r
TableDefinition(Class<T> clazz) {\r
this.clazz = clazz;\r
indexes.add(index);\r
}\r
\r
+ /**\r
+ * Defines an unique constraint with the specified model fields.\r
+ * \r
+ * @param name\r
+ * the constraint name (optional)\r
+ * @param modelFields\r
+ * the ordered list of model fields\r
+ */\r
+ void defineConstraintUnique(String name, Object[] modelFields) {\r
+ List<String> columnNames = mapColumnNames(modelFields);\r
+ addConstraintUnique(name, columnNames);\r
+ }\r
+\r
+ /**\r
+ * Defines an unique constraint.\r
+ * \r
+ * @param name\r
+ * @param columnNames\r
+ */\r
+ private void addConstraintUnique(String name, List<String> columnNames) {\r
+ ConstraintUniqueDefinition constraint = new ConstraintUniqueDefinition();\r
+ if (StringUtils.isNullOrEmpty(name)) {\r
+ constraint.constraintName = tableName + "_" + constraintsUnique.size();\r
+ } else {\r
+ constraint.constraintName = name;\r
+ }\r
+ constraint.uniqueColumns = Utils.newArrayList(columnNames);\r
+ constraintsUnique.add(constraint);\r
+ }\r
+\r
+ /**\r
+ * Defines a foreign key constraint with the specified model fields.\r
+ * \r
+ * @param name\r
+ * the constraint name (optional)\r
+ * @param modelFields\r
+ * the ordered list of model fields\r
+ */\r
+ void defineForeignKey(String name, Object[] modelFields, String refTableName, Object[] refModelFields,\r
+ ConstraintDeleteType deleteType, ConstraintUpdateType updateType,\r
+ ConstraintDeferrabilityType deferrabilityType) {\r
+ List<String> columnNames = mapColumnNames(modelFields);\r
+ List<String> referenceColumnNames = mapColumnNames(refModelFields);\r
+ addForeignKey(name, columnNames, refTableName, referenceColumnNames,\r
+ deleteType, updateType, deferrabilityType);\r
+ }\r
+\r
+ /**\r
+ * Defines a foreign key constraint.\r
+ * \r
+ * @param name\r
+ * @param columnNames\r
+ */\r
+ private void addForeignKey(String name, List<String> columnNames, String referenceTableName, \r
+ List<String> referenceColumnNames, ConstraintDeleteType deleteType, \r
+ ConstraintUpdateType updateType, ConstraintDeferrabilityType deferrabilityType) {\r
+ ConstraintForeignKeyDefinition constraint = new ConstraintForeignKeyDefinition();\r
+ if (StringUtils.isNullOrEmpty(name)) {\r
+ constraint.constraintName = tableName + "_" + constraintsUnique.size();\r
+ } else {\r
+ constraint.constraintName = name;\r
+ }\r
+ constraint.foreignColumns = Utils.newArrayList(columnNames);\r
+ constraint.referenceTable = referenceTableName;\r
+ constraint.referenceColumns = Utils.newArrayList(referenceColumnNames);\r
+ constraint.deleteType = deleteType;\r
+ constraint.updateType = updateType;\r
+ constraint.deferrabilityType = deferrabilityType;\r
+ constraintsForeignKey.add(constraint);\r
+ }\r
+ \r
void defineColumnName(Object column, String columnName) {\r
FieldDefinition def = fieldMap.get(column);\r
if (def != null) {\r
}\r
}\r
\r
+ // create unique constraints\r
+ for (ConstraintUniqueDefinition constraint : constraintsUnique) {\r
+ stat = new SQLStatement(db);\r
+ db.getDialect().prepareCreateConstraintUnique(stat, schemaName, tableName, constraint);\r
+ IciqlLogger.create(stat.getSQL());\r
+ try {\r
+ stat.executeUpdate();\r
+ } catch (IciqlException e) {\r
+ // maybe we should check more error codes\r
+ if (e.getIciqlCode() != IciqlException.CODE_OBJECT_ALREADY_EXISTS) {\r
+ throw e;\r
+ }\r
+ }\r
+ }\r
+ \r
+ // create foreign keys constraints\r
+ for (ConstraintForeignKeyDefinition constraint : constraintsForeignKey) {\r
+ stat = new SQLStatement(db);\r
+ db.getDialect().prepareCreateConstraintForeignKey(stat, schemaName, tableName, constraint);\r
+ IciqlLogger.create(stat.getSQL());\r
+ try {\r
+ stat.executeUpdate();\r
+ } catch (IciqlException e) {\r
+ // maybe we should check more error codes\r
+ if (e.getIciqlCode() != IciqlException.CODE_OBJECT_ALREADY_EXISTS) {\r
+ throw e;\r
+ }\r
+ }\r
+ }\r
+\r
// tables are created using IF NOT EXISTS\r
// but we may still need to upgrade\r
db.upgradeTable(this);\r
addIndex(index);\r
}\r
}\r
+ \r
+ if (clazz.isAnnotationPresent(IQContraintUnique.class)) {\r
+ // single table unique constraint\r
+ IQContraintUnique constraint = clazz.getAnnotation(IQContraintUnique.class);\r
+ addConstraintUnique(constraint);\r
+ }\r
+\r
+ if (clazz.isAnnotationPresent(IQContraintsUnique.class)) {\r
+ // multiple table unique constraints\r
+ IQContraintsUnique constraints = clazz.getAnnotation(IQContraintsUnique.class);\r
+ for (IQContraintUnique constraint : constraints.value()) {\r
+ addConstraintUnique(constraint);\r
+ }\r
+ }\r
+ \r
+ if (clazz.isAnnotationPresent(IQContraintForeignKey.class)) {\r
+ // single table constraint\r
+ IQContraintForeignKey constraint = clazz.getAnnotation(IQContraintForeignKey.class);\r
+ addConstraintForeignKey(constraint);\r
+ }\r
+\r
+ if (clazz.isAnnotationPresent(IQContraintsForeignKey.class)) {\r
+ // multiple table constraints\r
+ IQContraintsForeignKey constraints = clazz.getAnnotation(IQContraintsForeignKey.class);\r
+ for (IQContraintForeignKey constraint : constraints.value()) {\r
+ addConstraintForeignKey(constraint);\r
+ }\r
+ }\r
+ \r
+ }\r
+\r
+ private void addConstraintForeignKey(IQContraintForeignKey constraint) {\r
+ List<String> foreignColumns = Arrays.asList(constraint.foreignColumns());\r
+ List<String> referenceColumns = Arrays.asList(constraint.referenceColumns());\r
+ addContraintForeignKey(constraint.name(), foreignColumns, constraint.referenceName(), referenceColumns, constraint.deleteType(), constraint.updateType(), constraint.deferrabilityType());\r
+ }\r
+ \r
+ private void addConstraintUnique(IQContraintUnique constraint) {\r
+ List<String> uniqueColumns = Arrays.asList(constraint.uniqueColumns());\r
+ addContraintUnique(constraint.name(), uniqueColumns);\r
+ }\r
+ \r
+ /**\r
+ * Defines a foreign key constraint with the specified parameters.\r
+ * \r
+ * @param name\r
+ * name of the constraint\r
+ * @param foreignColumns\r
+ * list of columns declared as foreign\r
+ * @param referenceName\r
+ * reference table name\r
+ * @param referenceColumns\r
+ * list of columns used in reference table\r
+ * @param deleteType\r
+ * action on delete\r
+ * @param updateType\r
+ * action on update\r
+ * @param deferrabilityType\r
+ * deferrability mode\r
+ */\r
+ private void addContraintForeignKey(String name,\r
+ List<String> foreignColumns, String referenceName,\r
+ List<String> referenceColumns, ConstraintDeleteType deleteType,\r
+ ConstraintUpdateType updateType, ConstraintDeferrabilityType deferrabilityType) {\r
+ ConstraintForeignKeyDefinition constraint = new ConstraintForeignKeyDefinition();\r
+ if (StringUtils.isNullOrEmpty(name)) {\r
+ constraint.constraintName = tableName + "_" + constraintsForeignKey.size();\r
+ } else {\r
+ constraint.constraintName = name;\r
+ }\r
+ constraint.foreignColumns = Utils.newArrayList(foreignColumns);\r
+ constraint.referenceColumns = Utils.newArrayList(referenceColumns);\r
+ constraint.referenceTable = referenceName;\r
+ constraint.deleteType = deleteType;\r
+ constraint.updateType = updateType;\r
+ constraint.deferrabilityType = deferrabilityType;\r
+ constraintsForeignKey.add(constraint);\r
+ }\r
+\r
+ /**\r
+ * Defines a unique constraint with the specified parameters.\r
+ * \r
+ * @param name\r
+ * name of the constraint\r
+ * @param uniqueColumns\r
+ * list of columns declared as unique\r
+ */\r
+ private void addContraintUnique(String name, List<String> uniqueColumns) {\r
+ ConstraintUniqueDefinition constraint = new ConstraintUniqueDefinition();\r
+ if (StringUtils.isNullOrEmpty(name)) {\r
+ constraint.constraintName = tableName + "_" + constraintsUnique.size();\r
+ } else {\r
+ constraint.constraintName = name;\r
+ }\r
+ constraint.uniqueColumns = Utils.newArrayList(uniqueColumns);\r
+ constraintsUnique.add(constraint);\r
}\r
\r
private void addIndex(IQIndex index) {\r
return indexes;\r
}\r
\r
+ List<ConstraintUniqueDefinition> getContraintsUnique() {\r
+ return constraintsUnique;\r
+ }\r
+ \r
+ List<ConstraintForeignKeyDefinition> getContraintsForeignKey() {\r
+ return constraintsForeignKey;\r
+ }\r
+ \r
private void initObject(Object obj, Map<Object, FieldDefinition> map) {\r
for (FieldDefinition def : fields) {\r
Object newValue = def.initWithNewObject(obj);\r
/*
* 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.
import com.iciql.Iciql.IQSchema;
import com.iciql.Iciql.IQTable;
import com.iciql.Iciql.IndexType;
+import com.iciql.TableDefinition.ConstraintForeignKeyDefinition;
+import com.iciql.TableDefinition.ConstraintUniqueDefinition;
import com.iciql.TableDefinition.FieldDefinition;
import com.iciql.TableDefinition.IndexDefinition;
import com.iciql.util.StatementBuilder;
}
// TODO complete index validation.
// need to actually compare index types and columns within each index.
+
+ // TODO add constraints validation
+ List<ConstraintUniqueDefinition> defContraintsU = def.getContraintsUnique();
+ List<ConstraintForeignKeyDefinition> defContraintsFK = def.getContraintsForeignKey();
}
/**
--- /dev/null
+/*\r
+ * Copyright 2012 Frédéric Gaillard.\r
+ *\r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+package com.iciql.test;\r
+\r
+import static org.junit.Assert.assertEquals;\r
+\r
+import org.junit.After;\r
+import org.junit.Before;\r
+import org.junit.Test;\r
+\r
+import com.iciql.Db;\r
+import com.iciql.test.models.CategoryAnnotationOnly;\r
+import com.iciql.test.models.ProductAnnotationOnlyWithForeignKey;\r
+\r
+public class ForeignKeyTest {\r
+\r
+ /**\r
+ * This object represents a database (actually a connection to the\r
+ * database).\r
+ */\r
+\r
+ private Db db;\r
+\r
+ @Before\r
+ public void setUp() {\r
+ db = IciqlSuite.openNewDb();\r
+ db.insertAll(CategoryAnnotationOnly.getList());\r
+ db.insertAll(ProductAnnotationOnlyWithForeignKey.getList());\r
+ }\r
+\r
+ @After\r
+ public void tearDown() {\r
+ db.close();\r
+ }\r
+\r
+ @Test\r
+ public void testForeignKeyWithOnDeleteCascade() {\r
+ ProductAnnotationOnlyWithForeignKey p = new ProductAnnotationOnlyWithForeignKey();\r
+ long count1 = db.from(p).selectCount();\r
+ \r
+ // should remove 2 associated products\r
+ CategoryAnnotationOnly c = new CategoryAnnotationOnly();\r
+ db.from(c).where(c.categoryId).is(1L).delete();\r
+ \r
+ long count2 = db.from(p).selectCount();\r
+ \r
+ assertEquals(count1, count2 + 2L);\r
+ }\r
+\r
+\r
+}\r
/*\r
* Copyright 2011 James Moger.\r
+ * Copyright 2012 Frédéric Gaillard.\r
*\r
* Licensed under the Apache License, Version 2.0 (the "License");\r
* you may not use this file except in compliance with the License.\r
@SuiteClasses({ AliasMapTest.class, AnnotationsTest.class, BooleanModelTest.class, ClobTest.class,\r
ConcurrencyTest.class, EnumsTest.class, ModelsTest.class, PrimitivesTest.class,\r
RuntimeQueryTest.class, SamplesTest.class, UpdateTest.class, UpgradesTest.class, JoinTest.class,\r
- UUIDTest.class, ViewsTest.class })\r
+ UUIDTest.class, ViewsTest.class, ForeignKeyTest.class })\r
public class IciqlSuite {\r
\r
private static final TestDb[] TEST_DBS = {\r
--- /dev/null
+/*
+ * 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.test.models;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.iciql.Iciql.IQColumn;
+import com.iciql.Iciql.IQContraintUnique;
+import com.iciql.Iciql.IQTable;
+
+/**
+ * A table containing category data.
+ */
+
+@IQTable(name = "AnnotatedCatagory", primaryKey = "id")
+// @IQIndex(value = "categ", type=IndexType.UNIQUE)
+@IQContraintUnique(uniqueColumns = { "categ" })
+public class CategoryAnnotationOnly {
+
+ @IQColumn(name = "id", autoIncrement = true)
+ public Long categoryId;
+
+ @IQColumn(name = "categ", length = 15, trim = true)
+ public String category;
+
+ public CategoryAnnotationOnly() {
+ // public constructor
+ }
+
+ private CategoryAnnotationOnly(long categoryId, String category) {
+ this.categoryId = categoryId;
+ this.category = category;
+ }
+
+ private static CategoryAnnotationOnly create(int categoryId, String category) {
+ return new CategoryAnnotationOnly(categoryId, category);
+ }
+
+ public static List<CategoryAnnotationOnly> getList() {
+ CategoryAnnotationOnly[] list = {
+ create(1, "Beverages"),
+ create(2, "Condiments"),
+ create(3, "Produce"),
+ create(4, "Meat/Poultry"),
+ create(5,"Seafood")
+ };
+ return Arrays.asList(list);
+ }
+
+ public String toString() {
+ return category;
+ }
+
+}
--- /dev/null
+/*
+ * 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.test.models;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.iciql.Iciql.ConstraintDeleteType;
+import com.iciql.Iciql.IQColumn;
+import com.iciql.Iciql.IQContraintForeignKey;
+import com.iciql.Iciql.IQIndex;
+import com.iciql.Iciql.IQIndexes;
+import com.iciql.Iciql.IQTable;
+import com.iciql.Iciql.IndexType;
+
+/**
+ * A table containing product data.
+ */
+
+@IQTable(name = "AnnotatedProduct", primaryKey = "id")
+@IQIndexes({ @IQIndex({ "name", "cat" }), @IQIndex(name = "nameidx", type = IndexType.HASH, value = "name") })
+@IQContraintForeignKey(
+ foreignColumns= { "cat" },
+ referenceName = "AnnotatedCatagory",
+ referenceColumns = { "categ" },
+ deleteType = ConstraintDeleteType.CASCADE
+)
+public class ProductAnnotationOnlyWithForeignKey {
+
+ public String unmappedField;
+
+ @IQColumn(name = "id", autoIncrement = true)
+ public Long productId;
+
+ @IQColumn(name = "cat", length = 15, trim = true)
+ public String category;
+
+ @IQColumn(name = "name", length = 50)
+ public String productName;
+
+ @SuppressWarnings("unused")
+ @IQColumn
+ private Double unitPrice;
+
+ @IQColumn
+ private Integer unitsInStock;
+
+ public ProductAnnotationOnlyWithForeignKey() {
+ // public constructor
+ }
+
+ private ProductAnnotationOnlyWithForeignKey(long productId, String productName, String category, double unitPrice,
+ int unitsInStock, String unmappedField) {
+ this.productId = productId;
+ this.productName = productName;
+ this.category = category;
+ this.unitPrice = unitPrice;
+ this.unitsInStock = unitsInStock;
+ this.unmappedField = unmappedField;
+ }
+
+ private static ProductAnnotationOnlyWithForeignKey create(int productId, String productName, String category,
+ double unitPrice, int unitsInStock, String unmappedField) {
+ return new ProductAnnotationOnlyWithForeignKey(productId, productName, category, unitPrice, unitsInStock,
+ unmappedField);
+ }
+
+ public static List<ProductAnnotationOnlyWithForeignKey> getList() {
+ String unmappedField = "unmapped";
+ ProductAnnotationOnlyWithForeignKey[] list = { create(1, "Chai", "Beverages", 18, 39, unmappedField),
+ create(2, "Chang", "Beverages", 19.0, 17, unmappedField),
+ create(3, "Aniseed Syrup", "Condiments", 10.0, 13, unmappedField),
+ create(4, "Chef Anton's Cajun Seasoning", "Condiments", 22.0, 53, unmappedField),
+ create(5, "Chef Anton's Gumbo Mix", "Condiments", 21.3500, 0, unmappedField),
+ create(6, "Grandma's Boysenberry Spread", "Condiments", 25.0, 120, unmappedField),
+ create(7, "Uncle Bob's Organic Dried Pears", "Produce", 30.0, 15, unmappedField),
+ create(8, "Northwoods Cranberry Sauce", "Condiments", 40.0, 6, unmappedField),
+ create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29, unmappedField),
+ create(10, "Ikura", "Seafood", 31.0, 31, unmappedField), };
+ return Arrays.asList(list);
+ }
+
+ public String toString() {
+ return productName + ": " + unitsInStock;
+ }
+
+}