}});\r
%ENDCODE%\r
\r
+## View Statements\r
+\r
+%BEGINCODE%\r
+// the view named "ProductView" is created from the "Products" table\r
+@IQView(viewTableName = "Products")\r
+public class ProductView {\r
+\r
+ @IQColumn\r
+ @IQConstraint("this >= 200 AND this < 300")\r
+ Long id;\r
+ \r
+ @IQColumn\r
+ String name;\r
+}\r
+\r
+final ProductView v = new ProductView();\r
+List<ProductView> allProducts = db.from(v).select();\r
+\r
+// this version of the view model "ProductView" inherits table metadata\r
+// from the Products class which is annotated with IQTable\r
+@IQView(inheritColumns = true)\r
+public class ProductView extends Products {\r
+\r
+ // inherited BUT replaced to define the constraint\r
+ @IQColumn\r
+ @IQConstraint("this >= 200 AND this < 300")\r
+ Long id;\r
+ \r
+ // inherited from Products\r
+ //@IQColumn\r
+ //String name;\r
+}\r
+\r
+final ProductView v = new ProductView();\r
+List<ProductView> allProducts = db.from(v).select();\r
+\r
+// in this example we are creating a view based on a fluent query\r
+// and using 2 levels of inheritance. IQConstraints are ignored\r
+// when using this approach because we are fluently defining them.\r
+@IQView(inheritColumns = true)\r
+public class ProductViewInherited extends ProductView {\r
+\r
+}\r
+\r
+final Products p = new Products();\r
+db.from(p).where(p.id).atLeast(200L).and(p.id).lessThan(300L).createView(ProductViewInherited.class);\r
+\r
+// now replace the view with a variation\r
+db.from(p).where(p.id).atLeast(250L).and(p.id).lessThan(350L).replaceView(ProductViewInherited.class);\r
+\r
+// now drop the view from the database\r
+db.dropView(ProductViewInherited.class);\r
+\r
+%ENDCODE%\r
+\r
## Dynamic Queries\r
\r
Dynamic queries skip all field type checking and, depending on which approach you use, may skip model class/table name checking too.\r
\r
**%VERSION%** ([zip](http://code.google.com/p/iciql/downloads/detail?name=%ZIP%)|[jar](http://code.google.com/p/iciql/downloads/detail?name=%JAR%)) *released %BUILDDATE%*\r
\r
+- Implemented readonly view support. (issue 8)<br/>\r
+View models may be specified using the IQView annotation or Iciql.define(). Views can either be created automatically as part of a query of the view OR views may be constructed from a fluent statement.\r
+- Support inheriting columns from super.super class, if super.super is annotated.<br/>This allows for an inheritance hierarchy like:<br/>\r
+@IQTable class MyTable -> @IQView abstract class MyBaseView -> @IQView class MyConstrainedView\r
- Fixed order of DEFAULT value in create table statement (issue 11)\r
- Support inheritance of IQVersion for DbUpgrader implementations (issue 10)\r
- Fixed password bug in model generator (issue 7)\r
\r
+### Older Releases\r
+\r
**1.1.0** *released 2012-08-20*\r
\r
- All bulk operations (insert all, update all, delete all) now use JDBC savepoints to ensure atomicity of the transaction\r
Iciql maps resultset columns by the index of the model class field from a list. This assumes that *all* columns in the resultset have a corresponding model field definition. This works fine for most queries because iciql explicitly selects columns from the table (*select alpha, beta...*) when you execute *select()*. The problem is when iciql issues a dynamic wildcard query and your model does not represent all columns in the resultset: columns and fields may fail to correctly line-up.<p/>\r
Iciql now maps all fields by their column name, not by their position.\r
\r
-### Older Releases\r
-\r
**0.7.3** *released 2011-12-06*\r
\r
- api change release (API v8)\r
<tr><td>column mappings</td><td>wildcard queries index result sets by column name</td><td>all result sets built by field index<br/>this can fail for wildcard queries</td></tr>\r
<tr><td>savepoints</td><td>bulk operations (insert, update, delete) use savepoints with rollback in the event of failure</td><td>--</td></tr>\r
<tr><th colspan="3">syntax and api</th></tr>\r
+<tr><td>VIEWs</td><td>create readonly views either from a class definition or from a fluent statement</td><td>--</td></tr>\r
<tr><td>dynamic queries</td><td>methods and where clauses for dynamic queries that build iciql objects</td><td>--</td></tr>\r
-<tr><td>DROP</td><td>syntax to drop a table</td><td></td></tr>\r
+<tr><td>DROP</td><td>syntax to drop a table or view</td><td></td></tr>\r
<tr><td>BETWEEN</td><td>syntax for specifying a BETWEEN x AND y clause</td><td>--</td></tr>\r
<tr><th colspan="3">types</th></tr>\r
<tr><td>primitives</td><td>fully supported</td><td>--</td></tr>\r
import com.iciql.DbUpgrader.DefaultDbUpgrader;\r
import com.iciql.Iciql.IQTable;\r
import com.iciql.Iciql.IQVersion;\r
+import com.iciql.Iciql.IQView;\r
import com.iciql.util.IciqlLogger;\r
import com.iciql.util.JdbcUtils;\r
import com.iciql.util.StringUtils;\r
return rc;\r
}\r
\r
+ @SuppressWarnings("unchecked")\r
+ public <T> int dropView(Class<? extends T> modelClass) {\r
+ TableDefinition<T> def = (TableDefinition<T>) define(modelClass);\r
+ SQLStatement stat = new SQLStatement(this);\r
+ getDialect().prepareDropView(stat, def);\r
+ IciqlLogger.drop(stat.getSQL());\r
+ int rc = 0;\r
+ try {\r
+ rc = stat.executeUpdate();\r
+ } catch (IciqlException e) {\r
+ if (e.getIciqlCode() != IciqlException.CODE_OBJECT_NOT_FOUND) {\r
+ throw e;\r
+ }\r
+ }\r
+ // remove this model class from the table definition cache\r
+ classMap.remove(modelClass);\r
+ // remove this model class from the upgrade checked cache\r
+ upgradeChecked.remove(modelClass);\r
+ return rc;\r
+ }\r
+\r
public <T> List<T> buildObjects(Class<? extends T> modelClass, ResultSet rs) {\r
return buildObjects(modelClass, false, rs);\r
}\r
// initializer\r
T t = instance(clazz);\r
def.mapObject(t);\r
+ } else if (clazz.isAnnotationPresent(IQView.class)) {\r
+ // annotated classes skip the Define().define() static\r
+ // initializer\r
+ T t = instance(clazz);\r
+ def.mapObject(t);\r
}\r
}\r
return def;\r
currentTableDefinition.defineTableName(tableName);\r
}\r
\r
+ public static void viewTableName(String viewTableName) {\r
+ checkInDefine();\r
+ currentTableDefinition.defineViewTableName(viewTableName);\r
+ }\r
+ \r
public static void memoryTable() {\r
checkInDefine();\r
currentTableDefinition.defineMemoryTable();\r
checkInDefine();\r
currentTableDefinition.defineDefaultValue(column, defaultValue);\r
}\r
- \r
+\r
+ public static void constraint(Object column, String constraint) {\r
+ checkInDefine();\r
+ currentTableDefinition.defineConstraint(column, constraint);\r
+ }\r
+\r
static synchronized <T> void define(TableDefinition<T> tableDefinition, Iciql table) {\r
currentTableDefinition = tableDefinition;\r
currentTable = table;\r
String[] value() default {};\r
}\r
\r
+ /**\r
+ * Annotation to define a view.\r
+ */\r
+ @Retention(RetentionPolicy.RUNTIME)\r
+ @Target(ElementType.TYPE)\r
+ public @interface IQView {\r
+\r
+ /**\r
+ * The view name. If not specified the class name is used as the view\r
+ * name.\r
+ * <p>\r
+ * The view name may still be overridden in the define() method if the\r
+ * model class is not annotated with IQView. Default: unspecified.\r
+ */\r
+ String name() default "";\r
+ \r
+ /**\r
+ * The source table for the view.\r
+ * <p>\r
+ * The view name may still be overridden in the define() method if the\r
+ * model class is not annotated with IQView. Default: unspecified.\r
+ */\r
+ String tableName() default "";\r
+\r
+ /**\r
+ * The inherit columns allows this model class to inherit columns from\r
+ * its super class. Any IQTable annotation present on the super class is\r
+ * ignored. Default: false.\r
+ */\r
+ boolean inheritColumns() default false;\r
+\r
+ /**\r
+ * Whether or not iciql tries to create the view. Default:\r
+ * true.\r
+ */\r
+ boolean create() default true;\r
+\r
+ /**\r
+ * If true, only fields that are explicitly annotated as IQColumn are\r
+ * mapped. Default: true.\r
+ */\r
+ boolean annotationsOnly() default true;\r
+ }\r
+ \r
+ /**\r
+ * String snippet defining SQL constraints for a field. Use "this" as\r
+ * a placeholder for the column name. "this" will be substituted at\r
+ * runtime.\r
+ * <p>\r
+ * IQConstraint("this > 2 AND this <= 7")\r
+ * <p>\r
+ * This snippet may still be overridden in the define() method if the\r
+ * model class is not annotated with IQTable or IQView. Default: unspecified.\r
+ */\r
+ @Retention(RetentionPolicy.RUNTIME)\r
+ @Target(ElementType.FIELD)\r
+ public @interface IQConstraint {\r
+\r
+ String value() default "";\r
+ } \r
+ \r
/**\r
* Annotation to specify multiple indexes.\r
*/\r
} else if ("42P01".equals(state)) {\r
// PostgreSQL table not found\r
iciqlCode = CODE_OBJECT_NOT_FOUND;\r
+ } else if ("X0X05".equals(state)) {\r
+ // Derby view/table not found exists\r
+ iciqlCode = CODE_OBJECT_NOT_FOUND;\r
} else if ("X0Y32".equals(state)) {\r
// Derby table already exists\r
iciqlCode = CODE_OBJECT_ALREADY_EXISTS;\r
List<X> list = (List<X>) select(x);\r
return list.isEmpty() ? null : list.get(0);\r
}\r
+ \r
+ public <X> void createView(Class<X> viewClass) {\r
+ TableDefinition<X> viewDef = db.define(viewClass);\r
+ \r
+ SQLStatement fromWhere = new SQLStatement(db);\r
+ appendFromWhere(fromWhere, false);\r
+ \r
+ SQLStatement stat = new SQLStatement(db);\r
+ db.getDialect().prepareCreateView(stat, viewDef, fromWhere.toSQL());\r
+ IciqlLogger.create(stat.toSQL());\r
+ stat.execute();\r
+ }\r
+\r
+ public <X> void replaceView(Class<X> viewClass) {\r
+ db.dropView(viewClass);\r
+ createView(viewClass);\r
+ }\r
\r
public String getSQL() {\r
SQLStatement stat = getSelectStatement(false);\r
}\r
}\r
}\r
-\r
+ \r
void appendFromWhere(SQLStatement stat) {\r
+ appendFromWhere(stat, true);\r
+ }\r
+ \r
+ void appendFromWhere(SQLStatement stat, boolean log) {\r
stat.appendSQL(" FROM ");\r
from.appendSQL(stat);\r
for (SelectTable<T> join : joins) {\r
}\r
}\r
db.getDialect().appendLimitOffset(stat, limit, offset);\r
- IciqlLogger.select(stat.getSQL());\r
+ if (log) {\r
+ IciqlLogger.select(stat.getSQL());\r
+ }\r
}\r
\r
/**\r
public List<T> selectDistinct() {\r
return query.selectDistinct();\r
}\r
+ \r
+ public void createView(Class<?> viewClass) {\r
+ query.createView(viewClass);\r
+ }\r
+\r
+ public void replaceView(Class<?> viewClass) {\r
+ query.replaceView(viewClass);\r
+ }\r
\r
/**\r
* Order by primitive boolean field\r
*/
<T> void prepareDropTable(SQLStatement stat, TableDefinition<T> def);
+
+ /**
+ * Get the CREATE VIEW statement.
+ *
+ * @param stat
+ * @param def
+ */
+ <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> def);
+
+ /**
+ * Get the CREATE VIEW statement.
+ *
+ * @param stat
+ * @param def
+ * @param fromWhere
+ */
+ <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> def, String fromWhere);
+
+ /**
+ * Get the DROP VIEW statement.
+ *
+ * @param stat
+ * @param def
+ */
+ <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def);
+
/**
* Get the CREATE INDEX statement.
*
stat.setSQL(buff.toString());\r
}\r
\r
+ @Override\r
+ public <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def) {\r
+ StatementBuilder buff = new StatementBuilder("DROP VIEW "\r
+ + prepareTableName(def.schemaName, def.tableName));\r
+ stat.setSQL(buff.toString());\r
+ return;\r
+ }\r
+\r
+ protected <T> String prepareCreateView(TableDefinition<T> def) {\r
+ return "CREATE VIEW";\r
+ }\r
+\r
+ @Override\r
+ public <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> def) {\r
+ StatementBuilder buff = new StatementBuilder();\r
+ buff.append(" FROM ");\r
+ buff.append(prepareTableName(def.schemaName, def.viewTableName));\r
+\r
+ StatementBuilder where = new StatementBuilder();\r
+ for (FieldDefinition field : def.fields) {\r
+ if (!StringUtils.isNullOrEmpty(field.constraint)) {\r
+ where.appendExceptFirst(", ");\r
+ String col = prepareColumnName(field.columnName);\r
+ String constraint = field.constraint.replace("{0}", col).replace("this", col);\r
+ where.append(constraint);\r
+ }\r
+ }\r
+ if (where.length() > 0) {\r
+ buff.append(" WHERE ");\r
+ buff.append(where.toString());\r
+ }\r
+ \r
+ prepareCreateView(stat, def, buff.toString());\r
+ }\r
+ \r
+ @Override\r
+ public <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> def, String fromWhere) {\r
+ StatementBuilder buff = new StatementBuilder();\r
+ buff.append(prepareCreateView(def));\r
+ buff.append(" ");\r
+ buff.append(prepareTableName(def.schemaName, def.tableName));\r
+\r
+ buff.append(" AS SELECT ");\r
+ for (FieldDefinition field : def.fields) {\r
+ buff.appendExceptFirst(", ");\r
+ buff.append(prepareColumnName(field.columnName));\r
+ }\r
+ buff.append(fromWhere);\r
+ stat.setSQL(buff.toString());\r
+ }\r
+ \r
protected boolean isIntegerType(String dataType) {\r
if ("INT".equals(dataType)) {\r
return true;\r
return "CREATE CACHED TABLE IF NOT EXISTS";\r
}\r
}\r
+ \r
+ @Override\r
+ protected <T> String prepareCreateView(TableDefinition<T> def) {\r
+ return "CREATE VIEW IF NOT EXISTS";\r
+ }\r
+\r
+ @Override\r
+ public <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def) {\r
+ StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS "\r
+ + prepareTableName(def.schemaName, def.tableName));\r
+ stat.setSQL(buff.toString());\r
+ return;\r
+ }\r
\r
@Override\r
protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType,\r
return "CREATE CACHED TABLE IF NOT EXISTS";\r
}\r
}\r
+ \r
+ @Override\r
+ public <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def) {\r
+ StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS "\r
+ + prepareTableName(def.schemaName, def.tableName));\r
+ stat.setSQL(buff.toString());\r
+ return;\r
+ }\r
\r
@Override\r
protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType,\r
protected <T> String prepareCreateTable(TableDefinition<T> def) {\r
return "CREATE TABLE IF NOT EXISTS";\r
}\r
+ \r
+ @Override\r
+ public <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def) {\r
+ StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS "\r
+ + prepareTableName(def.schemaName, def.tableName));\r
+ stat.setSQL(buff.toString());\r
+ return;\r
+ }\r
+ \r
@Override\r
public String prepareColumnName(String name) {\r
return "`" + name + "`";\r
params.add(o);\r
return this;\r
}\r
+ \r
+ void execute() {\r
+ PreparedStatement ps = null;\r
+ try {\r
+ ps = prepare(false);\r
+ ps.execute();\r
+ } catch (SQLException e) {\r
+ throw IciqlException.fromSQL(getSQL(), e);\r
+ } finally {\r
+ JdbcUtils.closeSilently(ps);\r
+ }\r
+ }\r
\r
ResultSet executeQuery() {\r
try {\r
import java.util.ArrayList;\r
import java.util.Arrays;\r
import java.util.IdentityHashMap;\r
+import java.util.LinkedHashSet;\r
import java.util.List;\r
import java.util.Map;\r
+import java.util.Set;\r
\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.IQEnum;\r
import com.iciql.Iciql.IQIgnore;\r
import com.iciql.Iciql.IQIndex;\r
import com.iciql.Iciql.IQSchema;\r
import com.iciql.Iciql.IQTable;\r
import com.iciql.Iciql.IQVersion;\r
+import com.iciql.Iciql.IQView;\r
import com.iciql.Iciql.IndexType;\r
import com.iciql.util.IciqlLogger;\r
import com.iciql.util.StatementBuilder;\r
String defaultValue;\r
EnumType enumType;\r
boolean isPrimitive;\r
+ String constraint;\r
\r
Object getValue(Object obj) {\r
try {\r
public ArrayList<FieldDefinition> fields = Utils.newArrayList();\r
String schemaName;\r
String tableName;\r
+ String viewTableName;\r
int tableVersion;\r
List<String> primaryKeyColumnNames;\r
boolean memoryTable;\r
this.tableName = tableName;\r
}\r
\r
+ void defineViewTableName(String viewTableName) {\r
+ this.viewTableName = viewTableName;\r
+ }\r
+\r
void defineMemoryTable() {\r
this.memoryTable = true;\r
}\r
}\r
}\r
\r
+ void defineConstraint(Object column, String constraint) {\r
+ FieldDefinition def = fieldMap.get(column);\r
+ if (def != null) {\r
+ def.constraint = constraint;\r
+ }\r
+ }\r
+\r
void mapFields() {\r
boolean byAnnotationsOnly = false;\r
boolean inheritColumns = false;\r
inheritColumns = tableAnnotation.inheritColumns();\r
}\r
\r
+ if (clazz.isAnnotationPresent(IQView.class)) {\r
+ IQView viewAnnotation = clazz.getAnnotation(IQView.class);\r
+ byAnnotationsOnly = viewAnnotation.annotationsOnly();\r
+ inheritColumns = viewAnnotation.inheritColumns();\r
+ }\r
+\r
List<Field> classFields = Utils.newArrayList();\r
classFields.addAll(Arrays.asList(clazz.getDeclaredFields()));\r
if (inheritColumns) {\r
Class<?> superClass = clazz.getSuperclass();\r
classFields.addAll(Arrays.asList(superClass.getDeclaredFields()));\r
+ \r
+ if (superClass.isAnnotationPresent(IQView.class)) {\r
+ IQView superView = superClass.getAnnotation(IQView.class);\r
+ if (superView.inheritColumns()) {\r
+ // inherit columns from super.super.class\r
+ Class<?> superSuperClass = superClass.getSuperclass();\r
+ classFields.addAll(Arrays.asList(superSuperClass.getDeclaredFields()));\r
+ }\r
+ } else if (superClass.isAnnotationPresent(IQTable.class)) {\r
+ IQTable superTable = superClass.getAnnotation(IQTable.class);\r
+ if (superTable.inheritColumns()) {\r
+ // inherit columns from super.super.class\r
+ Class<?> superSuperClass = superClass.getSuperclass();\r
+ classFields.addAll(Arrays.asList(superSuperClass.getDeclaredFields()));\r
+ }\r
+ }\r
}\r
\r
Set<FieldDefinition> uniqueFields = new LinkedHashSet<FieldDefinition>();\r
boolean nullable = !f.getType().isPrimitive();\r
EnumType enumType = null;\r
String defaultValue = "";\r
+ String constraint = "";\r
// configure Java -> SQL enum mapping\r
if (f.getType().isEnum()) {\r
enumType = EnumType.DEFAULT_TYPE;\r
defaultValue = col.defaultValue();\r
}\r
}\r
+ \r
+ boolean hasConstraint = f.isAnnotationPresent(IQConstraint.class);\r
+ if (hasConstraint) {\r
+ IQConstraint con = f.getAnnotation(IQConstraint.class);\r
+ // annotation overrides\r
+ if (!StringUtils.isNullOrEmpty(con.value())) {\r
+ constraint = con.value();\r
+ }\r
+ }\r
\r
boolean reflectiveMatch = !byAnnotationsOnly;\r
- if (reflectiveMatch || hasAnnotation) {\r
+ if (reflectiveMatch || hasAnnotation || hasConstraint) {\r
FieldDefinition fieldDef = new FieldDefinition();\r
fieldDef.isPrimitive = f.getType().isPrimitive();\r
fieldDef.field = f;\r
fieldDef.defaultValue = defaultValue;\r
fieldDef.enumType = enumType;\r
fieldDef.dataType = ModelUtils.getDataType(fieldDef);\r
+ fieldDef.constraint = constraint;\r
uniqueFields.add(fieldDef);\r
}\r
}\r
}\r
\r
long insert(Db db, Object obj, boolean returnKey) {\r
+ if (!StringUtils.isNullOrEmpty(viewTableName)) {\r
+ throw new IciqlException("Iciql does not support inserting rows into views!");\r
+ }\r
SQLStatement stat = new SQLStatement(db);\r
StatementBuilder buff = new StatementBuilder("INSERT INTO ");\r
buff.append(db.getDialect().prepareTableName(schemaName, tableName)).append('(');\r
}\r
\r
int update(Db db, Object obj) {\r
+ if (!StringUtils.isNullOrEmpty(viewTableName)) {\r
+ throw new IciqlException("Iciql does not support updating rows in views!");\r
+ }\r
if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {\r
throw new IllegalStateException("No primary key columns defined for table " + obj.getClass()\r
+ " - no update possible");\r
}\r
\r
int delete(Db db, Object obj) {\r
+ if (!StringUtils.isNullOrEmpty(viewTableName)) {\r
+ throw new IciqlException("Iciql does not support deleting rows from views!");\r
+ }\r
if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {\r
throw new IllegalStateException("No primary key columns defined for table " + obj.getClass()\r
+ " - no update possible");\r
return this;\r
}\r
SQLStatement stat = new SQLStatement(db);\r
- db.getDialect().prepareCreateTable(stat, this);\r
+ if (StringUtils.isNullOrEmpty(viewTableName)) {\r
+ db.getDialect().prepareCreateTable(stat, this);\r
+ } else {\r
+ db.getDialect().prepareCreateView(stat, this);\r
+ }\r
IciqlLogger.create(stat.getSQL());\r
try {\r
stat.executeUpdate();\r
}\r
}\r
\r
+ if (clazz.isAnnotationPresent(IQView.class)) {\r
+ IQView viewAnnotation = clazz.getAnnotation(IQView.class);\r
+\r
+ // setup view name mapping, if properly annotated\r
+ // set this as the table name so it fits in seemlessly with iciql\r
+ if (!StringUtils.isNullOrEmpty(viewAnnotation.name())) {\r
+ tableName = viewAnnotation.name();\r
+ } else {\r
+ tableName = clazz.getSimpleName();\r
+ }\r
+\r
+ // setup source table name mapping, if properly annotated\r
+ if (!StringUtils.isNullOrEmpty(viewAnnotation.tableName())) {\r
+ viewTableName = viewAnnotation.tableName();\r
+ } else {\r
+ // check for IQTable annotation on super class\r
+ Class<?> superClass = clazz.getSuperclass();\r
+ if (superClass.isAnnotationPresent(IQTable.class)) {\r
+ IQTable table = superClass.getAnnotation(IQTable.class);\r
+ if (StringUtils.isNullOrEmpty(table.name())) {\r
+ // super.SimpleClassName\r
+ viewTableName = superClass.getSimpleName();\r
+ } else {\r
+ // super.IQTable.name()\r
+ viewTableName = table.name();\r
+ }\r
+ } else if (superClass.isAnnotationPresent(IQView.class)) {\r
+ // super class is a view\r
+ IQView parentView = superClass.getAnnotation(IQView.class);\r
+ if (StringUtils.isNullOrEmpty(parentView.tableName())) {\r
+ // parent view does not define a tableName, must be inherited\r
+ Class<?> superParent = superClass.getSuperclass();\r
+ if (superParent != null && superParent.isAnnotationPresent(IQTable.class)) {\r
+ IQTable superParentTable = superParent.getAnnotation(IQTable.class);\r
+ if (StringUtils.isNullOrEmpty(superParentTable.name())) {\r
+ // super.super.SimpleClassName\r
+ viewTableName = superParent.getSimpleName();\r
+ } else {\r
+ // super.super.IQTable.name()\r
+ viewTableName = superParentTable.name();\r
+ }\r
+ }\r
+ } else {\r
+ // super.IQView.tableName()\r
+ viewTableName = parentView.tableName();\r
+ }\r
+ }\r
+ \r
+ if (StringUtils.isNullOrEmpty(viewTableName)) {\r
+ // still missing view table name\r
+ throw new IciqlException("View model class \"{0}\" is missing a table name!", tableName);\r
+ }\r
+ }\r
+ \r
+ // allow control over createTableIfRequired()\r
+ createIfRequired = viewAnnotation.create();\r
+ }\r
+ \r
if (clazz.isAnnotationPresent(IQIndex.class)) {\r
// single table index\r
IQIndex index = clazz.getAnnotation(IQIndex.class);\r
import com.iciql.test.models.ProductAnnotationOnly;\r
import com.iciql.test.models.ProductInheritedAnnotation;\r
import com.iciql.test.models.ProductMixedAnnotation;\r
+import com.iciql.test.models.ProductView;\r
+import com.iciql.test.models.ProductViewFromQuery;\r
+import com.iciql.test.models.ProductViewInherited;\r
+import com.iciql.test.models.ProductViewInheritedComplex;\r
import com.iciql.test.models.SupportedTypes;\r
import com.iciql.util.IciqlLogger;\r
import com.iciql.util.IciqlLogger.IciqlListener;\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 })\r
+ UUIDTest.class, ViewsTest.class })\r
public class IciqlSuite {\r
\r
private static final TestDb[] TEST_DBS = {\r
}\r
db = Db.open(dataSource);\r
\r
+ // drop views\r
+ db.dropView(ProductView.class);\r
+ db.dropView(ProductViewInherited.class);\r
+ db.dropView(ProductViewFromQuery.class);\r
+ db.dropView(ProductViewInheritedComplex.class);\r
+\r
// drop tables\r
db.dropTable(BooleanModel.class);\r
db.dropTable(ComplexObject.class);\r
--- /dev/null
+/*
+ * Copyright 2012 James Moger.
+ *
+ * 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.iciql.Db;
+import com.iciql.test.models.ProductAnnotationOnly;
+import com.iciql.test.models.ProductView;
+import com.iciql.test.models.ProductViewFromQuery;
+import com.iciql.test.models.ProductViewInherited;
+import com.iciql.test.models.ProductViewInheritedComplex;
+import com.mysql.jdbc.StringUtils;
+
+/**
+ * Test annotation processing.
+ */
+public class ViewsTest {
+
+ /**
+ * This object represents a database (actually a connection to the
+ * database).
+ */
+
+ private Db db;
+
+ @Before
+ public void setUp() {
+ db = IciqlSuite.openNewDb();
+ db.insertAll(ProductAnnotationOnly.getList());
+ }
+
+ @After
+ public void tearDown() {
+ db.close();
+ }
+
+ @Test
+ public void testProductView() {
+ ProductView view = new ProductView();
+ List<ProductView> products = db.from(view).select();
+ assertEquals(5, products.size());
+ for (int i = 0; i < products.size(); i++) {
+ assertEquals(3 + i, products.get(i).productId.intValue());
+ }
+ }
+
+ @Test
+ public void testProductViewInherited() {
+ ProductViewInherited view = new ProductViewInherited();
+ List<ProductViewInherited> products = db.from(view).select();
+ assertEquals(5, products.size());
+ for (int i = 0; i < products.size(); i++) {
+ assertEquals(3 + i, products.get(i).productId.intValue());
+ }
+ }
+
+ @Test
+ public void testComplexInheritance() {
+ ProductViewInheritedComplex view = new ProductViewInheritedComplex();
+ List<ProductViewInheritedComplex> products = db.from(view).select();
+ assertEquals(5, products.size());
+ for (int i = 0; i < products.size(); i++) {
+ assertEquals(3 + i, products.get(i).productId.intValue());
+ assertTrue(!StringUtils.isNullOrEmpty(products.get(i).productName));
+ }
+ }
+
+ @Test
+ public void testCreateViewFromQuery() {
+ // create view from query
+ ProductAnnotationOnly product = new ProductAnnotationOnly();
+ db.from(product).where(product.productId).exceeds(2L).and(product.productId).atMost(7L).createView(ProductViewFromQuery.class);
+
+ // select from the created view
+ ProductViewFromQuery view = new ProductViewFromQuery();
+ List<ProductViewFromQuery> products = db.from(view).select();
+ assertEquals(5, products.size());
+ for (int i = 0; i < products.size(); i++) {
+ assertEquals(3 + i, products.get(i).productId.intValue());
+ }
+
+ // replace the view
+ db.from(product).where(product.productId).exceeds(3L).and(product.productId).atMost(8L).replaceView(ProductViewFromQuery.class);
+
+ // select from the replaced view
+ products = db.from(view).select();
+ assertEquals(5, products.size());
+ for (int i = 0; i < products.size(); i++) {
+ assertEquals(4 + i, products.get(i).productId.intValue());
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright 2012 James Moger.
+ *
+ * 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 com.iciql.Iciql.IQColumn;
+import com.iciql.Iciql.IQConstraint;
+import com.iciql.Iciql.IQView;
+
+/**
+ * A view containing product data.
+ */
+
+@IQView(name = "AnnotatedProductView", tableName = "AnnotatedProduct")
+public class ProductView {
+
+ public String unmappedField;
+
+ @IQColumn(name = "id", autoIncrement = true)
+ @IQConstraint("this <= 7 AND this > 2")
+ public Long productId;
+
+ @IQColumn(name = "name")
+ public String productName;
+
+ public ProductView() {
+ // public constructor
+ }
+
+ public String toString() {
+ return productName + " (" + productId + ")";
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2012 James Moger.
+ *
+ * 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 com.iciql.Iciql.IQColumn;
+import com.iciql.Iciql.IQView;
+
+/**
+ * A view containing product data.
+ */
+
+@IQView(name = "AnnotatedProductViewInherited", inheritColumns = true)
+public class ProductViewFromQuery extends ProductAnnotationOnly {
+
+ public String unmappedField;
+
+ @IQColumn(name = "id")
+ public Long productId;
+
+ public ProductViewFromQuery() {
+ // public constructor
+ }
+
+ public String toString() {
+ return productName + " (" + productId + ")";
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2012 James Moger.
+ *
+ * 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 com.iciql.Iciql.IQColumn;
+import com.iciql.Iciql.IQConstraint;
+import com.iciql.Iciql.IQView;
+
+/**
+ * A view containing product data.
+ */
+
+@IQView(name = "AnnotatedProductViewInherited", inheritColumns = true)
+public class ProductViewInherited extends ProductAnnotationOnly {
+
+ public String unmappedField;
+
+ @IQColumn(name = "id", autoIncrement = true)
+ @IQConstraint("this <= 7 AND this > 2")
+ public Long productId;
+
+ public ProductViewInherited() {
+ // public constructor
+ }
+
+ public String toString() {
+ return productName + " (" + productId + ")";
+ }
+
+}
--- /dev/null
+/*
+ * Copyright 2012 James Moger.
+ *
+ * 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 com.iciql.Iciql.IQView;
+
+/**
+ * A view containing product data.
+ */
+
+@IQView(inheritColumns = true)
+public class ProductViewInheritedComplex extends ProductViewInherited {
+
+}