From bb87e621eefd97872aa1a619d38c166b2f07db84 Mon Sep 17 00:00:00 2001 From: James Moger Date: Tue, 5 Apr 2016 12:27:55 -0400 Subject: [PATCH] Reformat project with default IntelliJ settings --- pom.xml | 3 +- src/main/java/com/iciql/CompareType.java | 30 +- src/main/java/com/iciql/Condition.java | 85 +- src/main/java/com/iciql/ConditionAndOr.java | 16 +- .../java/com/iciql/ConditionOpenClose.java | 16 +- src/main/java/com/iciql/Constants.java | 68 +- src/main/java/com/iciql/Dao.java | 261 +- .../iciql/DaoClasspathStatementProvider.java | 131 +- src/main/java/com/iciql/DaoProxy.java | 1251 ++++---- .../java/com/iciql/DaoStatementProvider.java | 17 +- src/main/java/com/iciql/Db.java | 1670 +++++----- src/main/java/com/iciql/DbInspector.java | 327 +- src/main/java/com/iciql/DbUpgrader.java | 82 +- src/main/java/com/iciql/DbVersion.java | 51 +- src/main/java/com/iciql/Define.java | 210 +- src/main/java/com/iciql/Filter.java | 2 +- src/main/java/com/iciql/Function.java | 242 +- src/main/java/com/iciql/Iciql.java | 1228 ++++---- src/main/java/com/iciql/IciqlException.java | 332 +- src/main/java/com/iciql/ModelUtils.java | 927 +++--- src/main/java/com/iciql/NestedConditions.java | 164 +- src/main/java/com/iciql/OrderExpression.java | 53 +- src/main/java/com/iciql/Query.java | 2104 ++++++------- src/main/java/com/iciql/QueryBetween.java | 62 +- src/main/java/com/iciql/QueryCondition.java | 238 +- src/main/java/com/iciql/QueryJoin.java | 78 +- .../java/com/iciql/QueryJoinCondition.java | 93 +- src/main/java/com/iciql/QueryWhere.java | 1188 ++++--- src/main/java/com/iciql/RuntimeParameter.java | 41 +- src/main/java/com/iciql/RuntimeToken.java | 57 +- src/main/java/com/iciql/SQLDialect.java | 370 +-- .../java/com/iciql/SQLDialectDefault.java | 984 +++--- src/main/java/com/iciql/SQLDialectDerby.java | 82 +- src/main/java/com/iciql/SQLDialectH2.java | 206 +- src/main/java/com/iciql/SQLDialectHSQL.java | 230 +- src/main/java/com/iciql/SQLDialectMSSQL.java | 64 +- src/main/java/com/iciql/SQLDialectMySQL.java | 124 +- .../java/com/iciql/SQLDialectPostgreSQL.java | 264 +- src/main/java/com/iciql/SQLDialectSQLite.java | 300 +- src/main/java/com/iciql/SQLStatement.java | 316 +- src/main/java/com/iciql/SelectColumn.java | 59 +- src/main/java/com/iciql/SelectTable.java | 165 +- src/main/java/com/iciql/SubQuery.java | 22 +- .../java/com/iciql/SubQueryCondition.java | 29 +- src/main/java/com/iciql/TableDefinition.java | 2324 +++++++------- src/main/java/com/iciql/TableInspector.java | 1353 ++++---- src/main/java/com/iciql/TestCondition.java | 157 +- src/main/java/com/iciql/Token.java | 16 +- src/main/java/com/iciql/UpdateColumn.java | 13 +- .../java/com/iciql/UpdateColumnIncrement.java | 54 +- src/main/java/com/iciql/UpdateColumnSet.java | 70 +- src/main/java/com/iciql/ValidationRemark.java | 190 +- .../com/iciql/adapter/GsonTypeAdapter.java | 50 +- .../adapter/JavaSerializationTypeAdapter.java | 119 +- .../iciql/adapter/SnakeYamlTypeAdapter.java | 67 +- .../com/iciql/adapter/XStreamTypeAdapter.java | 66 +- .../adapter/postgresql/JsonObjectAdapter.java | 40 +- .../adapter/postgresql/JsonStringAdapter.java | 73 +- .../postgresql/JsonbObjectAdapter.java | 40 +- .../postgresql/JsonbStringAdapter.java | 73 +- .../adapter/postgresql/XmlObjectAdapter.java | 9 +- .../adapter/postgresql/XmlStringAdapter.java | 73 +- src/main/java/com/iciql/bytecode/And.java | 32 +- .../java/com/iciql/bytecode/ArrayGet.java | 38 +- .../java/com/iciql/bytecode/CaseWhen.java | 64 +- .../java/com/iciql/bytecode/ClassReader.java | 2800 ++++++++--------- .../java/com/iciql/bytecode/Constant.java | 16 +- .../com/iciql/bytecode/ConstantNumber.java | 64 +- .../com/iciql/bytecode/ConstantString.java | 38 +- .../java/com/iciql/bytecode/Function.java | 30 +- src/main/java/com/iciql/bytecode/Not.java | 50 +- src/main/java/com/iciql/bytecode/Null.java | 22 +- .../java/com/iciql/bytecode/Operation.java | 162 +- src/main/java/com/iciql/bytecode/Or.java | 34 +- .../java/com/iciql/bytecode/Variable.java | 32 +- src/main/java/com/iciql/bytecode/package.html | 5 +- src/main/java/com/iciql/package.html | 5 +- .../java/com/iciql/util/GenerateModels.java | 290 +- src/main/java/com/iciql/util/IciqlLogger.java | 359 ++- src/main/java/com/iciql/util/JdbcUtils.java | 426 ++- .../com/iciql/util/Slf4jIciqlListener.java | 113 +- .../java/com/iciql/util/StatementBuilder.java | 240 +- src/main/java/com/iciql/util/StringUtils.java | 656 ++-- src/main/java/com/iciql/util/Utils.java | 1140 ++++--- .../com/iciql/util/WeakIdentityHashMap.java | 414 ++- src/main/java/com/iciql/util/package.html | 5 +- src/main/java/java/lang/AutoCloseable.java | 6 +- .../java/com/iciql/test/AliasMapTest.java | 201 +- .../java/com/iciql/test/AnnotationsTest.java | 321 +- .../java/com/iciql/test/BooleanModelTest.java | 275 +- src/test/java/com/iciql/test/ClobTest.java | 167 +- .../java/com/iciql/test/ConcurrencyTest.java | 337 +- .../com/iciql/test/DataTypeAdapterTest.java | 109 +- .../com/iciql/test/DefaultValuesTest.java | 63 +- src/test/java/com/iciql/test/EnumsTest.java | 243 +- .../java/com/iciql/test/ForeignKeyTest.java | 95 +- src/test/java/com/iciql/test/IciqlSuite.java | 1268 ++++---- src/test/java/com/iciql/test/JoinTest.java | 242 +- src/test/java/com/iciql/test/ModelsTest.java | 287 +- .../com/iciql/test/NestedConditionsTest.java | 478 ++- src/test/java/com/iciql/test/OneOfTest.java | 235 +- .../java/com/iciql/test/PrimitivesTest.java | 177 +- .../java/com/iciql/test/ProductDaoTest.java | 527 ++-- .../java/com/iciql/test/RuntimeQueryTest.java | 297 +- src/test/java/com/iciql/test/SamplesTest.java | 823 +++-- .../java/com/iciql/test/TransactionTest.java | 181 +- src/test/java/com/iciql/test/UUIDTest.java | 165 +- src/test/java/com/iciql/test/UpdateTest.java | 297 +- .../java/com/iciql/test/UpgradesTest.java | 298 +- src/test/java/com/iciql/test/ViewsTest.java | 163 +- .../com/iciql/test/models/BooleanModel.java | 142 +- .../test/models/CategoryAnnotationOnly.java | 66 +- .../com/iciql/test/models/ComplexObject.java | 56 +- .../java/com/iciql/test/models/Customer.java | 58 +- .../iciql/test/models/DefaultValuesModel.java | 42 +- .../com/iciql/test/models/EnumModels.java | 384 +-- .../iciql/test/models/MultipleBoolsModel.java | 41 +- .../java/com/iciql/test/models/Order.java | 69 +- .../iciql/test/models/PrimitivesModel.java | 104 +- .../java/com/iciql/test/models/Product.java | 123 +- .../test/models/ProductAnnotationOnly.java | 122 +- .../ProductAnnotationOnlyWithForeignKey.java | 128 +- .../models/ProductInheritedAnnotation.java | 56 +- .../test/models/ProductMixedAnnotation.java | 144 +- .../test/models/ProductNoCreateTable.java | 46 +- .../com/iciql/test/models/ProductView.java | 24 +- .../test/models/ProductViewFromQuery.java | 20 +- .../test/models/ProductViewInherited.java | 20 +- .../models/ProductViewInheritedComplex.java | 2 +- .../com/iciql/test/models/StaticQueries.java | 84 +- .../com/iciql/test/models/SupportedTypes.java | 358 +-- src/test/resources/iciql.properties | 7 +- 132 files changed, 17557 insertions(+), 17858 deletions(-) diff --git a/pom.xml b/pom.xml index 8a584e1..ec378fb 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 diff --git a/src/main/java/com/iciql/CompareType.java b/src/main/java/com/iciql/CompareType.java index 87ce77e..c97c0c8 100644 --- a/src/main/java/com/iciql/CompareType.java +++ b/src/main/java/com/iciql/CompareType.java @@ -22,24 +22,24 @@ package com.iciql; */ enum CompareType { - EQUAL("=", true), EXCEEDS(">", true), AT_LEAST(">=", true), LESS_THAN("<", true), AT_MOST("<=", true), NOT_EQUAL( - "<>", true), IS_NOT_NULL("IS NOT NULL", false), IS_NULL("IS NULL", false), LIKE("LIKE", true), BETWEEN( - "BETWEEN", true), IN("IN", true), NOT_IN("NOT IN", true); + EQUAL("=", true), EXCEEDS(">", true), AT_LEAST(">=", true), LESS_THAN("<", true), AT_MOST("<=", true), NOT_EQUAL( + "<>", true), IS_NOT_NULL("IS NOT NULL", false), IS_NULL("IS NULL", false), LIKE("LIKE", true), BETWEEN( + "BETWEEN", true), IN("IN", true), NOT_IN("NOT IN", true); - private String text; - private boolean hasRightExpression; + private String text; + private boolean hasRightExpression; - CompareType(String text, boolean hasRightExpression) { - this.text = text; - this.hasRightExpression = hasRightExpression; - } + CompareType(String text, boolean hasRightExpression) { + this.text = text; + this.hasRightExpression = hasRightExpression; + } - String getString() { - return text; - } + String getString() { + return text; + } - boolean hasRightExpression() { - return hasRightExpression; - } + boolean hasRightExpression() { + return hasRightExpression; + } } diff --git a/src/main/java/com/iciql/Condition.java b/src/main/java/com/iciql/Condition.java index 0ed1d06..ab5a5e3 100644 --- a/src/main/java/com/iciql/Condition.java +++ b/src/main/java/com/iciql/Condition.java @@ -19,56 +19,55 @@ package com.iciql; /** * A condition contains one or two operands and a compare operation. - * - * @param - * the operand type + * + * @param the operand type */ class Condition implements Token { - CompareType compareType; - A x, y, z; - Iterable i; + CompareType compareType; + A x, y, z; + Iterable i; - Condition(A x, CompareType compareType) { - this(x, null, null, null, compareType); - } + Condition(A x, CompareType compareType) { + this(x, null, null, null, compareType); + } - Condition(A x, A y, CompareType compareType) { - this(x, y, null, null, compareType); - } + Condition(A x, A y, CompareType compareType) { + this(x, y, null, null, compareType); + } - Condition(A x, A y, A z, CompareType compareType) { - this(x, y, z, null, compareType); - } + Condition(A x, A y, A z, CompareType compareType) { + this(x, y, z, null, compareType); + } - Condition(A x, Iterable i, CompareType compareType) { - this(x, null, null, i, compareType); - } + Condition(A x, Iterable i, CompareType compareType) { + this(x, null, null, i, compareType); + } - Condition(A x, A y, A z, Iterable i, CompareType compareType) { - this.compareType = compareType; - this.x = x; - this.y = y; - this.z = z; - this.i = i; - } + Condition(A x, A y, A z, Iterable i, CompareType compareType) { + this.compareType = compareType; + this.x = x; + this.y = y; + this.z = z; + this.i = i; + } - @SuppressWarnings("unchecked") - public void appendSQL(SQLStatement stat, Query query) { - query.appendSQL(stat, null, x); - stat.appendSQL(" "); - stat.appendSQL(compareType.getString()); - if (compareType.hasRightExpression()) { - if (i == null) { - stat.appendSQL(" "); - if (z == null) { - query.appendSQL(stat, x, y); - } else { - query.appendSQL(stat, x, y, z, compareType); - } - } else { - query.appendSQL(stat, x, (Iterable)i, compareType); - } - } - } + @SuppressWarnings("unchecked") + public void appendSQL(SQLStatement stat, Query query) { + query.appendSQL(stat, null, x); + stat.appendSQL(" "); + stat.appendSQL(compareType.getString()); + if (compareType.hasRightExpression()) { + if (i == null) { + stat.appendSQL(" "); + if (z == null) { + query.appendSQL(stat, x, y); + } else { + query.appendSQL(stat, x, y, z, compareType); + } + } else { + query.appendSQL(stat, x, (Iterable) i, compareType); + } + } + } } diff --git a/src/main/java/com/iciql/ConditionAndOr.java b/src/main/java/com/iciql/ConditionAndOr.java index 4d1cd0e..228fa9a 100644 --- a/src/main/java/com/iciql/ConditionAndOr.java +++ b/src/main/java/com/iciql/ConditionAndOr.java @@ -22,16 +22,16 @@ package com.iciql; */ enum ConditionAndOr implements Token { - AND("AND"), OR("OR"); + AND("AND"), OR("OR"); - private String text; + private String text; - ConditionAndOr(String text) { - this.text = text; - } + ConditionAndOr(String text) { + this.text = text; + } - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL(text); - } + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL(text); + } } diff --git a/src/main/java/com/iciql/ConditionOpenClose.java b/src/main/java/com/iciql/ConditionOpenClose.java index 5284abd..c0d6ea4 100644 --- a/src/main/java/com/iciql/ConditionOpenClose.java +++ b/src/main/java/com/iciql/ConditionOpenClose.java @@ -18,16 +18,16 @@ package com.iciql; enum ConditionOpenClose implements Token { - OPEN("("), CLOSE(")"); + OPEN("("), CLOSE(")"); - private String text; + private String text; - ConditionOpenClose(String text) { - this.text = text; - } + ConditionOpenClose(String text) { + this.text = text; + } - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL(text); - } + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL(text); + } } diff --git a/src/main/java/com/iciql/Constants.java b/src/main/java/com/iciql/Constants.java index e27e24a..8c8b898 100644 --- a/src/main/java/com/iciql/Constants.java +++ b/src/main/java/com/iciql/Constants.java @@ -25,38 +25,38 @@ import java.util.jar.Manifest; */ public class Constants { - public static final String NAME = "iciql"; - - // The build script extracts this exact line so be careful editing it - // and only use A-Z a-z 0-9 .-_ in the string. - public static final String API_CURRENT = "15"; - - public static String getVersion() { - return getManifestValue("implementation-version", "0.0.0-SNAPSHOT"); - } - - public static String getBuildDate() { - return getManifestValue("build-date", "PENDING"); - } - - private static String getManifestValue(String attrib, String defaultValue) { - Class clazz = Constants.class; - String className = clazz.getSimpleName() + ".class"; - String classPath = clazz.getResource(className).toString(); - try { - String manifestPath; - if (classPath.indexOf('!') > -1) { - manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF"; - } else { - String pkgPath = "/" + clazz.getPackage().getName().replace('.', '/'); - manifestPath = classPath.substring(0, classPath.indexOf(pkgPath)) + "/META-INF/MANIFEST.MF"; - } - Manifest manifest = new Manifest(new URL(manifestPath).openStream()); - Attributes attr = manifest.getMainAttributes(); - String value = attr.getValue(attrib); - return value; - } catch (Exception e) { - } - return defaultValue; - } + public static final String NAME = "iciql"; + + // The build script extracts this exact line so be careful editing it + // and only use A-Z a-z 0-9 .-_ in the string. + public static final String API_CURRENT = "15"; + + public static String getVersion() { + return getManifestValue("implementation-version", "0.0.0-SNAPSHOT"); + } + + public static String getBuildDate() { + return getManifestValue("build-date", "PENDING"); + } + + private static String getManifestValue(String attrib, String defaultValue) { + Class clazz = Constants.class; + String className = clazz.getSimpleName() + ".class"; + String classPath = clazz.getResource(className).toString(); + try { + String manifestPath; + if (classPath.indexOf('!') > -1) { + manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF"; + } else { + String pkgPath = "/" + clazz.getPackage().getName().replace('.', '/'); + manifestPath = classPath.substring(0, classPath.indexOf(pkgPath)) + "/META-INF/MANIFEST.MF"; + } + Manifest manifest = new Manifest(new URL(manifestPath).openStream()); + Attributes attr = manifest.getMainAttributes(); + String value = attr.getValue(attrib); + return value; + } catch (Exception e) { + } + return defaultValue; + } } diff --git a/src/main/java/com/iciql/Dao.java b/src/main/java/com/iciql/Dao.java index 29b42f0..8cb16ee 100644 --- a/src/main/java/com/iciql/Dao.java +++ b/src/main/java/com/iciql/Dao.java @@ -25,138 +25,137 @@ import java.util.List; * The Dao interface defines all CRUD methods for handling SQL object operations. * * @author James Moger - * */ public interface Dao extends AutoCloseable { - /** - * Insert an object into the database. - * - * @param t - * @return true if successful - */ - boolean insert(T t); - - /** - * Insert an object into the database and return it's primary key. - * - * @param t - * @return - */ - long insertAndGetKey(T t); - - /** - * Insert all objects into the database. - * - * @param list - */ - void insertAll(List list); - - /** - * Insert all objects into the database and return the list of primary keys. - * - * @param t - * @return a list of primary keys - */ - List insertAllAndGetKeys(List t); - - /** - * Updates an object in the database. - * - * @param t - * @return true if successful - */ - boolean update(T t); - - /** - * Updates all objects in the database. - * - * @param list - */ - void updateAll(List list); - - /** - * Inserts or updates an object in the database. - * - * @param t - */ - void merge(T t); - - /** - * Deletes an object from the database. - * - * @param t - * @return true if successful - */ - boolean delete(T t); - - /** - * Deletes all objects from the database. - * - * @param list - */ - void deleteAll(List list); - - /** - * Returns the underlying Db instance for lower-level access to database methods - * or direct JDBC access. - * - * @return the db instance - */ - Db db(); - - /** - * Close the underlying Db instance. - */ - @Override - void close(); - - /** - * Used to specify custom names for method parameters to be used - * for the SqlQuery or SqlUpdate annotations. - * - * You don't need to explicitly bind the parameters as each parameter - * is accessible by the standard "argN" syntax (0-indexed). - * - * Additionally, if you are compiling with Java 8 AND specifying the - * -parameters flag for javac, then you may use the parameter's name. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.PARAMETER }) - public @interface Bind { - String value(); - } - - /** - * - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.PARAMETER }) - public @interface BindBean { - String value() default ""; - } - - /** - * Used to indicate that a method should execute a query. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.METHOD }) - public @interface SqlQuery { - String value(); - } - - /** - * Used to indicate that a method should execute a statement. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.METHOD }) - public @interface SqlStatement { - String value(); - } - - public class BeanBinder { - public void bind(BindBean bind, Object obj) { - - } - } + /** + * Insert an object into the database. + * + * @param t + * @return true if successful + */ + boolean insert(T t); + + /** + * Insert an object into the database and return it's primary key. + * + * @param t + * @return + */ + long insertAndGetKey(T t); + + /** + * Insert all objects into the database. + * + * @param list + */ + void insertAll(List list); + + /** + * Insert all objects into the database and return the list of primary keys. + * + * @param t + * @return a list of primary keys + */ + List insertAllAndGetKeys(List t); + + /** + * Updates an object in the database. + * + * @param t + * @return true if successful + */ + boolean update(T t); + + /** + * Updates all objects in the database. + * + * @param list + */ + void updateAll(List list); + + /** + * Inserts or updates an object in the database. + * + * @param t + */ + void merge(T t); + + /** + * Deletes an object from the database. + * + * @param t + * @return true if successful + */ + boolean delete(T t); + + /** + * Deletes all objects from the database. + * + * @param list + */ + void deleteAll(List list); + + /** + * Returns the underlying Db instance for lower-level access to database methods + * or direct JDBC access. + * + * @return the db instance + */ + Db db(); + + /** + * Close the underlying Db instance. + */ + @Override + void close(); + + /** + * Used to specify custom names for method parameters to be used + * for the SqlQuery or SqlUpdate annotations. + *

+ * You don't need to explicitly bind the parameters as each parameter + * is accessible by the standard "argN" syntax (0-indexed). + *

+ * Additionally, if you are compiling with Java 8 AND specifying the + * -parameters flag for javac, then you may use the parameter's name. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + public @interface Bind { + String value(); + } + + /** + * + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.PARAMETER}) + public @interface BindBean { + String value() default ""; + } + + /** + * Used to indicate that a method should execute a query. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.METHOD}) + public @interface SqlQuery { + String value(); + } + + /** + * Used to indicate that a method should execute a statement. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.METHOD}) + public @interface SqlStatement { + String value(); + } + + public class BeanBinder { + public void bind(BindBean bind, Object obj) { + + } + } } diff --git a/src/main/java/com/iciql/DaoClasspathStatementProvider.java b/src/main/java/com/iciql/DaoClasspathStatementProvider.java index b1bbe14..3bb0b9e 100644 --- a/src/main/java/com/iciql/DaoClasspathStatementProvider.java +++ b/src/main/java/com/iciql/DaoClasspathStatementProvider.java @@ -15,81 +15,80 @@ */ package com.iciql; +import com.iciql.Iciql.Mode; + import java.io.InputStream; import java.util.Properties; -import com.iciql.Iciql.Mode; - /** * Loads DAO statements from Properties resource files the classpath. * * @author James Moger - * */ public class DaoClasspathStatementProvider implements DaoStatementProvider { - private final Properties externalStatements; - - public DaoClasspathStatementProvider() { - externalStatements = load(); - } - - /** - * Returns the list of statement resources to try locating. - * - * @return - */ - protected String[] getStatementResources() { - return new String[] { "/iciql.properties", "/iciql.xml", "/conf/iciql.properties", "/conf/iciql.xml" }; - } - - /** - * Loads the first statement resource found on the classpath. - * - * @return the loaded statements - */ - private Properties load() { - - Properties props = new Properties(); - for (String resource : getStatementResources()) { - - InputStream is = null; - - try { - is = DaoProxy.class.getResourceAsStream(resource); - - if (is != null) { - - if (resource.toLowerCase().endsWith(".xml")) { - // load an .XML statements file - props.loadFromXML(is); - } else { - // load a .Properties statements file - props.load(is); - } - - break; - } - - } catch (Exception e) { - throw new IciqlException(e, "Failed to parse {0}", resource); - } finally { - try { - is.close(); - } catch (Exception e) { - } - } - - } - return props; - } - - @Override - public String getStatement(String idOrStatement, Mode mode) { - final String modePrefix = "%" + mode.name().toLowerCase() + "."; - String value = externalStatements.getProperty(idOrStatement, idOrStatement); - value = externalStatements.getProperty(modePrefix + idOrStatement, value); - return value; - } + private final Properties externalStatements; + + public DaoClasspathStatementProvider() { + externalStatements = load(); + } + + /** + * Returns the list of statement resources to try locating. + * + * @return + */ + protected String[] getStatementResources() { + return new String[]{"/iciql.properties", "/iciql.xml", "/conf/iciql.properties", "/conf/iciql.xml"}; + } + + /** + * Loads the first statement resource found on the classpath. + * + * @return the loaded statements + */ + private Properties load() { + + Properties props = new Properties(); + for (String resource : getStatementResources()) { + + InputStream is = null; + + try { + is = DaoProxy.class.getResourceAsStream(resource); + + if (is != null) { + + if (resource.toLowerCase().endsWith(".xml")) { + // load an .XML statements file + props.loadFromXML(is); + } else { + // load a .Properties statements file + props.load(is); + } + + break; + } + + } catch (Exception e) { + throw new IciqlException(e, "Failed to parse {0}", resource); + } finally { + try { + is.close(); + } catch (Exception e) { + } + } + + } + return props; + } + + @Override + public String getStatement(String idOrStatement, Mode mode) { + final String modePrefix = "%" + mode.name().toLowerCase() + "."; + String value = externalStatements.getProperty(idOrStatement, idOrStatement); + value = externalStatements.getProperty(modePrefix + idOrStatement, value); + return value; + } } diff --git a/src/main/java/com/iciql/DaoProxy.java b/src/main/java/com/iciql/DaoProxy.java index 34187c4..8851a0c 100644 --- a/src/main/java/com/iciql/DaoProxy.java +++ b/src/main/java/com/iciql/DaoProxy.java @@ -15,6 +15,11 @@ */ package com.iciql; +import com.iciql.Iciql.DataTypeAdapter; +import com.iciql.util.JdbcUtils; +import com.iciql.util.StringUtils; +import com.iciql.util.Utils; + import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Constructor; @@ -37,503 +42,497 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.iciql.Iciql.DataTypeAdapter; -import com.iciql.util.JdbcUtils; -import com.iciql.util.StringUtils; -import com.iciql.util.Utils; - /** * DaoProxy creates a dynamic instance of the provided Dao interface. * - * @author James Moger - * * @param + * @author James Moger */ final class DaoProxy implements InvocationHandler, Dao { - private final Db db; + private final Db db; - private final Class daoInterface; + private final Class daoInterface; - private final char bindingDelimiter = ':'; + private final char bindingDelimiter = ':'; - private final Map indexedSqlCache; + private final Map indexedSqlCache; - DaoProxy(Db db, Class daoInterface) { - this.db = db; - this.daoInterface = daoInterface; - this.indexedSqlCache = new ConcurrentHashMap(); - } + DaoProxy(Db db, Class daoInterface) { + this.db = db; + this.daoInterface = daoInterface; + this.indexedSqlCache = new ConcurrentHashMap(); + } - /** - * Builds a proxy object for the DAO interface. - * - * @return a proxy object - */ - @SuppressWarnings("unchecked") - X build() { + /** + * Builds a proxy object for the DAO interface. + * + * @return a proxy object + */ + @SuppressWarnings("unchecked") + X build() { - if (!daoInterface.isInterface()) { - throw new IciqlException("Dao {0} must be an interface!", daoInterface.getName()); - } + if (!daoInterface.isInterface()) { + throw new IciqlException("Dao {0} must be an interface!", daoInterface.getName()); + } - ClassLoader classLoader = daoInterface.getClassLoader(); + ClassLoader classLoader = daoInterface.getClassLoader(); - Set> interfaces = new HashSet>(); - interfaces.add(Dao.class); - interfaces.add(daoInterface); - for (Class clazz : daoInterface.getInterfaces()) { - interfaces.add(clazz); - } + Set> interfaces = new HashSet>(); + interfaces.add(Dao.class); + interfaces.add(daoInterface); + for (Class clazz : daoInterface.getInterfaces()) { + interfaces.add(clazz); + } - Class[] constructorParams = { InvocationHandler.class }; - Class[] allInterfaces = interfaces.toArray(new Class[interfaces.size()]); + Class[] constructorParams = {InvocationHandler.class}; + Class[] allInterfaces = interfaces.toArray(new Class[interfaces.size()]); - try { + try { - Class proxyClass = Proxy.getProxyClass(classLoader, allInterfaces); - Constructor proxyConstructor = proxyClass.getConstructor(constructorParams); - return (X) proxyConstructor.newInstance(new Object[] { this }); + Class proxyClass = Proxy.getProxyClass(classLoader, allInterfaces); + Constructor proxyConstructor = proxyClass.getConstructor(constructorParams); + return (X) proxyConstructor.newInstance(new Object[]{this}); - } catch (Exception e) { - throw new IciqlException(e); - } - } + } catch (Exception e) { + throw new IciqlException(e); + } + } - /** - * Invoke intercepts method calls and delegates execution to the appropriate object. - */ - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - try { + /** + * Invoke intercepts method calls and delegates execution to the appropriate object. + */ + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + try { - if (method.getDeclaringClass() == Dao.class) { + if (method.getDeclaringClass() == Dao.class) { - return method.invoke(this, args); + return method.invoke(this, args); - } else if (method.isAnnotationPresent(SqlQuery.class)) { + } else if (method.isAnnotationPresent(SqlQuery.class)) { - String sql = method.getAnnotation(SqlQuery.class).value(); - String statement = db.getDaoStatementProvider().getStatement(sql, db.getMode()); - return executeQuery(method, args, statement); + String sql = method.getAnnotation(SqlQuery.class).value(); + String statement = db.getDaoStatementProvider().getStatement(sql, db.getMode()); + return executeQuery(method, args, statement); - } else if (method.isAnnotationPresent(SqlStatement.class)) { + } else if (method.isAnnotationPresent(SqlStatement.class)) { - String sql = method.getAnnotation(SqlStatement.class).value(); - String statement = db.getDaoStatementProvider().getStatement(sql, db.getMode()); - return executeStatement(method, args, statement); + String sql = method.getAnnotation(SqlStatement.class).value(); + String statement = db.getDaoStatementProvider().getStatement(sql, db.getMode()); + return executeStatement(method, args, statement); - } else { + } else { - throw new IciqlException("Can not invoke non-dao method {0}.{1}", - method.getDeclaringClass().getSimpleName(), method.getName()); + throw new IciqlException("Can not invoke non-dao method {0}.{1}", + method.getDeclaringClass().getSimpleName(), method.getName()); - } + } - } catch (InvocationTargetException te) { - throw te.getCause(); - } - } + } catch (InvocationTargetException te) { + throw te.getCause(); + } + } - /** - * Execute a query. - * - * @param method - * @param methodArgs - * @param sql - * @return the result - */ - private Object executeQuery(Method method, Object[] methodArgs, String sql) { + /** + * Execute a query. + * + * @param method + * @param methodArgs + * @param sql + * @return the result + */ + private Object executeQuery(Method method, Object[] methodArgs, String sql) { /* - * Determine and validate the return type + * Determine and validate the return type */ - Class returnType = method.getReturnType(); - - if (void.class == returnType) { - throw new IciqlException("You must specify a return type for @{0} {1}.{2}!", - SqlQuery.class.getSimpleName(), method.getDeclaringClass().getSimpleName(), method.getName()); - } - - if (Collection.class.isAssignableFrom(returnType)) { - throw new IciqlException("You may not return a collection for an @{0} method, please change the return type of {1}.{2} to YourClass[]!", - SqlQuery.class.getSimpleName(), method.getDeclaringClass().getSimpleName(), method.getName()); - } - - boolean isArray = false; - if (returnType.isArray()) { - isArray = true; - returnType = returnType.getComponentType(); - } - - boolean isJavaType = returnType.isEnum() - || returnType.isPrimitive() - || java.lang.Boolean.class.isAssignableFrom(returnType) - || java.lang.Number.class.isAssignableFrom(returnType) - || java.lang.String.class.isAssignableFrom(returnType) - || java.util.Date.class.isAssignableFrom(returnType) - || byte[].class.isAssignableFrom(returnType); - - Class> adapter = Utils.getDataTypeAdapter(method.getAnnotations()); - if (adapter == null) { - adapter = Utils.getDataTypeAdapter(returnType.getAnnotations()); - } + Class returnType = method.getReturnType(); + + if (void.class == returnType) { + throw new IciqlException("You must specify a return type for @{0} {1}.{2}!", + SqlQuery.class.getSimpleName(), method.getDeclaringClass().getSimpleName(), method.getName()); + } + + if (Collection.class.isAssignableFrom(returnType)) { + throw new IciqlException("You may not return a collection for an @{0} method, please change the return type of {1}.{2} to YourClass[]!", + SqlQuery.class.getSimpleName(), method.getDeclaringClass().getSimpleName(), method.getName()); + } + + boolean isArray = false; + if (returnType.isArray()) { + isArray = true; + returnType = returnType.getComponentType(); + } + + boolean isJavaType = returnType.isEnum() + || returnType.isPrimitive() + || java.lang.Boolean.class.isAssignableFrom(returnType) + || java.lang.Number.class.isAssignableFrom(returnType) + || java.lang.String.class.isAssignableFrom(returnType) + || java.util.Date.class.isAssignableFrom(returnType) + || byte[].class.isAssignableFrom(returnType); + + Class> adapter = Utils.getDataTypeAdapter(method.getAnnotations()); + if (adapter == null) { + adapter = Utils.getDataTypeAdapter(returnType.getAnnotations()); + } /* * Prepare & execute sql */ - PreparedSql preparedSql = prepareSql(method, methodArgs, sql); + PreparedSql preparedSql = prepareSql(method, methodArgs, sql); - List objects; - if (!isJavaType && adapter == null) { + List objects; + if (!isJavaType && adapter == null) { - // query of an Iciql model - objects = db.executeQuery(returnType, preparedSql.sql, preparedSql.parameters); + // query of an Iciql model + objects = db.executeQuery(returnType, preparedSql.sql, preparedSql.parameters); - } else { + } else { - // query of (array of) standard Java type or a DataTypeAdapter type - objects = Utils.newArrayList(); - ResultSet rs = db.executeQuery(preparedSql.sql, preparedSql.parameters); - try { + // query of (array of) standard Java type or a DataTypeAdapter type + objects = Utils.newArrayList(); + ResultSet rs = db.executeQuery(preparedSql.sql, preparedSql.parameters); + try { - while (rs.next()) { + while (rs.next()) { - Object value = db.getDialect().deserialize(rs, 1, returnType, adapter); - objects.add(value); + Object value = db.getDialect().deserialize(rs, 1, returnType, adapter); + objects.add(value); - if (!isArray) { - // we are not returning an array so we break - // the loop and return the first result - break; - } - } + if (!isArray) { + // we are not returning an array so we break + // the loop and return the first result + break; + } + } - } catch (SQLException e) { - throw new IciqlException(e); - } finally { - JdbcUtils.closeSilently(rs); - } + } catch (SQLException e) { + throw new IciqlException(e); + } finally { + JdbcUtils.closeSilently(rs); + } - } + } /* * Return the results */ - if (objects == null || objects.isEmpty()) { + if (objects == null || objects.isEmpty()) { - // no results - if (isArray) { - // return an empty array - return Array.newInstance(returnType, 0); - } + // no results + if (isArray) { + // return an empty array + return Array.newInstance(returnType, 0); + } - // nothing to return! - return null; + // nothing to return! + return null; - } else if (isArray) { + } else if (isArray) { - // return an array of object results - Object array = Array.newInstance(returnType, objects.size()); - for (int i = 0; i < objects.size(); i++) { - Array.set(array, i, objects.get(i)); - } - return array; + // return an array of object results + Object array = Array.newInstance(returnType, objects.size()); + for (int i = 0; i < objects.size(); i++) { + Array.set(array, i, objects.get(i)); + } + return array; - } + } - // return first element - return objects.get(0); - } + // return first element + return objects.get(0); + } - /** - * Execute a statement. - * - * @param method - * @param methodArgs - * @param sql - * @return the result - */ - private Object executeStatement(Method method, Object[] methodArgs, String sql) { + /** + * Execute a statement. + * + * @param method + * @param methodArgs + * @param sql + * @return the result + */ + private Object executeStatement(Method method, Object[] methodArgs, String sql) { /* * Determine and validate the return type */ - Class returnType = method.getReturnType(); + Class returnType = method.getReturnType(); - if (void.class != returnType && boolean.class != returnType && int.class != returnType) { + if (void.class != returnType && boolean.class != returnType && int.class != returnType) { - throw new IciqlException("Invalid return type '{0}' for @{1} {2}.{3}!", - returnType.getSimpleName(), SqlQuery.class.getSimpleName(), - method.getDeclaringClass().getSimpleName(), method.getName()); - } + throw new IciqlException("Invalid return type '{0}' for @{1} {2}.{3}!", + returnType.getSimpleName(), SqlQuery.class.getSimpleName(), + method.getDeclaringClass().getSimpleName(), method.getName()); + } /* * Prepare & execute sql */ - PreparedSql preparedSql = prepareSql(method, methodArgs, sql); - int rows = db.executeUpdate(preparedSql.sql, preparedSql.parameters); + PreparedSql preparedSql = prepareSql(method, methodArgs, sql); + int rows = db.executeUpdate(preparedSql.sql, preparedSql.parameters); /* * Return the results */ - if (void.class == returnType) { - - // return nothing - return null; - - } else if (boolean.class == returnType) { - - // return true if any rows were affected - return rows > 0; - - } else { - - // return number of rows - return rows; - - } - } - - /** - * Prepares an sql statement and execution parameters based on the supplied - * method and it's arguments. - * - * @param method - * @param methodArgs - * @param sql - * @return a prepared sql statement and arguments - */ - private PreparedSql prepareSql(Method method, Object[] methodArgs, String sql) { - - if (methodArgs == null || methodArgs.length == 0) { - // no method arguments - return new PreparedSql(sql, null); - } - - IndexedSql indexedSql = indexedSqlCache.get(method); - - if (indexedSql == null) { - - // index the sql and method args - indexedSql = indexSql(method, sql); - - // cache the indexed sql for re-use - indexedSqlCache.put(method, indexedSql); - } - - final PreparedSql preparedSql = indexedSql.prepareSql(db, methodArgs); - return preparedSql; - } - - /** - * Indexes an sql statement and method args based on the supplied - * method and it's arguments. - * - * @param method - * @param sql - * @return an indexed sql statement and arguments - */ - private IndexedSql indexSql(Method method, String sql) { - - Map parameterIndex = buildParameterIndex(method); - - // build a regex to extract parameter names from the sql statement - StringBuilder sb = new StringBuilder(); - sb.append(bindingDelimiter); - sb.append("{1}(\\?"); - for (String name : parameterIndex.keySet()) { - sb.append("|"); - // strip binding delimeter from name - sb.append(name); - } - sb.append(')'); - - // identify parameters, replace with the '?' PreparedStatement - // delimiter and build the PreparedStatement parameters array - final String regex = sb.toString(); - final Pattern p = Pattern.compile(regex); - final Matcher m = p.matcher(sql); - final StringBuffer buffer = new StringBuffer(); - - List indexedArgs = Utils.newArrayList(); - int count = 0; - while (m.find()) { - String binding = m.group(1); - m.appendReplacement(buffer, "?"); - - IndexedArgument indexedArg; - if ("?".equals(binding)) { - // standard ? JDBC placeholder - indexedArg = parameterIndex.get("arg" + count); - } else { - // named placeholder - indexedArg = parameterIndex.get(binding); - } - - if (indexedArg == null) { - throw new IciqlException("Unbound SQL parameter '{0}' in {1}.{2}", - binding, method.getDeclaringClass().getSimpleName(), method.getName()); - } - indexedArgs.add(indexedArg); - - count++; - } - m.appendTail(buffer); - - final String statement = buffer.toString(); - - // create an IndexedSql container for the statement and indexes - return new IndexedSql(statement, Collections.unmodifiableList(indexedArgs)); - - } - - /** - * Builds an index of parameter name->(position,typeAdapter) from the method arguments - * array. This index is calculated once per method. - * - * @param method - * @return a bindings map of ("name", IndexedArgument) pairs - */ - private Map buildParameterIndex(Method method) { - - Map index = new TreeMap(); - - Annotation [][] annotationsMatrix = method.getParameterAnnotations(); - for (int i = 0; i < annotationsMatrix.length; i++) { - - Annotation [] annotations = annotationsMatrix[i]; + if (void.class == returnType) { + + // return nothing + return null; + + } else if (boolean.class == returnType) { + + // return true if any rows were affected + return rows > 0; + + } else { + + // return number of rows + return rows; + + } + } + + /** + * Prepares an sql statement and execution parameters based on the supplied + * method and it's arguments. + * + * @param method + * @param methodArgs + * @param sql + * @return a prepared sql statement and arguments + */ + private PreparedSql prepareSql(Method method, Object[] methodArgs, String sql) { + + if (methodArgs == null || methodArgs.length == 0) { + // no method arguments + return new PreparedSql(sql, null); + } + + IndexedSql indexedSql = indexedSqlCache.get(method); + + if (indexedSql == null) { + + // index the sql and method args + indexedSql = indexSql(method, sql); + + // cache the indexed sql for re-use + indexedSqlCache.put(method, indexedSql); + } + + final PreparedSql preparedSql = indexedSql.prepareSql(db, methodArgs); + return preparedSql; + } + + /** + * Indexes an sql statement and method args based on the supplied + * method and it's arguments. + * + * @param method + * @param sql + * @return an indexed sql statement and arguments + */ + private IndexedSql indexSql(Method method, String sql) { + + Map parameterIndex = buildParameterIndex(method); + + // build a regex to extract parameter names from the sql statement + StringBuilder sb = new StringBuilder(); + sb.append(bindingDelimiter); + sb.append("{1}(\\?"); + for (String name : parameterIndex.keySet()) { + sb.append("|"); + // strip binding delimeter from name + sb.append(name); + } + sb.append(')'); + + // identify parameters, replace with the '?' PreparedStatement + // delimiter and build the PreparedStatement parameters array + final String regex = sb.toString(); + final Pattern p = Pattern.compile(regex); + final Matcher m = p.matcher(sql); + final StringBuffer buffer = new StringBuffer(); + + List indexedArgs = Utils.newArrayList(); + int count = 0; + while (m.find()) { + String binding = m.group(1); + m.appendReplacement(buffer, "?"); + + IndexedArgument indexedArg; + if ("?".equals(binding)) { + // standard ? JDBC placeholder + indexedArg = parameterIndex.get("arg" + count); + } else { + // named placeholder + indexedArg = parameterIndex.get(binding); + } + + if (indexedArg == null) { + throw new IciqlException("Unbound SQL parameter '{0}' in {1}.{2}", + binding, method.getDeclaringClass().getSimpleName(), method.getName()); + } + indexedArgs.add(indexedArg); + + count++; + } + m.appendTail(buffer); + + final String statement = buffer.toString(); + + // create an IndexedSql container for the statement and indexes + return new IndexedSql(statement, Collections.unmodifiableList(indexedArgs)); + + } + + /** + * Builds an index of parameter name->(position,typeAdapter) from the method arguments + * array. This index is calculated once per method. + * + * @param method + * @return a bindings map of ("name", IndexedArgument) pairs + */ + private Map buildParameterIndex(Method method) { + + Map index = new TreeMap(); + + Annotation[][] annotationsMatrix = method.getParameterAnnotations(); + for (int i = 0; i < annotationsMatrix.length; i++) { + + Annotation[] annotations = annotationsMatrix[i]; /* * Conditionally map the bean properties of the method argument * class to Method and Field instances. */ - BindBean bean = getAnnotation(BindBean.class, annotations); - if (bean != null) { - final String prefix = bean.value(); - final Class argumentClass = method.getParameterTypes()[i]; - Map beanIndex = buildBeanIndex(i, prefix, argumentClass); - index.putAll(beanIndex); - } - - Class> typeAdapter = Utils.getDataTypeAdapter(annotations); - final IndexedArgument indexedArgument = new IndexedArgument(i, typeAdapter); - - // :N - 1-indexed, like JDBC ResultSet - index.put("" + (i + 1), indexedArgument); - - // argN - 0-indexed, like Reflection - index.put("arg" + i, indexedArgument); - - // Bound name - Bind binding = getAnnotation(Bind.class, annotations); - if (binding!= null && !binding.value().isEmpty()) { - index.put(binding.value(), indexedArgument); - } - - // try mapping Java 8 argument names, may overwrite argN - try { - Class nullArgs = null; - Method getParameters = method.getClass().getMethod("getParameters", nullArgs); - if (getParameters != null) { - Object [] parameters = (Object []) getParameters.invoke(method, nullArgs); - if (parameters != null) { - Object o = parameters[i]; - Method getName = o.getClass().getMethod("getName", nullArgs); - String j8name = getName.invoke(o, nullArgs).toString(); - if (!j8name.isEmpty()) { - index.put(j8name, indexedArgument); - } - } - } - } catch (Throwable t) { - } - } - - return index; - } - - /** - * Builds an index of parameter name->(position,method) from the method arguments - * array. This index is calculated once per method. - * - * @param argumentIndex - * @param prefix - * @param beanClass - * @return a bindings map of ("prefix.property", IndexedArgument) pairs - */ - private Map buildBeanIndex(int argumentIndex, String prefix, Class beanClass) { - - final String beanPrefix = StringUtils.isNullOrEmpty(prefix) ? "" : (prefix + "."); - final Map index = new TreeMap(); - - // map JavaBean property getters - for (Method method : beanClass.getMethods()) { - - if (Modifier.isStatic(method.getModifiers()) - || method.getReturnType() == void.class - || method.getParameterTypes().length > 0 - || method.getDeclaringClass() == Object.class) { - - // not a JavaBean property - continue; - } - - final String propertyName; - final String name = method.getName(); - if (name.startsWith("get")) { - propertyName = method.getName().substring(3); - } else if (name.startsWith("is")) { - propertyName = method.getName().substring(2); - } else { - propertyName = null; - } - - if (propertyName == null) { - // not a conventional JavaBean property - continue; - } - - final String binding = beanPrefix + preparePropertyName(propertyName); - final IndexedArgument indexedArg = new IndexedArgument(argumentIndex, method); - - index.put(binding, indexedArg); - } - - // map public instance fields - for (Field field : beanClass.getFields()) { - - if (Modifier.isStatic(field.getModifiers())) { - // not a JavaBean property - continue; - } - - final String binding = beanPrefix + preparePropertyName(field.getName()); - final IndexedArgument indexedArg = new IndexedArgument(argumentIndex, field); - - index.put(binding, indexedArg); - - } - - return index; - } - - @SuppressWarnings("unchecked") - private T getAnnotation(Class annotationClass, Annotation [] annotations) { - if (annotations != null) { - for (Annotation annotation : annotations) { - if (annotation.annotationType() == annotationClass) { - return (T) annotation; - } - } - } - return null; - } - - private String preparePropertyName(String value) { - return Character.toLowerCase(value.charAt(0)) + value.substring(1); - } + BindBean bean = getAnnotation(BindBean.class, annotations); + if (bean != null) { + final String prefix = bean.value(); + final Class argumentClass = method.getParameterTypes()[i]; + Map beanIndex = buildBeanIndex(i, prefix, argumentClass); + index.putAll(beanIndex); + } + + Class> typeAdapter = Utils.getDataTypeAdapter(annotations); + final IndexedArgument indexedArgument = new IndexedArgument(i, typeAdapter); + + // :N - 1-indexed, like JDBC ResultSet + index.put("" + (i + 1), indexedArgument); + + // argN - 0-indexed, like Reflection + index.put("arg" + i, indexedArgument); + + // Bound name + Bind binding = getAnnotation(Bind.class, annotations); + if (binding != null && !binding.value().isEmpty()) { + index.put(binding.value(), indexedArgument); + } + + // try mapping Java 8 argument names, may overwrite argN + try { + Class nullArgs = null; + Method getParameters = method.getClass().getMethod("getParameters", nullArgs); + if (getParameters != null) { + Object[] parameters = (Object[]) getParameters.invoke(method, nullArgs); + if (parameters != null) { + Object o = parameters[i]; + Method getName = o.getClass().getMethod("getName", nullArgs); + String j8name = getName.invoke(o, nullArgs).toString(); + if (!j8name.isEmpty()) { + index.put(j8name, indexedArgument); + } + } + } + } catch (Throwable t) { + } + } + + return index; + } + + /** + * Builds an index of parameter name->(position,method) from the method arguments + * array. This index is calculated once per method. + * + * @param argumentIndex + * @param prefix + * @param beanClass + * @return a bindings map of ("prefix.property", IndexedArgument) pairs + */ + private Map buildBeanIndex(int argumentIndex, String prefix, Class beanClass) { + + final String beanPrefix = StringUtils.isNullOrEmpty(prefix) ? "" : (prefix + "."); + final Map index = new TreeMap(); + + // map JavaBean property getters + for (Method method : beanClass.getMethods()) { + + if (Modifier.isStatic(method.getModifiers()) + || method.getReturnType() == void.class + || method.getParameterTypes().length > 0 + || method.getDeclaringClass() == Object.class) { + + // not a JavaBean property + continue; + } + + final String propertyName; + final String name = method.getName(); + if (name.startsWith("get")) { + propertyName = method.getName().substring(3); + } else if (name.startsWith("is")) { + propertyName = method.getName().substring(2); + } else { + propertyName = null; + } + + if (propertyName == null) { + // not a conventional JavaBean property + continue; + } + + final String binding = beanPrefix + preparePropertyName(propertyName); + final IndexedArgument indexedArg = new IndexedArgument(argumentIndex, method); + + index.put(binding, indexedArg); + } + + // map public instance fields + for (Field field : beanClass.getFields()) { + + if (Modifier.isStatic(field.getModifiers())) { + // not a JavaBean property + continue; + } + + final String binding = beanPrefix + preparePropertyName(field.getName()); + final IndexedArgument indexedArg = new IndexedArgument(argumentIndex, field); + + index.put(binding, indexedArg); + + } + + return index; + } + + @SuppressWarnings("unchecked") + private T getAnnotation(Class annotationClass, Annotation[] annotations) { + if (annotations != null) { + for (Annotation annotation : annotations) { + if (annotation.annotationType() == annotationClass) { + return (T) annotation; + } + } + } + return null; + } + + private String preparePropertyName(String value) { + return Character.toLowerCase(value.charAt(0)) + value.substring(1); + } /* * @@ -541,213 +540,213 @@ final class DaoProxy implements InvocationHandler, Dao { * */ - @Override - public final Db db() { - return db; - } - - @Override - public final boolean insert(T t) { - return db.insert(t); - } - - @Override - public final void insertAll(List t) { - db.insertAll(t); - } - - @Override - public final long insertAndGetKey(T t) { - return db.insertAndGetKey(t); - } - - @Override - public final List insertAllAndGetKeys(List t) { - return db.insertAllAndGetKeys(t); - } - - @Override - public final boolean update(T t) { - return db.update(t); - } - - @Override - public final void updateAll(List t) { - db.updateAll(t); - } - - @Override - public final void merge(T t) { - db.merge(t); - } - - @Override - public final boolean delete(T t) { - return db.delete(t); - } - - @Override - public final void deleteAll(List t) { - db.deleteAll(t); - } - - @Override - public final void close() { - db.close(); - } - - /** - * Container class to hold the prepared JDBC SQL statement and execution - * parameters. - */ - private class PreparedSql { - final String sql; - final Object [] parameters; - - PreparedSql(String sql, Object [] parameters) { - this.sql = sql; - this.parameters = parameters; - } - - @Override - public String toString() { - return sql; - } - - } - - /** - * Container class to hold a parsed JDBC SQL statement and - * IndexedParameters. - *

- * Instances of this class are cached because they are functional processing - * containers as they contain Method and Field references for binding beans - * and matching to method arguments. - *

- */ - private class IndexedSql { - final String sql; - final List indexedArgs; - - IndexedSql(String sql, List indexedArgs) { - this.sql = sql; - this.indexedArgs = indexedArgs; - } - - /** - * Prepares the method arguments for statement execution. - * - * @param db - * @param methodArgs - * @return the prepared sql statement and parameters - */ - PreparedSql prepareSql(Db db, Object [] methodArgs) { - - Object [] parameters = new Object[indexedArgs.size()]; - - for (int i = 0; i < indexedArgs.size(); i++) { - - IndexedArgument indexedArg = indexedArgs.get(i); - Object methodArg = methodArgs[indexedArg.index]; + @Override + public final Db db() { + return db; + } + + @Override + public final boolean insert(T t) { + return db.insert(t); + } + + @Override + public final void insertAll(List t) { + db.insertAll(t); + } + + @Override + public final long insertAndGetKey(T t) { + return db.insertAndGetKey(t); + } + + @Override + public final List insertAllAndGetKeys(List t) { + return db.insertAllAndGetKeys(t); + } + + @Override + public final boolean update(T t) { + return db.update(t); + } + + @Override + public final void updateAll(List t) { + db.updateAll(t); + } + + @Override + public final void merge(T t) { + db.merge(t); + } + + @Override + public final boolean delete(T t) { + return db.delete(t); + } + + @Override + public final void deleteAll(List t) { + db.deleteAll(t); + } + + @Override + public final void close() { + db.close(); + } + + /** + * Container class to hold the prepared JDBC SQL statement and execution + * parameters. + */ + private class PreparedSql { + final String sql; + final Object[] parameters; + + PreparedSql(String sql, Object[] parameters) { + this.sql = sql; + this.parameters = parameters; + } + + @Override + public String toString() { + return sql; + } + + } + + /** + * Container class to hold a parsed JDBC SQL statement and + * IndexedParameters. + *

+ * Instances of this class are cached because they are functional processing + * containers as they contain Method and Field references for binding beans + * and matching to method arguments. + *

+ */ + private class IndexedSql { + final String sql; + final List indexedArgs; + + IndexedSql(String sql, List indexedArgs) { + this.sql = sql; + this.indexedArgs = indexedArgs; + } + + /** + * Prepares the method arguments for statement execution. + * + * @param db + * @param methodArgs + * @return the prepared sql statement and parameters + */ + PreparedSql prepareSql(Db db, Object[] methodArgs) { + + Object[] parameters = new Object[indexedArgs.size()]; + + for (int i = 0; i < indexedArgs.size(); i++) { + + IndexedArgument indexedArg = indexedArgs.get(i); + Object methodArg = methodArgs[indexedArg.index]; + + Object value = methodArg; + Class> typeAdapter = indexedArg.typeAdapter; + + if (indexedArg.method != null) { + + // execute the bean method + try { + + value = indexedArg.method.invoke(methodArg); + typeAdapter = Utils.getDataTypeAdapter(indexedArg.method.getAnnotations()); + + } catch (Exception e) { + throw new IciqlException(e); + } + + } else if (indexedArg.field != null) { + + // extract the field value + try { + + value = indexedArg.field.get(methodArg); + typeAdapter = Utils.getDataTypeAdapter(indexedArg.field.getAnnotations()); + + } catch (Exception e) { + throw new IciqlException(e); + } + + } else if (typeAdapter == null) { + + // identify the type adapter for the argument class + typeAdapter = Utils.getDataTypeAdapter(methodArg.getClass().getAnnotations()); + } + + // prepare the parameter + parameters[i] = db.getDialect().serialize(value, typeAdapter); + + } + + return new PreparedSql(sql, parameters); + + } + + @Override + public String toString() { + return sql; + } + } + + /** + * IndexedArgument holds cached information about how to process an method + * argument by it's index in the method arguments array. + *

+ * An argument may be passed-through, might be bound to a bean property, + * might be transformed with a type adapter, or a combination of these. + *

+ */ + private class IndexedArgument { + final int index; + final Class> typeAdapter; + final Method method; + final Field field; + + IndexedArgument(int index, Class> typeAdapter) { + this.index = index; + this.typeAdapter = typeAdapter; + this.method = null; + this.field = null; + } + + IndexedArgument(int methodArgIndex, Method method) { + this.index = methodArgIndex; + this.typeAdapter = null; + this.method = method; + this.field = null; + } + + IndexedArgument(int methodArgIndex, Field field) { + this.index = methodArgIndex; + this.typeAdapter = null; + this.method = null; + this.field = field; + } + + @Override + public String toString() { + + String accessor; + if (method != null) { + accessor = "M:" + method.getDeclaringClass().getSimpleName() + "." + method.getName(); + } else if (field != null) { + accessor = "F:" + field.getDeclaringClass().getSimpleName() + "." + field.getName(); + } else { + accessor = "A:arg"; + } - Object value = methodArg; - Class> typeAdapter = indexedArg.typeAdapter; - - if (indexedArg.method != null) { - - // execute the bean method - try { - - value = indexedArg.method.invoke(methodArg); - typeAdapter = Utils.getDataTypeAdapter(indexedArg.method.getAnnotations()); - - } catch (Exception e) { - throw new IciqlException(e); - } - - } else if (indexedArg.field != null) { - - // extract the field value - try { - - value = indexedArg.field.get(methodArg); - typeAdapter = Utils.getDataTypeAdapter(indexedArg.field.getAnnotations()); - - } catch (Exception e) { - throw new IciqlException(e); - } - - } else if (typeAdapter == null) { - - // identify the type adapter for the argument class - typeAdapter = Utils.getDataTypeAdapter(methodArg.getClass().getAnnotations()); - } - - // prepare the parameter - parameters[i] = db.getDialect().serialize(value, typeAdapter); - - } - - return new PreparedSql(sql, parameters); - - } - - @Override - public String toString() { - return sql; - } - } - - /** - * IndexedArgument holds cached information about how to process an method - * argument by it's index in the method arguments array. - *

- * An argument may be passed-through, might be bound to a bean property, - * might be transformed with a type adapter, or a combination of these. - *

- */ - private class IndexedArgument { - final int index; - final Class> typeAdapter; - final Method method; - final Field field; - - IndexedArgument(int index, Class> typeAdapter) { - this.index = index; - this.typeAdapter = typeAdapter; - this.method = null; - this.field = null; - } - - IndexedArgument(int methodArgIndex, Method method) { - this.index = methodArgIndex; - this.typeAdapter = null; - this.method = method; - this.field = null; - } - - IndexedArgument(int methodArgIndex, Field field) { - this.index = methodArgIndex; - this.typeAdapter = null; - this.method = null; - this.field = field; - } - - @Override - public String toString() { - - String accessor; - if (method != null) { - accessor = "M:" + method.getDeclaringClass().getSimpleName() + "." + method.getName(); - } else if (field != null) { - accessor = "F:" + field.getDeclaringClass().getSimpleName() + "." + field.getName(); - } else { - accessor = "A:arg"; - } - - return index + ":" + accessor + (typeAdapter == null ? "" : (":" + typeAdapter.getSimpleName())); - } - - } + return index + ":" + accessor + (typeAdapter == null ? "" : (":" + typeAdapter.getSimpleName())); + } + + } } diff --git a/src/main/java/com/iciql/DaoStatementProvider.java b/src/main/java/com/iciql/DaoStatementProvider.java index 7e7522a..673638c 100644 --- a/src/main/java/com/iciql/DaoStatementProvider.java +++ b/src/main/java/com/iciql/DaoStatementProvider.java @@ -21,16 +21,15 @@ import com.iciql.Iciql.Mode; * Defines the interface for retrieving externalized DAO statements. * * @author James Moger - * */ public interface DaoStatementProvider { - /** - * Returns the statement associated with the id. - * - * @param idOrStatement - * @param mode - * @return the statement - */ - String getStatement(String idOrStatement, Mode mode); + /** + * Returns the statement associated with the id. + * + * @param idOrStatement + * @param mode + * @return the statement + */ + String getStatement(String idOrStatement, Mode mode); } diff --git a/src/main/java/com/iciql/Db.java b/src/main/java/com/iciql/Db.java index d0756bc..24396a4 100644 --- a/src/main/java/com/iciql/Db.java +++ b/src/main/java/com/iciql/Db.java @@ -19,6 +19,18 @@ package com.iciql; +import com.iciql.DbUpgrader.DefaultDbUpgrader; +import com.iciql.Iciql.IQTable; +import com.iciql.Iciql.IQVersion; +import com.iciql.Iciql.IQView; +import com.iciql.Iciql.Mode; +import com.iciql.util.IciqlLogger; +import com.iciql.util.JdbcUtils; +import com.iciql.util.StringUtils; +import com.iciql.util.Utils; +import com.iciql.util.WeakIdentityHashMap; + +import javax.sql.DataSource; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; @@ -35,851 +47,827 @@ import java.util.List; import java.util.Map; import java.util.Set; -import javax.sql.DataSource; - -import com.iciql.DbUpgrader.DefaultDbUpgrader; -import com.iciql.Iciql.IQTable; -import com.iciql.Iciql.IQVersion; -import com.iciql.Iciql.IQView; -import com.iciql.Iciql.Mode; -import com.iciql.util.IciqlLogger; -import com.iciql.util.JdbcUtils; -import com.iciql.util.StringUtils; -import com.iciql.util.Utils; -import com.iciql.util.WeakIdentityHashMap; - /** * This class represents a connection to a database. */ public class Db implements AutoCloseable { - /** - * This map It holds unique tokens that are generated by functions such as - * Function.sum(..) in "db.from(p).select(Function.sum(p.unitPrice))". It - * doesn't actually hold column tokens, as those are bound to the query - * itself. - */ - private static final Map TOKENS; - - private static final Map> DIALECTS; - - private final Connection conn; - private final Mode mode; - private final Map, TableDefinition> classMap = Collections - .synchronizedMap(new HashMap, TableDefinition>()); - private final SQLDialect dialect; - private DbUpgrader dbUpgrader = new DefaultDbUpgrader(); - private final Set> upgradeChecked = Collections.synchronizedSet(new HashSet>()); - - private boolean skipCreate; - private boolean autoSavePoint = true; - private DaoStatementProvider daoStatementProvider; - - static { - TOKENS = Collections.synchronizedMap(new WeakIdentityHashMap()); - DIALECTS = Collections.synchronizedMap(new HashMap>()); - // can register by... - // 1. Connection class name - // 2. DatabaseMetaData.getDatabaseProductName() - DIALECTS.put("Apache Derby", SQLDialectDerby.class); - DIALECTS.put("H2", SQLDialectH2.class); - DIALECTS.put("HSQL Database Engine", SQLDialectHSQL.class); - DIALECTS.put("MySQL", SQLDialectMySQL.class); - DIALECTS.put("PostgreSQL", SQLDialectPostgreSQL.class); - DIALECTS.put("Microsoft SQL Server", SQLDialectMSSQL.class); - DIALECTS.put("SQLite", SQLDialectSQLite.class); - } - - private Db(Connection conn, Mode mode) { - this.conn = conn; - this.mode = mode; - String databaseName = null; - try { - DatabaseMetaData data = conn.getMetaData(); - databaseName = data.getDatabaseProductName(); - } catch (SQLException s) { - throw new IciqlException(s, "failed to retrieve database metadata!"); - } - dialect = getDialect(databaseName, conn.getClass().getName()); - dialect.configureDialect(this); - daoStatementProvider = new NoExternalDaoStatements(); - } - - /** - * Register a new/custom dialect class. You can use this method to replace - * any existing dialect or to add a new one. - * - * @param token - * the fully qualified name of the connection class or the - * expected result of DatabaseMetaData.getDatabaseProductName() - * @param dialectClass - * the dialect class to register - */ - public static void registerDialect(String token, Class dialectClass) { - DIALECTS.put(token, dialectClass); - } - - SQLDialect getDialect(String databaseName, String className) { - Class dialectClass = null; - if (DIALECTS.containsKey(className)) { - // dialect registered by connection class name - dialectClass = DIALECTS.get(className); - } else if (DIALECTS.containsKey(databaseName)) { - // dialect registered by database name - dialectClass = DIALECTS.get(databaseName); - } else { - // did not find a match, use default - dialectClass = SQLDialectDefault.class; - } - return instance(dialectClass); - } - - static X registerToken(X x, Token token) { - TOKENS.put(x, token); - return x; - } - - static boolean isToken(X x) { - return TOKENS.containsKey(x); - } - - static Token getToken(Object x) { - return TOKENS.get(x); - } - - static T instance(Class clazz) { - try { - return clazz.newInstance(); - } catch (Exception e) { - throw new IciqlException(e); - } - } - - public static Db open(String url) { - return open(url, Mode.PROD); - } - - public static Db open(String url, Mode mode) { - try { - Connection conn = JdbcUtils.getConnection(null, url, null, null); - return new Db(conn, mode); - } catch (SQLException e) { - throw new IciqlException(e); - } - } - - public static Db open(String url, String user, String password) { - return open(url, user, password, Mode.PROD); - } - - public static Db open(String url, String user, String password, Mode mode) { - try { - Connection conn = JdbcUtils.getConnection(null, url, user, password); - return new Db(conn, mode); - } catch (SQLException e) { - throw new IciqlException(e); - } - } - - public static Db open(String url, String user, char[] password) { - return open(url, user, password, Mode.PROD); - } - - public static Db open(String url, String user, char[] password, Mode mode) { - try { - Connection conn = JdbcUtils.getConnection(null, url, user, password == null ? null : new String(password)); - return new Db(conn, mode); - } catch (SQLException e) { - throw new IciqlException(e); - } - } - - public static Db open(DataSource ds) { - return open(ds, Mode.PROD); - } - - /** - * Create a new database instance using a data source. This method is fast, - * so that you can always call open() / close() on usage. - * - * @param ds - * the data source - * @param mode - * the runtime mode - * @return the database instance. - */ - public static Db open(DataSource ds, Mode mode) { - try { - return new Db(ds.getConnection(), mode); - } catch (SQLException e) { - throw new IciqlException(e); - } - } - - public static Db open(Connection conn) { - return open(conn, Mode.PROD); - } - - public static Db open(Connection conn, Mode mode) { - return new Db(conn, mode); - } - - /** - * Returns the Iciql runtime mode. - * - * @return the runtime mode - */ - public Mode getMode() { - return mode; - } - - /** - * Returns a new DAO instance for the specified class. - * - * @param daoClass - * @return - * @throws Exception - */ - @SuppressWarnings("resource") - public X open(Class daoClass) { - return new DaoProxy(this, daoClass).build(); - } - - /** - * Returns the DAO statement provider. - * - * @return the DAO statement provider - */ - public DaoStatementProvider getDaoStatementProvider() { - return daoStatementProvider; - } - - /** - * Sets the DAO statement provider. - * - * @param statementProvider - */ - public void setDaoStatementProvider(DaoStatementProvider statementProvider) { - if (statementProvider == null) { - throw new IciqlException("You must provide a valid {0} instance!", - DaoStatementProvider.class.getSimpleName()); - } - - this.daoStatementProvider = statementProvider; - } - - /** - * Convenience function to avoid import statements in application code. - */ - public void activateConsoleLogger() { - IciqlLogger.activateConsoleLogger(); - } - - /** - * Convenience function to avoid import statements in application code. - */ - public void deactivateConsoleLogger() { - IciqlLogger.deactivateConsoleLogger(); - } - - public boolean insert(T t) { - Class clazz = t.getClass(); - long rc = define(clazz).createIfRequired(this).insert(this, t, false); - if (rc == 0) { - throw new IciqlException("Failed to insert {0}. Affected rowcount == 0.", t); - } - return rc == 1; - } - - public long insertAndGetKey(T t) { - Class clazz = t.getClass(); - return define(clazz).createIfRequired(this).insert(this, t, true); - } - - /** - * Upsert INSERTS if the record does not exist or UPDATES the record if it - * does exist. Not all databases support MERGE and the syntax varies with - * the database. - * - * If the database does not support a MERGE or INSERT OR REPLACE INTO syntax - * the dialect can try to simulate a merge by implementing: - *

- * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0) - *

- * iciql will check the affected row count returned by the internal merge - * method and if the affected row count = 0, it will issue an update. - *

- * See the Derby dialect for an implementation of this technique. - *

- * If the dialect does not support merge an IciqlException will be thrown. - * - * @param t - */ - public void upsert(T t) { - Class clazz = t.getClass(); - TableDefinition def = define(clazz).createIfRequired(this); - int rc = def.merge(this, t); - if (rc == 0) { - rc = def.update(this, t); - } - if (rc == 0) { - throw new IciqlException("upsert failed"); - } - } - - /** - * Merge INSERTS if the record does not exist or UPDATES the record if it - * does exist. Not all databases support MERGE and the syntax varies with - * the database. - * - * If the database does not support a MERGE or INSERT OR REPLACE INTO syntax - * the dialect can try to simulate a merge by implementing: - *

- * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0) - *

- * iciql will check the affected row count returned by the internal merge - * method and if the affected row count = 0, it will issue an update. - *

- * See the Derby dialect for an implementation of this technique. - *

- * If the dialect does not support merge an IciqlException will be thrown. - * - * @param t - */ - public void merge(T t) { - upsert(t); - } - - public boolean update(T t) { - Class clazz = t.getClass(); - return define(clazz).createIfRequired(this).update(this, t) == 1; - } - - public boolean delete(T t) { - Class clazz = t.getClass(); - return define(clazz).createIfRequired(this).delete(this, t) == 1; - } - - public Query from(T alias) { - Class clazz = alias.getClass(); - define(clazz).createIfRequired(this); - return Query.from(this, alias); - } - - @SuppressWarnings("unchecked") - public boolean dropTable(Class modelClass) { - TableDefinition def = (TableDefinition) define(modelClass); - SQLStatement stat = new SQLStatement(this); - getDialect().prepareDropTable(stat, def); - IciqlLogger.drop(stat.getSQL()); - int rc = 0; - try { - rc = stat.executeUpdate(); - } catch (IciqlException e) { - if (e.getIciqlCode() != IciqlException.CODE_OBJECT_NOT_FOUND) { - throw e; - } - } - // remove this model class from the table definition cache - classMap.remove(modelClass); - // remove this model class from the upgrade checked cache - upgradeChecked.remove(modelClass); - return rc == 1; - } - - @SuppressWarnings("unchecked") - public boolean dropView(Class modelClass) { - TableDefinition def = (TableDefinition) define(modelClass); - SQLStatement stat = new SQLStatement(this); - getDialect().prepareDropView(stat, def); - IciqlLogger.drop(stat.getSQL()); - int rc = 0; - try { - rc = stat.executeUpdate(); - } catch (IciqlException e) { - if (e.getIciqlCode() != IciqlException.CODE_OBJECT_NOT_FOUND) { - throw e; - } - } - // remove this model class from the table definition cache - classMap.remove(modelClass); - // remove this model class from the upgrade checked cache - upgradeChecked.remove(modelClass); - return rc == 1; - } - - public List buildObjects(Class modelClass, ResultSet rs) { - return buildObjects(modelClass, false, rs); - } - - @SuppressWarnings("unchecked") - public List buildObjects(Class modelClass, boolean wildcardSelect, ResultSet rs) { - List result = new ArrayList(); - TableDefinition def = (TableDefinition) define(modelClass); - try { - // SQLite returns pre-closed ResultSets for query results with 0 rows - if (!rs.isClosed()) { - int[] columns = def.mapColumns(dialect, wildcardSelect, rs); - while (rs.next()) { - T item = Utils.newObject(modelClass); - def.readRow(dialect, item, rs, columns); - result.add(item); - } - } - } catch (SQLException e) { - throw new IciqlException(e); - } - return result; - } - - Db upgradeDb() { - if (!upgradeChecked.contains(dbUpgrader.getClass())) { - // flag as checked immediately because calls are nested. - upgradeChecked.add(dbUpgrader.getClass()); - - IQVersion model = dbUpgrader.getClass().getAnnotation(IQVersion.class); - if (model.value() == 0) { - // try superclass - Class superClass = dbUpgrader.getClass().getSuperclass(); - if (superClass.isAnnotationPresent(IQVersion.class)) { - model = superClass.getAnnotation(IQVersion.class); - } - } - if (model.value() > 0) { - DbVersion v = new DbVersion(); - // (SCHEMA="" && TABLE="") == DATABASE - DbVersion dbVersion = from(v).where(v.schemaName).is("").and(v.tableName).is("") - .selectFirst(); - if (dbVersion == null) { - // database has no version registration, but model specifies - // version: insert DbVersion entry and return. - DbVersion newDb = new DbVersion(model.value()); - // database is an older version than the model - boolean success = dbUpgrader.upgradeDatabase(this, 0, newDb.version); - if (success) { - insert(newDb); - } - } else { - // database has a version registration: - // check to see if upgrade is required. - if ((model.value() > dbVersion.version) && (dbUpgrader != null)) { - // database is an older version than the model - boolean success = dbUpgrader.upgradeDatabase(this, dbVersion.version, model.value()); - if (success) { - dbVersion.version = model.value(); - update(dbVersion); - } - } - } - } - } - return this; - } - - void upgradeTable(TableDefinition model) { - if (!upgradeChecked.contains(model.getModelClass())) { - // flag is checked immediately because calls are nested - upgradeChecked.add(model.getModelClass()); - - if (model.tableVersion > 0) { - // table is using iciql version tracking. - DbVersion v = new DbVersion(); - String schema = StringUtils.isNullOrEmpty(model.schemaName) ? "" : model.schemaName; - DbVersion dbVersion = from(v).where(v.schemaName).is(schema).and(v.tableName) - .is(model.tableName).selectFirst(); - if (dbVersion == null) { - // table has no version registration, but model specifies - // version: insert DbVersion entry - DbVersion newTable = new DbVersion(model.tableVersion); - newTable.schemaName = schema; - newTable.tableName = model.tableName; - insert(newTable); - } else { - // table has a version registration: - // check if upgrade is required - if ((model.tableVersion > dbVersion.version) && (dbUpgrader != null)) { - // table is an older version than model - boolean success = dbUpgrader.upgradeTable(this, schema, model.tableName, - dbVersion.version, model.tableVersion); - if (success) { - dbVersion.version = model.tableVersion; - update(dbVersion); - } - } - } - } - } - } - - TableDefinition define(Class clazz) { - TableDefinition def = getTableDefinition(clazz); - if (def == null) { - upgradeDb(); - def = new TableDefinition(clazz); - def.mapFields(this); - classMap.put(clazz, def); - if (Iciql.class.isAssignableFrom(clazz)) { - T t = instance(clazz); - Iciql table = (Iciql) t; - Define.define(def, table); - } else if (clazz.isAnnotationPresent(IQTable.class)) { - // annotated classes skip the Define().define() static - // initializer - T t = instance(clazz); - def.mapObject(t); - } else if (clazz.isAnnotationPresent(IQView.class)) { - // annotated classes skip the Define().define() static - // initializer - T t = instance(clazz); - def.mapObject(t); - } - } - return def; - } - - boolean hasCreated(Class clazz) { - return upgradeChecked.contains(clazz); - } - - public synchronized void setDbUpgrader(DbUpgrader upgrader) { - if (!upgrader.getClass().isAnnotationPresent(IQVersion.class)) { - throw new IciqlException("DbUpgrader must be annotated with " + IQVersion.class.getSimpleName()); - } - this.dbUpgrader = upgrader; - upgradeChecked.clear(); - } - - public SQLDialect getDialect() { - return dialect; - } - - public Connection getConnection() { - return conn; - } - - @Override - public void close() { - try { - conn.close(); - } catch (Exception e) { - throw new IciqlException(e); - } - } - - public TestCondition test(A x) { - return new TestCondition(x); - } - - public void insertAll(List list) { - if (list.size() == 0) { - return; - } - Savepoint savepoint = null; - try { - Class clazz = list.get(0).getClass(); - TableDefinition def = define(clazz).createIfRequired(this); - savepoint = prepareSavepoint(); - for (T t : list) { - PreparedStatement ps = def.createInsertStatement(this, t, false); - int rc = ps.executeUpdate(); - if (rc == 0) { - throw new IciqlException("Failed to insert {0}. Affected rowcount == 0.", t); - } - } - commit(savepoint); - } catch (SQLException e) { - rollback(savepoint); - throw new IciqlException(e); - } catch (IciqlException e) { - rollback(savepoint); - throw e; - } - } - - public List insertAllAndGetKeys(List list) { - List identities = new ArrayList(); - if (list.size() == 0) { - return identities; - } - Savepoint savepoint = null; - try { - Class clazz = list.get(0).getClass(); - TableDefinition def = define(clazz).createIfRequired(this); - savepoint = prepareSavepoint(); - for (T t : list) { - long key = def.insert(this, t, true); - identities.add(key); - } - commit(savepoint); - } catch (IciqlException e) { - rollback(savepoint); - throw e; - } - return identities; - } - - public void updateAll(List list) { - if (list.size() == 0) { - return; - } - Savepoint savepoint = null; - try { - Class clazz = list.get(0).getClass(); - TableDefinition def = define(clazz).createIfRequired(this); - savepoint = prepareSavepoint(); - for (T t : list) { - def.update(this, t); - } - commit(savepoint); - } catch (IciqlException e) { - rollback(savepoint); - throw e; - } - } - - public void deleteAll(List list) { - if (list.size() == 0) { - return; - } - Savepoint savepoint = null; - try { - Class clazz = list.get(0).getClass(); - TableDefinition def = define(clazz).createIfRequired(this); - savepoint = prepareSavepoint(); - for (T t : list) { - def.delete(this, t); - } - commit(savepoint); - } catch (IciqlException e) { - rollback(savepoint); - throw e; - } - } - - PreparedStatement prepare(String sql, boolean returnGeneratedKeys) { - IciqlException.checkUnmappedField(sql); - try { - if (returnGeneratedKeys) { - return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); - } - return conn.prepareStatement(sql); - } catch (SQLException e) { - throw IciqlException.fromSQL(sql, e); - } - } - - Savepoint prepareSavepoint() { - // don't change auto-commit mode. - // don't create save point. - if (!autoSavePoint || !dialect.supportsSavePoints()) { - return null; - } - // create a savepoint - Savepoint savepoint = null; - try { - conn.setAutoCommit(false); - savepoint = conn.setSavepoint(); - } catch (SQLFeatureNotSupportedException e) { - // jdbc driver does not support save points - } catch (SQLException e) { - throw new IciqlException(e, "Could not create save point"); - } - return savepoint; - } - - void commit(Savepoint savepoint) { - if (savepoint != null) { - try { - conn.commit(); - conn.setAutoCommit(true); - } catch (SQLException e) { - throw new IciqlException(e, "Failed to commit pending transactions"); - } - } - } - - void rollback(Savepoint savepoint) { - if (savepoint != null) { - try { - conn.rollback(savepoint); - conn.setAutoCommit(true); - } catch (SQLException s) { - throw new IciqlException(s, "Failed to rollback transactions"); - } - } - } - - @SuppressWarnings("unchecked") - TableDefinition getTableDefinition(Class clazz) { - return (TableDefinition) classMap.get(clazz); - } - - /** - * Run a SQL query directly against the database. - * - * Be sure to close the ResultSet with - * - *

-	 * JdbcUtils.closeSilently(rs, true);
-	 * 
- * - * @param sql - * the SQL statement - * @param args - * optional object arguments for x=? tokens in query - * @return the result set - */ - public ResultSet executeQuery(String sql, List args) { - return executeQuery(sql, args.toArray()); - } - - /** - * Run a SQL query directly against the database. - * - * Be sure to close the ResultSet with - * - *
-	 * JdbcUtils.closeSilently(rs, true);
-	 * 
- * - * @param sql - * the SQL statement - * @param args - * optional object arguments for x=? tokens in query - * @return the result set - */ - public ResultSet executeQuery(String sql, Object... args) { - try { - if (args == null || args.length == 0) { - return conn.createStatement().executeQuery(sql); - } else { - PreparedStatement stat = conn.prepareStatement(sql); - int i = 1; - for (Object arg : args) { - stat.setObject(i++, arg); - } - return stat.executeQuery(); - } - } catch (SQLException e) { - throw new IciqlException(e); - } - } - - /** - * Run a SQL query directly against the database and map the results to the - * model class. - * - * @param modelClass - * the model class to bind the query ResultSet rows into. - * @param sql - * the SQL statement - * @return the result set - */ - public List executeQuery(Class modelClass, String sql, List args) { - return executeQuery(modelClass, sql, args.toArray()); - } - - /** - * Run a SQL query directly against the database and map the results to the - * model class. - * - * @param modelClass - * the model class to bind the query ResultSet rows into. - * @param sql - * the SQL statement - * @return the result set - */ - public List executeQuery(Class modelClass, String sql, Object... args) { - ResultSet rs = null; - try { - if (args == null || args.length == 0) { - rs = conn.createStatement().executeQuery(sql); - } else { - PreparedStatement stat = conn.prepareStatement(sql); - int i = 1; - for (Object arg : args) { - stat.setObject(i++, arg); - } - rs = stat.executeQuery(); - } - boolean wildcardSelect = sql.toLowerCase().matches("select .*\\*.+"); - return buildObjects(modelClass, wildcardSelect, rs); - } catch (SQLException e) { - throw new IciqlException(e); - } finally { - JdbcUtils.closeSilently(rs, true); - } - } - - /** - * Run a SQL statement directly against the database. - * - * @param sql - * the SQL statement - * @return the update count - */ - public int executeUpdate(String sql, Object... args) { - Statement stat = null; - try { - int updateCount; - if (args == null || args.length == 0) { - stat = conn.createStatement(); - updateCount = stat.executeUpdate(sql); - } else { - PreparedStatement ps = conn.prepareStatement(sql); - int i = 1; - for (Object arg : args) { - ps.setObject(i++, arg); - } - updateCount = ps.executeUpdate(); - stat = ps; - } - return updateCount; - } catch (SQLException e) { - throw new IciqlException(e); - } finally { - JdbcUtils.closeSilently(stat); - } - } - - /** - * Allow to enable/disable globally createIfRequired in TableDefinition. - * For advanced user wanting to gain full control of transactions. - * Default value is false. - * @param skipCreate - */ - public void setSkipCreate(boolean skipCreate) { - this.skipCreate = skipCreate; - } - - public boolean getSkipCreate() { - return this.skipCreate; - } - - /** - * Allow to enable/disable usage of save point. - * For advanced user wanting to gain full control of transactions. - * Default value is false. - * @param autoSavePoint - */ - public void setAutoSavePoint(boolean autoSavePoint) { - this.autoSavePoint = autoSavePoint; - } - - public boolean getAutoSavePoint() { - return this.autoSavePoint; - } - - /** - * Default DAO statement provider. - */ - class NoExternalDaoStatements implements DaoStatementProvider { - - @Override - public String getStatement(String idOrStatement, Mode mode) { - return idOrStatement; - } - - } + /** + * This map It holds unique tokens that are generated by functions such as + * Function.sum(..) in "db.from(p).select(Function.sum(p.unitPrice))". It + * doesn't actually hold column tokens, as those are bound to the query + * itself. + */ + private static final Map TOKENS; + + private static final Map> DIALECTS; + + private final Connection conn; + private final Mode mode; + private final Map, TableDefinition> classMap = Collections + .synchronizedMap(new HashMap, TableDefinition>()); + private final SQLDialect dialect; + private DbUpgrader dbUpgrader = new DefaultDbUpgrader(); + private final Set> upgradeChecked = Collections.synchronizedSet(new HashSet>()); + + private boolean skipCreate; + private boolean autoSavePoint = true; + private DaoStatementProvider daoStatementProvider; + + static { + TOKENS = Collections.synchronizedMap(new WeakIdentityHashMap()); + DIALECTS = Collections.synchronizedMap(new HashMap>()); + // can register by... + // 1. Connection class name + // 2. DatabaseMetaData.getDatabaseProductName() + DIALECTS.put("Apache Derby", SQLDialectDerby.class); + DIALECTS.put("H2", SQLDialectH2.class); + DIALECTS.put("HSQL Database Engine", SQLDialectHSQL.class); + DIALECTS.put("MySQL", SQLDialectMySQL.class); + DIALECTS.put("PostgreSQL", SQLDialectPostgreSQL.class); + DIALECTS.put("Microsoft SQL Server", SQLDialectMSSQL.class); + DIALECTS.put("SQLite", SQLDialectSQLite.class); + } + + private Db(Connection conn, Mode mode) { + this.conn = conn; + this.mode = mode; + String databaseName = null; + try { + DatabaseMetaData data = conn.getMetaData(); + databaseName = data.getDatabaseProductName(); + } catch (SQLException s) { + throw new IciqlException(s, "failed to retrieve database metadata!"); + } + dialect = getDialect(databaseName, conn.getClass().getName()); + dialect.configureDialect(this); + daoStatementProvider = new NoExternalDaoStatements(); + } + + /** + * Register a new/custom dialect class. You can use this method to replace + * any existing dialect or to add a new one. + * + * @param token the fully qualified name of the connection class or the + * expected result of DatabaseMetaData.getDatabaseProductName() + * @param dialectClass the dialect class to register + */ + public static void registerDialect(String token, Class dialectClass) { + DIALECTS.put(token, dialectClass); + } + + SQLDialect getDialect(String databaseName, String className) { + Class dialectClass = null; + if (DIALECTS.containsKey(className)) { + // dialect registered by connection class name + dialectClass = DIALECTS.get(className); + } else if (DIALECTS.containsKey(databaseName)) { + // dialect registered by database name + dialectClass = DIALECTS.get(databaseName); + } else { + // did not find a match, use default + dialectClass = SQLDialectDefault.class; + } + return instance(dialectClass); + } + + static X registerToken(X x, Token token) { + TOKENS.put(x, token); + return x; + } + + static boolean isToken(X x) { + return TOKENS.containsKey(x); + } + + static Token getToken(Object x) { + return TOKENS.get(x); + } + + static T instance(Class clazz) { + try { + return clazz.newInstance(); + } catch (Exception e) { + throw new IciqlException(e); + } + } + + public static Db open(String url) { + return open(url, Mode.PROD); + } + + public static Db open(String url, Mode mode) { + try { + Connection conn = JdbcUtils.getConnection(null, url, null, null); + return new Db(conn, mode); + } catch (SQLException e) { + throw new IciqlException(e); + } + } + + public static Db open(String url, String user, String password) { + return open(url, user, password, Mode.PROD); + } + + public static Db open(String url, String user, String password, Mode mode) { + try { + Connection conn = JdbcUtils.getConnection(null, url, user, password); + return new Db(conn, mode); + } catch (SQLException e) { + throw new IciqlException(e); + } + } + + public static Db open(String url, String user, char[] password) { + return open(url, user, password, Mode.PROD); + } + + public static Db open(String url, String user, char[] password, Mode mode) { + try { + Connection conn = JdbcUtils.getConnection(null, url, user, password == null ? null : new String(password)); + return new Db(conn, mode); + } catch (SQLException e) { + throw new IciqlException(e); + } + } + + public static Db open(DataSource ds) { + return open(ds, Mode.PROD); + } + + /** + * Create a new database instance using a data source. This method is fast, + * so that you can always call open() / close() on usage. + * + * @param ds the data source + * @param mode the runtime mode + * @return the database instance. + */ + public static Db open(DataSource ds, Mode mode) { + try { + return new Db(ds.getConnection(), mode); + } catch (SQLException e) { + throw new IciqlException(e); + } + } + + public static Db open(Connection conn) { + return open(conn, Mode.PROD); + } + + public static Db open(Connection conn, Mode mode) { + return new Db(conn, mode); + } + + /** + * Returns the Iciql runtime mode. + * + * @return the runtime mode + */ + public Mode getMode() { + return mode; + } + + /** + * Returns a new DAO instance for the specified class. + * + * @param daoClass + * @return + * @throws Exception + */ + @SuppressWarnings("resource") + public X open(Class daoClass) { + return new DaoProxy(this, daoClass).build(); + } + + /** + * Returns the DAO statement provider. + * + * @return the DAO statement provider + */ + public DaoStatementProvider getDaoStatementProvider() { + return daoStatementProvider; + } + + /** + * Sets the DAO statement provider. + * + * @param statementProvider + */ + public void setDaoStatementProvider(DaoStatementProvider statementProvider) { + if (statementProvider == null) { + throw new IciqlException("You must provide a valid {0} instance!", + DaoStatementProvider.class.getSimpleName()); + } + + this.daoStatementProvider = statementProvider; + } + + /** + * Convenience function to avoid import statements in application code. + */ + public void activateConsoleLogger() { + IciqlLogger.activateConsoleLogger(); + } + + /** + * Convenience function to avoid import statements in application code. + */ + public void deactivateConsoleLogger() { + IciqlLogger.deactivateConsoleLogger(); + } + + public boolean insert(T t) { + Class clazz = t.getClass(); + long rc = define(clazz).createIfRequired(this).insert(this, t, false); + if (rc == 0) { + throw new IciqlException("Failed to insert {0}. Affected rowcount == 0.", t); + } + return rc == 1; + } + + public long insertAndGetKey(T t) { + Class clazz = t.getClass(); + return define(clazz).createIfRequired(this).insert(this, t, true); + } + + /** + * Upsert INSERTS if the record does not exist or UPDATES the record if it + * does exist. Not all databases support MERGE and the syntax varies with + * the database. + *

+ * If the database does not support a MERGE or INSERT OR REPLACE INTO syntax + * the dialect can try to simulate a merge by implementing: + *

+ * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0) + *

+ * iciql will check the affected row count returned by the internal merge + * method and if the affected row count = 0, it will issue an update. + *

+ * See the Derby dialect for an implementation of this technique. + *

+ * If the dialect does not support merge an IciqlException will be thrown. + * + * @param t + */ + public void upsert(T t) { + Class clazz = t.getClass(); + TableDefinition def = define(clazz).createIfRequired(this); + int rc = def.merge(this, t); + if (rc == 0) { + rc = def.update(this, t); + } + if (rc == 0) { + throw new IciqlException("upsert failed"); + } + } + + /** + * Merge INSERTS if the record does not exist or UPDATES the record if it + * does exist. Not all databases support MERGE and the syntax varies with + * the database. + *

+ * If the database does not support a MERGE or INSERT OR REPLACE INTO syntax + * the dialect can try to simulate a merge by implementing: + *

+ * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0) + *

+ * iciql will check the affected row count returned by the internal merge + * method and if the affected row count = 0, it will issue an update. + *

+ * See the Derby dialect for an implementation of this technique. + *

+ * If the dialect does not support merge an IciqlException will be thrown. + * + * @param t + */ + public void merge(T t) { + upsert(t); + } + + public boolean update(T t) { + Class clazz = t.getClass(); + return define(clazz).createIfRequired(this).update(this, t) == 1; + } + + public boolean delete(T t) { + Class clazz = t.getClass(); + return define(clazz).createIfRequired(this).delete(this, t) == 1; + } + + public Query from(T alias) { + Class clazz = alias.getClass(); + define(clazz).createIfRequired(this); + return Query.from(this, alias); + } + + @SuppressWarnings("unchecked") + public boolean dropTable(Class modelClass) { + TableDefinition def = (TableDefinition) define(modelClass); + SQLStatement stat = new SQLStatement(this); + getDialect().prepareDropTable(stat, def); + IciqlLogger.drop(stat.getSQL()); + int rc = 0; + try { + rc = stat.executeUpdate(); + } catch (IciqlException e) { + if (e.getIciqlCode() != IciqlException.CODE_OBJECT_NOT_FOUND) { + throw e; + } + } + // remove this model class from the table definition cache + classMap.remove(modelClass); + // remove this model class from the upgrade checked cache + upgradeChecked.remove(modelClass); + return rc == 1; + } + + @SuppressWarnings("unchecked") + public boolean dropView(Class modelClass) { + TableDefinition def = (TableDefinition) define(modelClass); + SQLStatement stat = new SQLStatement(this); + getDialect().prepareDropView(stat, def); + IciqlLogger.drop(stat.getSQL()); + int rc = 0; + try { + rc = stat.executeUpdate(); + } catch (IciqlException e) { + if (e.getIciqlCode() != IciqlException.CODE_OBJECT_NOT_FOUND) { + throw e; + } + } + // remove this model class from the table definition cache + classMap.remove(modelClass); + // remove this model class from the upgrade checked cache + upgradeChecked.remove(modelClass); + return rc == 1; + } + + public List buildObjects(Class modelClass, ResultSet rs) { + return buildObjects(modelClass, false, rs); + } + + @SuppressWarnings("unchecked") + public List buildObjects(Class modelClass, boolean wildcardSelect, ResultSet rs) { + List result = new ArrayList(); + TableDefinition def = (TableDefinition) define(modelClass); + try { + // SQLite returns pre-closed ResultSets for query results with 0 rows + if (!rs.isClosed()) { + int[] columns = def.mapColumns(dialect, wildcardSelect, rs); + while (rs.next()) { + T item = Utils.newObject(modelClass); + def.readRow(dialect, item, rs, columns); + result.add(item); + } + } + } catch (SQLException e) { + throw new IciqlException(e); + } + return result; + } + + Db upgradeDb() { + if (!upgradeChecked.contains(dbUpgrader.getClass())) { + // flag as checked immediately because calls are nested. + upgradeChecked.add(dbUpgrader.getClass()); + + IQVersion model = dbUpgrader.getClass().getAnnotation(IQVersion.class); + if (model.value() == 0) { + // try superclass + Class superClass = dbUpgrader.getClass().getSuperclass(); + if (superClass.isAnnotationPresent(IQVersion.class)) { + model = superClass.getAnnotation(IQVersion.class); + } + } + if (model.value() > 0) { + DbVersion v = new DbVersion(); + // (SCHEMA="" && TABLE="") == DATABASE + DbVersion dbVersion = from(v).where(v.schemaName).is("").and(v.tableName).is("") + .selectFirst(); + if (dbVersion == null) { + // database has no version registration, but model specifies + // version: insert DbVersion entry and return. + DbVersion newDb = new DbVersion(model.value()); + // database is an older version than the model + boolean success = dbUpgrader.upgradeDatabase(this, 0, newDb.version); + if (success) { + insert(newDb); + } + } else { + // database has a version registration: + // check to see if upgrade is required. + if ((model.value() > dbVersion.version) && (dbUpgrader != null)) { + // database is an older version than the model + boolean success = dbUpgrader.upgradeDatabase(this, dbVersion.version, model.value()); + if (success) { + dbVersion.version = model.value(); + update(dbVersion); + } + } + } + } + } + return this; + } + + void upgradeTable(TableDefinition model) { + if (!upgradeChecked.contains(model.getModelClass())) { + // flag is checked immediately because calls are nested + upgradeChecked.add(model.getModelClass()); + + if (model.tableVersion > 0) { + // table is using iciql version tracking. + DbVersion v = new DbVersion(); + String schema = StringUtils.isNullOrEmpty(model.schemaName) ? "" : model.schemaName; + DbVersion dbVersion = from(v).where(v.schemaName).is(schema).and(v.tableName) + .is(model.tableName).selectFirst(); + if (dbVersion == null) { + // table has no version registration, but model specifies + // version: insert DbVersion entry + DbVersion newTable = new DbVersion(model.tableVersion); + newTable.schemaName = schema; + newTable.tableName = model.tableName; + insert(newTable); + } else { + // table has a version registration: + // check if upgrade is required + if ((model.tableVersion > dbVersion.version) && (dbUpgrader != null)) { + // table is an older version than model + boolean success = dbUpgrader.upgradeTable(this, schema, model.tableName, + dbVersion.version, model.tableVersion); + if (success) { + dbVersion.version = model.tableVersion; + update(dbVersion); + } + } + } + } + } + } + + TableDefinition define(Class clazz) { + TableDefinition def = getTableDefinition(clazz); + if (def == null) { + upgradeDb(); + def = new TableDefinition(clazz); + def.mapFields(this); + classMap.put(clazz, def); + if (Iciql.class.isAssignableFrom(clazz)) { + T t = instance(clazz); + Iciql table = (Iciql) t; + Define.define(def, table); + } else if (clazz.isAnnotationPresent(IQTable.class)) { + // annotated classes skip the Define().define() static + // initializer + T t = instance(clazz); + def.mapObject(t); + } else if (clazz.isAnnotationPresent(IQView.class)) { + // annotated classes skip the Define().define() static + // initializer + T t = instance(clazz); + def.mapObject(t); + } + } + return def; + } + + boolean hasCreated(Class clazz) { + return upgradeChecked.contains(clazz); + } + + public synchronized void setDbUpgrader(DbUpgrader upgrader) { + if (!upgrader.getClass().isAnnotationPresent(IQVersion.class)) { + throw new IciqlException("DbUpgrader must be annotated with " + IQVersion.class.getSimpleName()); + } + this.dbUpgrader = upgrader; + upgradeChecked.clear(); + } + + public SQLDialect getDialect() { + return dialect; + } + + public Connection getConnection() { + return conn; + } + + @Override + public void close() { + try { + conn.close(); + } catch (Exception e) { + throw new IciqlException(e); + } + } + + public TestCondition test(A x) { + return new TestCondition(x); + } + + public void insertAll(List list) { + if (list.size() == 0) { + return; + } + Savepoint savepoint = null; + try { + Class clazz = list.get(0).getClass(); + TableDefinition def = define(clazz).createIfRequired(this); + savepoint = prepareSavepoint(); + for (T t : list) { + PreparedStatement ps = def.createInsertStatement(this, t, false); + int rc = ps.executeUpdate(); + if (rc == 0) { + throw new IciqlException("Failed to insert {0}. Affected rowcount == 0.", t); + } + } + commit(savepoint); + } catch (SQLException e) { + rollback(savepoint); + throw new IciqlException(e); + } catch (IciqlException e) { + rollback(savepoint); + throw e; + } + } + + public List insertAllAndGetKeys(List list) { + List identities = new ArrayList(); + if (list.size() == 0) { + return identities; + } + Savepoint savepoint = null; + try { + Class clazz = list.get(0).getClass(); + TableDefinition def = define(clazz).createIfRequired(this); + savepoint = prepareSavepoint(); + for (T t : list) { + long key = def.insert(this, t, true); + identities.add(key); + } + commit(savepoint); + } catch (IciqlException e) { + rollback(savepoint); + throw e; + } + return identities; + } + + public void updateAll(List list) { + if (list.size() == 0) { + return; + } + Savepoint savepoint = null; + try { + Class clazz = list.get(0).getClass(); + TableDefinition def = define(clazz).createIfRequired(this); + savepoint = prepareSavepoint(); + for (T t : list) { + def.update(this, t); + } + commit(savepoint); + } catch (IciqlException e) { + rollback(savepoint); + throw e; + } + } + + public void deleteAll(List list) { + if (list.size() == 0) { + return; + } + Savepoint savepoint = null; + try { + Class clazz = list.get(0).getClass(); + TableDefinition def = define(clazz).createIfRequired(this); + savepoint = prepareSavepoint(); + for (T t : list) { + def.delete(this, t); + } + commit(savepoint); + } catch (IciqlException e) { + rollback(savepoint); + throw e; + } + } + + PreparedStatement prepare(String sql, boolean returnGeneratedKeys) { + IciqlException.checkUnmappedField(sql); + try { + if (returnGeneratedKeys) { + return conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); + } + return conn.prepareStatement(sql); + } catch (SQLException e) { + throw IciqlException.fromSQL(sql, e); + } + } + + Savepoint prepareSavepoint() { + // don't change auto-commit mode. + // don't create save point. + if (!autoSavePoint || !dialect.supportsSavePoints()) { + return null; + } + // create a savepoint + Savepoint savepoint = null; + try { + conn.setAutoCommit(false); + savepoint = conn.setSavepoint(); + } catch (SQLFeatureNotSupportedException e) { + // jdbc driver does not support save points + } catch (SQLException e) { + throw new IciqlException(e, "Could not create save point"); + } + return savepoint; + } + + void commit(Savepoint savepoint) { + if (savepoint != null) { + try { + conn.commit(); + conn.setAutoCommit(true); + } catch (SQLException e) { + throw new IciqlException(e, "Failed to commit pending transactions"); + } + } + } + + void rollback(Savepoint savepoint) { + if (savepoint != null) { + try { + conn.rollback(savepoint); + conn.setAutoCommit(true); + } catch (SQLException s) { + throw new IciqlException(s, "Failed to rollback transactions"); + } + } + } + + @SuppressWarnings("unchecked") + TableDefinition getTableDefinition(Class clazz) { + return (TableDefinition) classMap.get(clazz); + } + + /** + * Run a SQL query directly against the database. + *

+ * Be sure to close the ResultSet with + *

+ *

+     * JdbcUtils.closeSilently(rs, true);
+     * 
+ * + * @param sql the SQL statement + * @param args optional object arguments for x=? tokens in query + * @return the result set + */ + public ResultSet executeQuery(String sql, List args) { + return executeQuery(sql, args.toArray()); + } + + /** + * Run a SQL query directly against the database. + *

+ * Be sure to close the ResultSet with + *

+ *

+     * JdbcUtils.closeSilently(rs, true);
+     * 
+ * + * @param sql the SQL statement + * @param args optional object arguments for x=? tokens in query + * @return the result set + */ + public ResultSet executeQuery(String sql, Object... args) { + try { + if (args == null || args.length == 0) { + return conn.createStatement().executeQuery(sql); + } else { + PreparedStatement stat = conn.prepareStatement(sql); + int i = 1; + for (Object arg : args) { + stat.setObject(i++, arg); + } + return stat.executeQuery(); + } + } catch (SQLException e) { + throw new IciqlException(e); + } + } + + /** + * Run a SQL query directly against the database and map the results to the + * model class. + * + * @param modelClass the model class to bind the query ResultSet rows into. + * @param sql the SQL statement + * @return the result set + */ + public List executeQuery(Class modelClass, String sql, List args) { + return executeQuery(modelClass, sql, args.toArray()); + } + + /** + * Run a SQL query directly against the database and map the results to the + * model class. + * + * @param modelClass the model class to bind the query ResultSet rows into. + * @param sql the SQL statement + * @return the result set + */ + public List executeQuery(Class modelClass, String sql, Object... args) { + ResultSet rs = null; + try { + if (args == null || args.length == 0) { + rs = conn.createStatement().executeQuery(sql); + } else { + PreparedStatement stat = conn.prepareStatement(sql); + int i = 1; + for (Object arg : args) { + stat.setObject(i++, arg); + } + rs = stat.executeQuery(); + } + boolean wildcardSelect = sql.toLowerCase().matches("select .*\\*.+"); + return buildObjects(modelClass, wildcardSelect, rs); + } catch (SQLException e) { + throw new IciqlException(e); + } finally { + JdbcUtils.closeSilently(rs, true); + } + } + + /** + * Run a SQL statement directly against the database. + * + * @param sql the SQL statement + * @return the update count + */ + public int executeUpdate(String sql, Object... args) { + Statement stat = null; + try { + int updateCount; + if (args == null || args.length == 0) { + stat = conn.createStatement(); + updateCount = stat.executeUpdate(sql); + } else { + PreparedStatement ps = conn.prepareStatement(sql); + int i = 1; + for (Object arg : args) { + ps.setObject(i++, arg); + } + updateCount = ps.executeUpdate(); + stat = ps; + } + return updateCount; + } catch (SQLException e) { + throw new IciqlException(e); + } finally { + JdbcUtils.closeSilently(stat); + } + } + + /** + * Allow to enable/disable globally createIfRequired in TableDefinition. + * For advanced user wanting to gain full control of transactions. + * Default value is false. + * + * @param skipCreate + */ + public void setSkipCreate(boolean skipCreate) { + this.skipCreate = skipCreate; + } + + public boolean getSkipCreate() { + return this.skipCreate; + } + + /** + * Allow to enable/disable usage of save point. + * For advanced user wanting to gain full control of transactions. + * Default value is false. + * + * @param autoSavePoint + */ + public void setAutoSavePoint(boolean autoSavePoint) { + this.autoSavePoint = autoSavePoint; + } + + public boolean getAutoSavePoint() { + return this.autoSavePoint; + } + + /** + * Default DAO statement provider. + */ + class NoExternalDaoStatements implements DaoStatementProvider { + + @Override + public String getStatement(String idOrStatement, Mode mode) { + return idOrStatement; + } + + } } diff --git a/src/main/java/com/iciql/DbInspector.java b/src/main/java/com/iciql/DbInspector.java index acaceea..62576e3 100644 --- a/src/main/java/com/iciql/DbInspector.java +++ b/src/main/java/com/iciql/DbInspector.java @@ -17,6 +17,11 @@ package com.iciql; +import com.iciql.Iciql.IQTable; +import com.iciql.util.JdbcUtils; +import com.iciql.util.StringUtils; +import com.iciql.util.Utils; + import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; @@ -24,11 +29,6 @@ import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; -import com.iciql.Iciql.IQTable; -import com.iciql.util.JdbcUtils; -import com.iciql.util.StringUtils; -import com.iciql.util.Utils; - /** * Class to inspect a model and a database for the purposes of model validation * and automatic model generation. This class finds the available schemas and @@ -36,169 +36,158 @@ import com.iciql.util.Utils; */ public class DbInspector { - private Db db; - private DatabaseMetaData metaData; - private Class dateTimeClass = java.util.Date.class; - - public DbInspector(Db db) { - this.db = db; - setPreferredDateTimeClass(db.getDialect().getDateTimeClass()); - } - - /** - * Set the preferred class to store date and time. Possible values are: - * java.util.Date (default) and java.sql.Timestamp. - * - * @param dateTimeClass - * the new class - */ - public void setPreferredDateTimeClass(Class dateTimeClass) { - this.dateTimeClass = dateTimeClass; - } - - /** - * Generates models class skeletons for schemas and tables. If the table - * name is undefined, models will be generated for every table within the - * specified schema. Additionally, if no schema is defined, models will be - * generated for all schemas and all tables. - * - * @param schema - * the schema name (optional) - * @param table - * the table name (optional) - * @param packageName - * the package name (optional) - * @param annotateSchema - * (includes schema name in annotation) - * @param trimStrings - * (trims strings to maxLength of column) - * @return a list of complete model classes as strings, each element a class - */ - public List generateModel(String schema, String table, String packageName, - boolean annotateSchema, boolean trimStrings) { - try { - List models = Utils.newArrayList(); - List tables = getTables(schema, table); - for (TableInspector t : tables) { - t.read(metaData); - String model = t.generateModel(packageName, annotateSchema, trimStrings); - models.add(model); - } - return models; - } catch (SQLException s) { - throw new IciqlException(s); - } - } - - /** - * Validates a model. - * - * @param model - * an instance of the model class - * @param throwOnError - * if errors should cause validation to fail - * @return a list of validation remarks - */ - public List validateModel(T model, boolean throwOnError) { - try { - TableInspector inspector = getTable(model); - inspector.read(metaData); - @SuppressWarnings("unchecked") - Class clazz = (Class) model.getClass(); - TableDefinition def = db.define(clazz); - return inspector.validate(def, throwOnError); - } catch (SQLException s) { - throw new IciqlException(s); - } - } - - private DatabaseMetaData getMetaData() throws SQLException { - if (metaData == null) { - metaData = db.getConnection().getMetaData(); - } - return metaData; - } - - /** - * Get the table in the database based on the model definition. - * - * @param model - * an instance of the model class - * @return the table inspector - */ - private TableInspector getTable(T model) throws SQLException { - @SuppressWarnings("unchecked") - Class clazz = (Class) model.getClass(); - TableDefinition def = db.define(clazz); - boolean forceUpperCase = getMetaData().storesUpperCaseIdentifiers(); - String schema = (forceUpperCase && def.schemaName != null) ? def.schemaName.toUpperCase() - : def.schemaName; - String table = forceUpperCase ? def.tableName.toUpperCase() : def.tableName; - List tables = getTables(schema, table); - return tables.get(0); - } - - /** - * Returns a list of tables. This method always returns at least one - * element. If no table is found, an exception is thrown. - * - * @param schema - * the schema name - * @param table - * the table name - * @return a list of table inspectors (always contains at least one element) - */ - private List getTables(String schema, String table) throws SQLException { - ResultSet rs = null; - try { - rs = getMetaData().getSchemas(); - ArrayList schemaList = Utils.newArrayList(); - while (rs.next()) { - schemaList.add(rs.getString("TABLE_SCHEM")); - } - JdbcUtils.closeSilently(rs); - - String iciqlTables = DbVersion.class.getAnnotation(IQTable.class).name(); - - List tables = Utils.newArrayList(); - if (schemaList.size() == 0) { - schemaList.add(null); - } - for (String s : schemaList) { - rs = getMetaData().getTables(null, s, null, new String[] { "TABLE" }); - while (rs.next()) { - String t = rs.getString("TABLE_NAME"); - if (t.charAt(0) == '"') { - t = t.substring(1); - } - if (t.charAt(t.length() - 1) == '"') { - t = t.substring(0, t.length() - 1); - } - if (!t.equalsIgnoreCase(iciqlTables)) { - tables.add(new TableInspector(s, t, dateTimeClass)); - } - } - } - - if (StringUtils.isNullOrEmpty(schema) && StringUtils.isNullOrEmpty(table)) { - // all schemas and tables - return tables; - } - // schema subset OR table subset OR exact match - List matches = Utils.newArrayList(); - for (TableInspector t : tables) { - if (t.matches(schema, table)) { - matches.add(t); - } - } - if (matches.size() == 0) { - throw new IciqlException(MessageFormat.format("Failed to find schema={0} table={1}", - schema == null ? "" : schema, table == null ? "" : table)); - } - return matches; - } finally { - JdbcUtils.closeSilently(rs); - } - } + private Db db; + private DatabaseMetaData metaData; + private Class dateTimeClass = java.util.Date.class; + + public DbInspector(Db db) { + this.db = db; + setPreferredDateTimeClass(db.getDialect().getDateTimeClass()); + } + + /** + * Set the preferred class to store date and time. Possible values are: + * java.util.Date (default) and java.sql.Timestamp. + * + * @param dateTimeClass the new class + */ + public void setPreferredDateTimeClass(Class dateTimeClass) { + this.dateTimeClass = dateTimeClass; + } + + /** + * Generates models class skeletons for schemas and tables. If the table + * name is undefined, models will be generated for every table within the + * specified schema. Additionally, if no schema is defined, models will be + * generated for all schemas and all tables. + * + * @param schema the schema name (optional) + * @param table the table name (optional) + * @param packageName the package name (optional) + * @param annotateSchema (includes schema name in annotation) + * @param trimStrings (trims strings to maxLength of column) + * @return a list of complete model classes as strings, each element a class + */ + public List generateModel(String schema, String table, String packageName, + boolean annotateSchema, boolean trimStrings) { + try { + List models = Utils.newArrayList(); + List tables = getTables(schema, table); + for (TableInspector t : tables) { + t.read(metaData); + String model = t.generateModel(packageName, annotateSchema, trimStrings); + models.add(model); + } + return models; + } catch (SQLException s) { + throw new IciqlException(s); + } + } + + /** + * Validates a model. + * + * @param model an instance of the model class + * @param throwOnError if errors should cause validation to fail + * @return a list of validation remarks + */ + public List validateModel(T model, boolean throwOnError) { + try { + TableInspector inspector = getTable(model); + inspector.read(metaData); + @SuppressWarnings("unchecked") + Class clazz = (Class) model.getClass(); + TableDefinition def = db.define(clazz); + return inspector.validate(def, throwOnError); + } catch (SQLException s) { + throw new IciqlException(s); + } + } + + private DatabaseMetaData getMetaData() throws SQLException { + if (metaData == null) { + metaData = db.getConnection().getMetaData(); + } + return metaData; + } + + /** + * Get the table in the database based on the model definition. + * + * @param model an instance of the model class + * @return the table inspector + */ + private TableInspector getTable(T model) throws SQLException { + @SuppressWarnings("unchecked") + Class clazz = (Class) model.getClass(); + TableDefinition def = db.define(clazz); + boolean forceUpperCase = getMetaData().storesUpperCaseIdentifiers(); + String schema = (forceUpperCase && def.schemaName != null) ? def.schemaName.toUpperCase() + : def.schemaName; + String table = forceUpperCase ? def.tableName.toUpperCase() : def.tableName; + List tables = getTables(schema, table); + return tables.get(0); + } + + /** + * Returns a list of tables. This method always returns at least one + * element. If no table is found, an exception is thrown. + * + * @param schema the schema name + * @param table the table name + * @return a list of table inspectors (always contains at least one element) + */ + private List getTables(String schema, String table) throws SQLException { + ResultSet rs = null; + try { + rs = getMetaData().getSchemas(); + ArrayList schemaList = Utils.newArrayList(); + while (rs.next()) { + schemaList.add(rs.getString("TABLE_SCHEM")); + } + JdbcUtils.closeSilently(rs); + + String iciqlTables = DbVersion.class.getAnnotation(IQTable.class).name(); + + List tables = Utils.newArrayList(); + if (schemaList.size() == 0) { + schemaList.add(null); + } + for (String s : schemaList) { + rs = getMetaData().getTables(null, s, null, new String[]{"TABLE"}); + while (rs.next()) { + String t = rs.getString("TABLE_NAME"); + if (t.charAt(0) == '"') { + t = t.substring(1); + } + if (t.charAt(t.length() - 1) == '"') { + t = t.substring(0, t.length() - 1); + } + if (!t.equalsIgnoreCase(iciqlTables)) { + tables.add(new TableInspector(s, t, dateTimeClass)); + } + } + } + + if (StringUtils.isNullOrEmpty(schema) && StringUtils.isNullOrEmpty(table)) { + // all schemas and tables + return tables; + } + // schema subset OR table subset OR exact match + List matches = Utils.newArrayList(); + for (TableInspector t : tables) { + if (t.matches(schema, table)) { + matches.add(t); + } + } + if (matches.size() == 0) { + throw new IciqlException(MessageFormat.format("Failed to find schema={0} table={1}", + schema == null ? "" : schema, table == null ? "" : table)); + } + return matches; + } finally { + JdbcUtils.closeSilently(rs); + } + } } diff --git a/src/main/java/com/iciql/DbUpgrader.java b/src/main/java/com/iciql/DbUpgrader.java index 1303f4e..1fd9284 100644 --- a/src/main/java/com/iciql/DbUpgrader.java +++ b/src/main/java/com/iciql/DbUpgrader.java @@ -27,55 +27,47 @@ import com.iciql.Iciql.IQVersion; */ public interface DbUpgrader { - /** - * Defines method interface to handle database upgrades. This method is only - * called if your DbUpgrader implementation is annotated with - * IQDatabase. - * - * @param db - * the database - * @param fromVersion - * the old version - * @param toVersion - * the new version - * @return true for successful upgrade. If the upgrade is successful, the - * version registry is automatically updated. - */ - boolean upgradeDatabase(Db db, int fromVersion, int toVersion); + /** + * Defines method interface to handle database upgrades. This method is only + * called if your DbUpgrader implementation is annotated with + * IQDatabase. + * + * @param db the database + * @param fromVersion the old version + * @param toVersion the new version + * @return true for successful upgrade. If the upgrade is successful, the + * version registry is automatically updated. + */ + boolean upgradeDatabase(Db db, int fromVersion, int toVersion); - /** - * Defines method interface to handle table upgrades. - * - * @param db - * the database - * @param schema - * the schema - * @param table - * the table - * @param fromVersion - * the old version - * @param toVersion - * the new version - * @return true for successful upgrade. If the upgrade is successful, the - * version registry is automatically updated. - */ - boolean upgradeTable(Db db, String schema, String table, int fromVersion, int toVersion); + /** + * Defines method interface to handle table upgrades. + * + * @param db the database + * @param schema the schema + * @param table the table + * @param fromVersion the old version + * @param toVersion the new version + * @return true for successful upgrade. If the upgrade is successful, the + * version registry is automatically updated. + */ + boolean upgradeTable(Db db, String schema, String table, int fromVersion, int toVersion); - /** - * The default database upgrader. It throws runtime exception instead of - * handling upgrade requests. - */ - @IQVersion(0) - public static class DefaultDbUpgrader implements DbUpgrader { + /** + * The default database upgrader. It throws runtime exception instead of + * handling upgrade requests. + */ + @IQVersion(0) + public static class DefaultDbUpgrader implements DbUpgrader { - public boolean upgradeDatabase(Db db, int fromVersion, int toVersion) { - throw new IciqlException("Please provide your own DbUpgrader implementation."); - } + public boolean upgradeDatabase(Db db, int fromVersion, int toVersion) { + throw new IciqlException("Please provide your own DbUpgrader implementation."); + } - public boolean upgradeTable(Db db, String schema, String table, int fromVersion, int toVersion) { - throw new IciqlException("Please provide your own DbUpgrader implementation."); - } + public boolean upgradeTable(Db db, String schema, String table, int fromVersion, int toVersion) { + throw new IciqlException("Please provide your own DbUpgrader implementation."); + } - } + } } diff --git a/src/main/java/com/iciql/DbVersion.java b/src/main/java/com/iciql/DbVersion.java index 6270e14..26f1dd7 100644 --- a/src/main/java/com/iciql/DbVersion.java +++ b/src/main/java/com/iciql/DbVersion.java @@ -23,33 +23,32 @@ import com.iciql.Iciql.IQTable; /** * A system table to track database and table versions. */ -@IQTable(name = "iq_versions", primaryKey = { "schemaName", "tableName" }, memoryTable = true) +@IQTable(name = "iq_versions", primaryKey = {"schemaName", "tableName"}, memoryTable = true) public class DbVersion { - @IQColumn(length = 255) - String schemaName = ""; - - @IQColumn(length = 255) - String tableName = ""; - - @IQColumn - Integer version; - - public DbVersion() { - // nothing to do - } - - /** - * Constructor for defining a version entry. Both the schema and the table - * are empty strings, which means this is the row for the 'database'. - * - * @param version - * the database version - */ - public DbVersion(int version) { - this.schemaName = ""; - this.tableName = ""; - this.version = version; - } + @IQColumn(length = 255) + String schemaName = ""; + + @IQColumn(length = 255) + String tableName = ""; + + @IQColumn + Integer version; + + public DbVersion() { + // nothing to do + } + + /** + * Constructor for defining a version entry. Both the schema and the table + * are empty strings, which means this is the row for the 'database'. + * + * @param version the database version + */ + public DbVersion(int version) { + this.schemaName = ""; + this.tableName = ""; + this.version = version; + } } diff --git a/src/main/java/com/iciql/Define.java b/src/main/java/com/iciql/Define.java index b16ee6e..54c9857 100644 --- a/src/main/java/com/iciql/Define.java +++ b/src/main/java/com/iciql/Define.java @@ -28,31 +28,31 @@ import com.iciql.Iciql.IndexType; public class Define { - private static TableDefinition currentTableDefinition; - private static Iciql currentTable; + private static TableDefinition currentTableDefinition; + private static Iciql currentTable; - public static void skipCreate() { - checkInDefine(); - currentTableDefinition.defineSkipCreate(); - } + public static void skipCreate() { + checkInDefine(); + currentTableDefinition.defineSkipCreate(); + } - public static void index(IndexType type, Object... columns) { - checkInDefine(); - currentTableDefinition.defineIndex(null, type, columns); - } + public static void index(IndexType type, Object... columns) { + checkInDefine(); + currentTableDefinition.defineIndex(null, type, columns); + } - public static void index(String name, IndexType type, Object... columns) { - checkInDefine(); - currentTableDefinition.defineIndex(name, type, columns); - } + public static void index(String name, IndexType type, Object... columns) { + checkInDefine(); + currentTableDefinition.defineIndex(name, type, columns); + } - public static void constraintUnique(String name, Object... columns) { - checkInDefine(); - currentTableDefinition.defineConstraintUnique(name, columns); - } + public static void constraintUnique(String name, Object... columns) { + checkInDefine(); + currentTableDefinition.defineConstraintUnique(name, columns); + } /* - * The variable argument type Object can't be used twice :-) + * The variable argument type Object can't be used twice :-) */ // public static void constraintForeignKey(String name, String refTableName, // ConstraintDeleteType deleteType, ConstraintUpdateType updateType, @@ -61,91 +61,91 @@ public class Define { // currentTableDefinition.defineForeignKey(name, columns, refTableName, Columns, deleteType, updateType, deferrabilityType); // } - public static void primaryKey(Object... columns) { - checkInDefine(); - currentTableDefinition.definePrimaryKey(columns); - } - - public static void schemaName(String schemaName) { - checkInDefine(); - currentTableDefinition.defineSchemaName(schemaName); - } - - public static void tableName(String tableName) { - checkInDefine(); - currentTableDefinition.defineTableName(tableName); - } - - public static void viewTableName(String viewTableName) { - checkInDefine(); - currentTableDefinition.defineViewTableName(viewTableName); - } - - public static void memoryTable() { - checkInDefine(); - currentTableDefinition.defineMemoryTable(); - } - - public static void columnName(Object column, String columnName) { - checkInDefine(); - currentTableDefinition.defineColumnName(column, columnName); - } - - public static void autoIncrement(Object column) { - checkInDefine(); - currentTableDefinition.defineAutoIncrement(column); - } - - public static void length(Object column, int length) { - checkInDefine(); - currentTableDefinition.defineLength(column, length); - } - - public static void scale(Object column, int scale) { - checkInDefine(); - currentTableDefinition.defineScale(column, scale); - } - - public static void trim(Object column) { - checkInDefine(); - currentTableDefinition.defineTrim(column); - } - - public static void nullable(Object column, boolean isNullable) { - checkInDefine(); - currentTableDefinition.defineNullable(column, isNullable); - } - - public static void defaultValue(Object column, String defaultValue) { - checkInDefine(); - currentTableDefinition.defineDefaultValue(column, defaultValue); - } - - public static void constraint(Object column, String constraint) { - checkInDefine(); - currentTableDefinition.defineConstraint(column, constraint); - } - - public static void typeAdapter(Object column, Class> typeAdapter) { - checkInDefine(); - currentTableDefinition.defineTypeAdapter(column, typeAdapter); - } - - static synchronized void define(TableDefinition tableDefinition, Iciql table) { - currentTableDefinition = tableDefinition; - currentTable = table; - tableDefinition.mapObject(table); - table.defineIQ(); - currentTable = null; - currentTableDefinition = null; - } - - private static void checkInDefine() { - if (currentTable == null) { - throw new IciqlException("This method may only be called " - + "from within the define() method, and the define() method " - + "is called by the framework."); - } - } + public static void primaryKey(Object... columns) { + checkInDefine(); + currentTableDefinition.definePrimaryKey(columns); + } + + public static void schemaName(String schemaName) { + checkInDefine(); + currentTableDefinition.defineSchemaName(schemaName); + } + + public static void tableName(String tableName) { + checkInDefine(); + currentTableDefinition.defineTableName(tableName); + } + + public static void viewTableName(String viewTableName) { + checkInDefine(); + currentTableDefinition.defineViewTableName(viewTableName); + } + + public static void memoryTable() { + checkInDefine(); + currentTableDefinition.defineMemoryTable(); + } + + public static void columnName(Object column, String columnName) { + checkInDefine(); + currentTableDefinition.defineColumnName(column, columnName); + } + + public static void autoIncrement(Object column) { + checkInDefine(); + currentTableDefinition.defineAutoIncrement(column); + } + + public static void length(Object column, int length) { + checkInDefine(); + currentTableDefinition.defineLength(column, length); + } + + public static void scale(Object column, int scale) { + checkInDefine(); + currentTableDefinition.defineScale(column, scale); + } + + public static void trim(Object column) { + checkInDefine(); + currentTableDefinition.defineTrim(column); + } + + public static void nullable(Object column, boolean isNullable) { + checkInDefine(); + currentTableDefinition.defineNullable(column, isNullable); + } + + public static void defaultValue(Object column, String defaultValue) { + checkInDefine(); + currentTableDefinition.defineDefaultValue(column, defaultValue); + } + + public static void constraint(Object column, String constraint) { + checkInDefine(); + currentTableDefinition.defineConstraint(column, constraint); + } + + public static void typeAdapter(Object column, Class> typeAdapter) { + checkInDefine(); + currentTableDefinition.defineTypeAdapter(column, typeAdapter); + } + + static synchronized void define(TableDefinition tableDefinition, Iciql table) { + currentTableDefinition = tableDefinition; + currentTable = table; + tableDefinition.mapObject(table); + table.defineIQ(); + currentTable = null; + currentTableDefinition = null; + } + + private static void checkInDefine() { + if (currentTable == null) { + throw new IciqlException("This method may only be called " + + "from within the define() method, and the define() method " + + "is called by the framework."); + } + } } diff --git a/src/main/java/com/iciql/Filter.java b/src/main/java/com/iciql/Filter.java index 99dbdc3..b4d4e2d 100644 --- a/src/main/java/com/iciql/Filter.java +++ b/src/main/java/com/iciql/Filter.java @@ -21,5 +21,5 @@ package com.iciql; * Represents the WHERE clause of a query. */ public interface Filter { - boolean where(); + boolean where(); } diff --git a/src/main/java/com/iciql/Function.java b/src/main/java/com/iciql/Function.java index 3faddb7..d0d3d2e 100644 --- a/src/main/java/com/iciql/Function.java +++ b/src/main/java/com/iciql/Function.java @@ -24,126 +24,126 @@ import com.iciql.util.Utils; */ public class Function implements Token { - // must be a new instance - private static final Long COUNT_STAR = new Long(0); - - protected Object[] x; - private String name; - - protected Function(String name, Object... x) { - this.name = name; - this.x = x; - } - - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL(name).appendSQL("("); - int i = 0; - for (Object o : x) { - if (i++ > 0) { - stat.appendSQL(","); - } - query.appendSQL(stat, null, o); - } - stat.appendSQL(")"); - } - - public static Long count() { - return COUNT_STAR; - } - - public static Integer length(Object x) { - return Db.registerToken(Utils.newObject(Integer.class), new Function("LENGTH", x)); - } - - @SuppressWarnings("unchecked") - public static T sum(T x) { - return (T) Db.registerToken(Utils.newObject(x.getClass()), new Function("SUM", x)); - } - - public static Long count(Object x) { - return Db.registerToken(Utils.newObject(Long.class), new Function("COUNT", x)); - } - - public static Boolean isNull(Object x) { - return Db.registerToken(Utils.newObject(Boolean.class), new Function("", x) { - public void appendSQL(SQLStatement stat, Query query) { - query.appendSQL(stat, null, x[0]); - stat.appendSQL(" IS NULL"); - } - }); - } - - public static Boolean isNotNull(Object x) { - return Db.registerToken(Utils.newObject(Boolean.class), new Function("", x) { - public void appendSQL(SQLStatement stat, Query query) { - query.appendSQL(stat, null, x[0]); - stat.appendSQL(" IS NOT NULL"); - } - }); - } - - public static Boolean not(Boolean x) { - return Db.registerToken(Utils.newObject(Boolean.class), new Function("", x) { - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL("NOT "); - query.appendSQL(stat, null, x[0]); - } - }); - } - - public static Boolean or(Boolean... x) { - return Db.registerToken(Utils.newObject(Boolean.class), new Function("", (Object[]) x) { - public void appendSQL(SQLStatement stat, Query query) { - int i = 0; - for (Object o : x) { - if (i++ > 0) { - stat.appendSQL(" OR "); - } - query.appendSQL(stat, null, o); - } - } - }); - } - - public static Boolean and(Boolean... x) { - return Db.registerToken(Utils.newObject(Boolean.class), new Function("", (Object[]) x) { - public void appendSQL(SQLStatement stat, Query query) { - int i = 0; - for (Object o : x) { - if (i++ > 0) { - stat.appendSQL(" AND "); - } - query.appendSQL(stat, null, o); - } - } - }); - } - - @SuppressWarnings("unchecked") - public static X min(X x) { - Class clazz = (Class) x.getClass(); - X o = Utils.newObject(clazz); - return Db.registerToken(o, new Function("MIN", x)); - } - - @SuppressWarnings("unchecked") - public static X max(X x) { - Class clazz = (Class) x.getClass(); - X o = Utils.newObject(clazz); - return Db.registerToken(o, new Function("MAX", x)); - } - - public static Boolean like(String x, String pattern) { - Boolean o = Utils.newObject(Boolean.class); - return Db.registerToken(o, new Function("LIKE", x, pattern) { - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL("("); - query.appendSQL(stat, null, x[0]); - stat.appendSQL(" LIKE "); - query.appendSQL(stat, x[0], x[1]); - stat.appendSQL(")"); - } - }); - } + // must be a new instance + private static final Long COUNT_STAR = new Long(0); + + protected Object[] x; + private String name; + + protected Function(String name, Object... x) { + this.name = name; + this.x = x; + } + + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL(name).appendSQL("("); + int i = 0; + for (Object o : x) { + if (i++ > 0) { + stat.appendSQL(","); + } + query.appendSQL(stat, null, o); + } + stat.appendSQL(")"); + } + + public static Long count() { + return COUNT_STAR; + } + + public static Integer length(Object x) { + return Db.registerToken(Utils.newObject(Integer.class), new Function("LENGTH", x)); + } + + @SuppressWarnings("unchecked") + public static T sum(T x) { + return (T) Db.registerToken(Utils.newObject(x.getClass()), new Function("SUM", x)); + } + + public static Long count(Object x) { + return Db.registerToken(Utils.newObject(Long.class), new Function("COUNT", x)); + } + + public static Boolean isNull(Object x) { + return Db.registerToken(Utils.newObject(Boolean.class), new Function("", x) { + public void appendSQL(SQLStatement stat, Query query) { + query.appendSQL(stat, null, x[0]); + stat.appendSQL(" IS NULL"); + } + }); + } + + public static Boolean isNotNull(Object x) { + return Db.registerToken(Utils.newObject(Boolean.class), new Function("", x) { + public void appendSQL(SQLStatement stat, Query query) { + query.appendSQL(stat, null, x[0]); + stat.appendSQL(" IS NOT NULL"); + } + }); + } + + public static Boolean not(Boolean x) { + return Db.registerToken(Utils.newObject(Boolean.class), new Function("", x) { + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL("NOT "); + query.appendSQL(stat, null, x[0]); + } + }); + } + + public static Boolean or(Boolean... x) { + return Db.registerToken(Utils.newObject(Boolean.class), new Function("", (Object[]) x) { + public void appendSQL(SQLStatement stat, Query query) { + int i = 0; + for (Object o : x) { + if (i++ > 0) { + stat.appendSQL(" OR "); + } + query.appendSQL(stat, null, o); + } + } + }); + } + + public static Boolean and(Boolean... x) { + return Db.registerToken(Utils.newObject(Boolean.class), new Function("", (Object[]) x) { + public void appendSQL(SQLStatement stat, Query query) { + int i = 0; + for (Object o : x) { + if (i++ > 0) { + stat.appendSQL(" AND "); + } + query.appendSQL(stat, null, o); + } + } + }); + } + + @SuppressWarnings("unchecked") + public static X min(X x) { + Class clazz = (Class) x.getClass(); + X o = Utils.newObject(clazz); + return Db.registerToken(o, new Function("MIN", x)); + } + + @SuppressWarnings("unchecked") + public static X max(X x) { + Class clazz = (Class) x.getClass(); + X o = Utils.newObject(clazz); + return Db.registerToken(o, new Function("MAX", x)); + } + + public static Boolean like(String x, String pattern) { + Boolean o = Utils.newObject(Boolean.class); + return Db.registerToken(o, new Function("LIKE", x, pattern) { + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL("("); + query.appendSQL(stat, null, x[0]); + stat.appendSQL(" LIKE "); + query.appendSQL(stat, x[0], x[1]); + stat.appendSQL(")"); + } + }); + } } diff --git a/src/main/java/com/iciql/Iciql.java b/src/main/java/com/iciql/Iciql.java index 302b34d..14d3ab6 100644 --- a/src/main/java/com/iciql/Iciql.java +++ b/src/main/java/com/iciql/Iciql.java @@ -171,7 +171,7 @@ import java.lang.annotation.Target; *

* Automatic model generation: you may automatically generate model classes as * strings with the Db and DbInspector objects: - * + *

*

  * Db db = Db.open("jdbc:h2:mem:", "sa", "sa");
  * DbInspector inspector = new DbInspector(db);
@@ -179,10 +179,10 @@ import java.lang.annotation.Target;
  *         inspector.generateModel(schema, table, packageName,
  *         annotateSchema, trimStrings)
  * 
- * + *

* Or you may use the GenerateModels tool to generate and save your classes to * the file system: - * + *

*

  * java -jar iciql.jar
  *      -url "jdbc:h2:mem:"
@@ -190,10 +190,10 @@ import java.lang.annotation.Target;
  *      -package packageName -folder destination
  *      -annotateSchema false -trimStrings true
  * 
- * + *

* Model validation: you may validate your model class with DbInspector object. * The DbInspector will report errors, warnings, and suggestions: - * + *

*

  * Db db = Db.open("jdbc:h2:mem:", "sa", "sa");
  * DbInspector inspector = new DbInspector(db);
@@ -205,615 +205,613 @@ import java.lang.annotation.Target;
  */
 public interface Iciql {
 
-	/**
-	 * An annotation for an iciql version.
-	 * 

- * - * @IQVersion(1) - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface IQVersion { - - /** - * If set to a non-zero value, iciql maintains a "iq_versions" table - * within your database. The version number is used to call to a - * registered DbUpgrader implementation to perform relevant ALTER - * statements. Default: 0. You must specify a DbUpgrader on your Db - * object to use this parameter. - */ - int value() default 0; - - } - - /** - * An annotation for a schema. - *

- * - * @IQSchema("PUBLIC") - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface IQSchema { - - /** - * The schema may be optionally specified. Default: unspecified. - */ - String value() default ""; - - } - - /** - * Enumeration defining the four index types. - */ - public static enum IndexType { - STANDARD, UNIQUE, HASH, UNIQUE_HASH; - } - - /** - * An index annotation. - *

- *

    - *
  • @IQIndex("name") - *
  • @IQIndex({"street", "city"}) - *
  • @IQIndex(name="streetidx", value={"street", "city"}) - *
  • @IQIndex(name="addressidx", type=IndexType.UNIQUE, - * value={"house_number", "street", "city"}) - *
- */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface IQIndex { - - /** - * Index name. If null or empty, iciql will generate one. - */ - String name() default ""; - - /** - * Type of the index. - *
    - *
  • com.iciql.iciql.IndexType.STANDARD - *
  • com.iciql.iciql.IndexType.UNIQUE - *
  • com.iciql.iciql.IndexType.HASH - *
  • com.iciql.iciql.IndexType.UNIQUE_HASH - *
- * - * HASH indexes may only be valid for single column indexes. - * - */ - IndexType type() default IndexType.STANDARD; - - /** - * Columns to include in index. - *
    - *
  • single column index: value = "id" - *
  • multiple column index: value = { "id", "name", "date" } - *
- */ - String[] value() default {}; - } - - /** - * Enumeration defining the ON DELETE actions. - */ - public static enum ConstraintDeleteType { - UNSET, CASCADE, RESTRICT, SET_NULL, NO_ACTION, SET_DEFAULT; - } - - /** - * Enumeration defining the ON UPDATE actions. - */ - public static enum ConstraintUpdateType { - UNSET, CASCADE, RESTRICT, SET_NULL, NO_ACTION, SET_DEFAULT; - } - - /** - * Enumeration defining the deferrability. - */ - public static enum ConstraintDeferrabilityType { - UNSET, DEFERRABLE_INITIALLY_DEFERRED, DEFERRABLE_INITIALLY_IMMEDIATE, NOT_DEFERRABLE; - } - - /** - * A foreign key constraint annotation. - *

- *

    - *
  • @IQContraintForeignKey( - * foreignColumns = { "idaccount"}, - * referenceName = "account", - * referenceColumns = { "id" }, - * deleteType = ConstrainDeleteType.CASCADE, - * updateType = ConstraintUpdateType.NO_ACTION ) - *
- * Note : reference columns should have a unique constraint defined in referenceName table, - * some database used to define a unique index instead of a unique constraint - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface IQContraintForeignKey { - - /** - * Constraint name. If null or empty, iciql will generate one. - */ - String name() default ""; - - /** - * Type of the action on delete, default to unspecified. - *
    - *
  • com.iciql.iciql.ConstrainDeleteType.CASCADE - *
  • com.iciql.iciql.ConstrainDeleteType.RESTRICT - *
  • com.iciql.iciql.ConstrainDeleteType.SET_NULL - *
  • com.iciql.iciql.ConstrainDeleteType.NO_ACTION - *
  • com.iciql.iciql.ConstrainDeleteType.SET_DEFAULT - *
- */ - ConstraintDeleteType deleteType() default ConstraintDeleteType.UNSET; - - /** - * Type of the action on update, default to unspecified. - *
    - *
  • com.iciql.iciql.ConstrainUpdateType.CASCADE - *
  • com.iciql.iciql.ConstrainUpdateType.RESTRICT - *
  • com.iciql.iciql.ConstrainUpdateType.SET_NULL - *
  • com.iciql.iciql.ConstrainUpdateType.NO_ACTION - *
  • com.iciql.iciql.ConstrainUpdateType.SET_DEFAULT - *
- */ - ConstraintUpdateType updateType() default ConstraintUpdateType.UNSET; - - /** - * Type of the deferrability mode, default to unspecified - *
    - *
  • com.iciql.iciql.ConstrainUpdateType.CASCADE - *
  • ConstraintDeferrabilityType.DEFERRABLE_INITIALLY_DEFERRED - *
  • ConstraintDeferrabilityType.DEFERRABLE_INITIALLY_IMMEDIATE - *
  • ConstraintDeferrabilityType.NOT_DEFERRABLE - *
- */ - ConstraintDeferrabilityType deferrabilityType() default ConstraintDeferrabilityType.UNSET; - - /** - * The source table for the columns defined as foreign. - */ - String tableName() default ""; - - /** - * Columns defined as 'foreign'. - *
    - *
  • single column : foreignColumns = "id" - *
  • multiple column : foreignColumns = { "id", "name", "date" } - *
- */ - String[] foreignColumns() default {}; - - /** - * The reference table for the columns defined as references. - */ - String referenceName() default ""; - - /** - * Columns defined as 'references'. - *
    - *
  • single column : referenceColumns = "id" - *
  • multiple column : referenceColumns = { "id", "name", "date" } - *
- */ - String[] referenceColumns() default {}; - } - - /** - * Annotation to specify multiple foreign keys constraints. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface IQContraintsForeignKey { - IQContraintForeignKey[] value() default {}; - } - - /** - * A unique constraint annotation. - *

- *

    - *
  • @IQContraintUnique(uniqueColumns = { "street", "city" }) - *
  • @IQContraintUnique(name="streetconstraint", uniqueColumns = { "street", "city" }) - *
- */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface IQContraintUnique { - - /** - * Constraint name. If null or empty, iciql will generate one. - */ - String name() default ""; - - /** - * Columns defined as 'unique'. - *
    - *
  • single column : uniqueColumns = "id" - *
  • multiple column : uniqueColumns = { "id", "name", "date" } - *
- */ - String[] uniqueColumns() default {}; - - } - - /** - * Annotation to specify multiple unique constraints. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface IQContraintsUnique { - IQContraintUnique[] value() default {}; - } - - /** - * Annotation to define a view. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface IQView { - - /** - * The view name. If not specified the class name is used as the view - * name. - *

- * The view name may still be overridden in the define() method if the - * model class is not annotated with IQView. Default: unspecified. - */ - String name() default ""; - - /** - * The source table for the view. - *

- * The view name may still be overridden in the define() method if the - * model class is not annotated with IQView. Default: unspecified. - */ - String tableName() default ""; - - /** - * The inherit columns allows this model class to inherit columns from - * its super class. IQTable and IQView annotations present on the super - * class or above are honored. Default: false. - */ - boolean inheritColumns() default false; - - /** - * Whether or not iciql tries to create the view. Default: - * true. - */ - boolean create() default true; - - /** - * If true, only fields that are explicitly annotated as IQColumn are - * mapped. Default: true. - */ - boolean annotationsOnly() default true; - } - - /** - * String snippet defining SQL constraints for a field. Use "this" as - * a placeholder for the column name. "this" will be substituted at - * runtime. - *

- * IQConstraint("this > 2 AND this <= 7") - *

- * This snippet may still be overridden in the define() method if the - * model class is not annotated with IQTable or IQView. Default: unspecified. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface IQConstraint { - - String value() default ""; - } - - /** - * Annotation to specify multiple indexes. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface IQIndexes { - IQIndex[] value() default {}; - } - - /** - * Annotation to define a table. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.TYPE) - public @interface IQTable { - - /** - * The table name. If not specified the class name is used as the table - * name. - *

- * The table name may still be overridden in the define() method if the - * model class is not annotated with IQTable. Default: unspecified. - */ - String name() default ""; - - /** - * The primary key may be optionally specified. If it is not specified, - * then no primary key is set by the IQTable annotation. You may specify - * a composite primary key. - *

    - *
  • single column primaryKey: value = "id" - *
  • compound primary key: value = { "id", "name" } - *
- * The primary key may still be overridden in the define() method if the - * model class is not annotated with IQTable. Default: unspecified. - */ - String[] primaryKey() default {}; - - /** - * The inherit columns allows this model class to inherit columns from - * its super class. IQTable and IQView annotations present on the super - * class or above are honored. Default: false. - */ - boolean inheritColumns() default false; - - /** - * Whether or not iciql tries to create the table and indexes. Default: - * true. - */ - boolean create() default true; - - /** - * If true, only fields that are explicitly annotated as IQColumn are - * mapped. Default: true. - */ - boolean annotationsOnly() default true; - - /** - * If true, this table is created as a memory table where data is - * persistent, but index data is kept in main memory. Valid only for H2 - * and HSQL databases. Default: false. - */ - boolean memoryTable() default false; - } - - /** - * Annotation to define a column. Annotated fields may have any scope - * (however, the JVM may raise a SecurityException if the SecurityManager - * doesn't allow iciql to access the field.) - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface IQColumn { - - /** - * If not specified, the field name is used as the column name. Default: - * the field name. - */ - String name() default ""; - - /** - * This column is the primary key. Default: false. - */ - boolean primaryKey() default false; - - /** - * The column is created with a sequence as the default value. Default: - * false. - */ - boolean autoIncrement() default false; - - /** - * Length is used to define the length of a VARCHAR column or to define - * the precision of a DECIMAL(precision, scale) expression. - *

- * If larger than zero, it is used during the CREATE TABLE phase. For - * string values it may also be used to prevent database exceptions on - * INSERT and UPDATE statements (see trim). - *

- * Any length set in define() may override this annotation setting if - * the model class is not annotated with IQTable. Default: 0. - */ - int length() default 0; - - /** - * Scale is used during the CREATE TABLE phase to define the scale of a - * DECIMAL(precision, scale) expression. - *

- * Any scale set in define() may override this annotation setting if the - * model class is not annotated with IQTable. Default: 0. - */ - int scale() default 0; - - /** - * If true, iciql will automatically trim the string if it exceeds - * length (value.substring(0, length)). Default: false. - */ - boolean trim() default false; - - /** - * If false, iciql will set the column NOT NULL during the CREATE TABLE - * phase. Default: true. - */ - boolean nullable() default true; - - /** - * The default value assigned to the column during the CREATE TABLE - * phase. This field could contain a literal single-quoted value, or a - * function call. Empty strings are considered NULL. Examples: - *

    - *
  • defaultValue="" (null) - *
  • defaultValue="CURRENT_TIMESTAMP" - *
  • defaultValue="''" (empty string) - *
  • defaultValue="'0'" - *
  • defaultValue="'1970-01-01 00:00:01'" - *
- * if the default value is specified, and auto increment is disabled, - * and primary key is disabled, then this value is included in the - * "DEFAULT ..." phrase of a column during the CREATE TABLE process. - *

- * Alternatively, you may specify a default object value on the field - * and this will be converted to a properly formatted DEFAULT expression - * during the CREATE TABLE process. - *

- * Default: unspecified (null). - */ - String defaultValue() default ""; - - } - - /** - * Interface for using the EnumType.ENUMID enumeration mapping strategy. - *

- * Enumerations wishing to use EnumType.ENUMID must implement this - * interface. - */ - public interface EnumId { - X enumId(); - Class enumIdClass(); - } - - - - /** - * Enumeration representing how to map a java.lang.Enum to a column. - *

- *

    - *
  • NAME - name() : string - *
  • ORDINAL - ordinal() : int - *
  • ENUMID - enumId() : X - *
- * - * @see com.iciql.Iciql.EnumId interface - */ - public enum EnumType { - NAME, ORDINAL, ENUMID; - - public static final EnumType DEFAULT_TYPE = NAME; - } - - /** - * Annotation to define how a java.lang.Enum is mapped to a column. - *

- * This annotation can be used on: - *

    - *
  • a field instance of an enumeration type - *
  • on the enumeration class declaration - *
- * If you choose to annotate the class declaration, that will be the default - * mapping strategy for all @IQColumn instances of the enum. This can still - * be overridden for an individual field by specifying the IQEnum - * annotation. - *

- * The default mapping is by NAME. - * - *

-	 * IQEnum(EnumType.NAME)
-	 * 
- * - * A string mapping will generate either a VARCHAR, if IQColumn.length > 0 - * or a TEXT column if IQColumn.length == 0 - * - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.FIELD, ElementType.TYPE }) - public @interface IQEnum { - EnumType value() default EnumType.NAME; - } - - /** - * Annotation to define an ignored field. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target(ElementType.FIELD) - public @interface IQIgnore{ - } - - /** - * The runtime mode for Iciql. - */ - public static enum Mode { - - DEV, TEST, PROD; - - public static Mode fromValue(String value) { - - for (Mode mode : values()) { - if (mode.name().equalsIgnoreCase(value)) { - return mode; - } - } - - return PROD; - } - } - - /** - * This method is called to let the table define the primary key, indexes, - * and the table name. - */ - void defineIQ(); - - /** - * Specify a custom type adapter for a method return type, a class field, or a method - * parameter. Type adapters allow you to transform content received from or inserted into - * a database field. - */ - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) - public @interface TypeAdapter { - Class> value(); - } - - /** - * Interface to allow implementations of custom data type adapters for supporting - * database-specific data types, like the Postgres 'json' or 'xml' types, - * or for supporting other object serialization schemes. - *

NOTE: Data type adapters are not thread-safe!

- * - * @param - */ - public interface DataTypeAdapter { - - /** - * The SQL data type for this adapter. - * - * @return the SQL data type - */ - String getDataType(); - - /** - * The Java domain type for this adapter. - * - * @return the Java domain type - */ - Class getJavaType(); - - - /** - * Set the runtime mode. - *

- * Allows type adapters to adapt type mappings based on the runtime - * mode. - *

- * - * @param mode - */ - void setMode(Mode mode); - - /** - * Serializes your Java object into a JDBC object. - * - * @param value - * @return a JDBC object - */ - Object serialize(T value); - - /** - * Deserializes a JDBC object into your Java object. - * - * @param value - * @return the Java object - */ - T deserialize(Object value); - - } + /** + * An annotation for an iciql version. + *

+ * + * @IQVersion(1) + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface IQVersion { + + /** + * If set to a non-zero value, iciql maintains a "iq_versions" table + * within your database. The version number is used to call to a + * registered DbUpgrader implementation to perform relevant ALTER + * statements. Default: 0. You must specify a DbUpgrader on your Db + * object to use this parameter. + */ + int value() default 0; + + } + + /** + * An annotation for a schema. + *

+ * + * @IQSchema("PUBLIC") + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface IQSchema { + + /** + * The schema may be optionally specified. Default: unspecified. + */ + String value() default ""; + + } + + /** + * Enumeration defining the four index types. + */ + public static enum IndexType { + STANDARD, UNIQUE, HASH, UNIQUE_HASH; + } + + /** + * An index annotation. + *

+ *

    + *
  • @IQIndex("name") + *
  • @IQIndex({"street", "city"}) + *
  • @IQIndex(name="streetidx", value={"street", "city"}) + *
  • @IQIndex(name="addressidx", type=IndexType.UNIQUE, + * value={"house_number", "street", "city"}) + *
+ */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface IQIndex { + + /** + * Index name. If null or empty, iciql will generate one. + */ + String name() default ""; + + /** + * Type of the index. + *
    + *
  • com.iciql.iciql.IndexType.STANDARD + *
  • com.iciql.iciql.IndexType.UNIQUE + *
  • com.iciql.iciql.IndexType.HASH + *
  • com.iciql.iciql.IndexType.UNIQUE_HASH + *
+ *

+ * HASH indexes may only be valid for single column indexes. + */ + IndexType type() default IndexType.STANDARD; + + /** + * Columns to include in index. + *

    + *
  • single column index: value = "id" + *
  • multiple column index: value = { "id", "name", "date" } + *
+ */ + String[] value() default {}; + } + + /** + * Enumeration defining the ON DELETE actions. + */ + public static enum ConstraintDeleteType { + UNSET, CASCADE, RESTRICT, SET_NULL, NO_ACTION, SET_DEFAULT; + } + + /** + * Enumeration defining the ON UPDATE actions. + */ + public static enum ConstraintUpdateType { + UNSET, CASCADE, RESTRICT, SET_NULL, NO_ACTION, SET_DEFAULT; + } + + /** + * Enumeration defining the deferrability. + */ + public static enum ConstraintDeferrabilityType { + UNSET, DEFERRABLE_INITIALLY_DEFERRED, DEFERRABLE_INITIALLY_IMMEDIATE, NOT_DEFERRABLE; + } + + /** + * A foreign key constraint annotation. + *

+ *

    + *
  • @IQContraintForeignKey( + * foreignColumns = { "idaccount"}, + * referenceName = "account", + * referenceColumns = { "id" }, + * deleteType = ConstrainDeleteType.CASCADE, + * updateType = ConstraintUpdateType.NO_ACTION ) + *
+ * Note : reference columns should have a unique constraint defined in referenceName table, + * some database used to define a unique index instead of a unique constraint + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface IQContraintForeignKey { + + /** + * Constraint name. If null or empty, iciql will generate one. + */ + String name() default ""; + + /** + * Type of the action on delete, default to unspecified. + *
    + *
  • com.iciql.iciql.ConstrainDeleteType.CASCADE + *
  • com.iciql.iciql.ConstrainDeleteType.RESTRICT + *
  • com.iciql.iciql.ConstrainDeleteType.SET_NULL + *
  • com.iciql.iciql.ConstrainDeleteType.NO_ACTION + *
  • com.iciql.iciql.ConstrainDeleteType.SET_DEFAULT + *
+ */ + ConstraintDeleteType deleteType() default ConstraintDeleteType.UNSET; + + /** + * Type of the action on update, default to unspecified. + *
    + *
  • com.iciql.iciql.ConstrainUpdateType.CASCADE + *
  • com.iciql.iciql.ConstrainUpdateType.RESTRICT + *
  • com.iciql.iciql.ConstrainUpdateType.SET_NULL + *
  • com.iciql.iciql.ConstrainUpdateType.NO_ACTION + *
  • com.iciql.iciql.ConstrainUpdateType.SET_DEFAULT + *
+ */ + ConstraintUpdateType updateType() default ConstraintUpdateType.UNSET; + + /** + * Type of the deferrability mode, default to unspecified + *
    + *
  • com.iciql.iciql.ConstrainUpdateType.CASCADE + *
  • ConstraintDeferrabilityType.DEFERRABLE_INITIALLY_DEFERRED + *
  • ConstraintDeferrabilityType.DEFERRABLE_INITIALLY_IMMEDIATE + *
  • ConstraintDeferrabilityType.NOT_DEFERRABLE + *
+ */ + ConstraintDeferrabilityType deferrabilityType() default ConstraintDeferrabilityType.UNSET; + + /** + * The source table for the columns defined as foreign. + */ + String tableName() default ""; + + /** + * Columns defined as 'foreign'. + *
    + *
  • single column : foreignColumns = "id" + *
  • multiple column : foreignColumns = { "id", "name", "date" } + *
+ */ + String[] foreignColumns() default {}; + + /** + * The reference table for the columns defined as references. + */ + String referenceName() default ""; + + /** + * Columns defined as 'references'. + *
    + *
  • single column : referenceColumns = "id" + *
  • multiple column : referenceColumns = { "id", "name", "date" } + *
+ */ + String[] referenceColumns() default {}; + } + + /** + * Annotation to specify multiple foreign keys constraints. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface IQContraintsForeignKey { + IQContraintForeignKey[] value() default {}; + } + + /** + * A unique constraint annotation. + *

+ *

    + *
  • @IQContraintUnique(uniqueColumns = { "street", "city" }) + *
  • @IQContraintUnique(name="streetconstraint", uniqueColumns = { "street", "city" }) + *
+ */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface IQContraintUnique { + + /** + * Constraint name. If null or empty, iciql will generate one. + */ + String name() default ""; + + /** + * Columns defined as 'unique'. + *
    + *
  • single column : uniqueColumns = "id" + *
  • multiple column : uniqueColumns = { "id", "name", "date" } + *
+ */ + String[] uniqueColumns() default {}; + + } + + /** + * Annotation to specify multiple unique constraints. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface IQContraintsUnique { + IQContraintUnique[] value() default {}; + } + + /** + * Annotation to define a view. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface IQView { + + /** + * The view name. If not specified the class name is used as the view + * name. + *

+ * The view name may still be overridden in the define() method if the + * model class is not annotated with IQView. Default: unspecified. + */ + String name() default ""; + + /** + * The source table for the view. + *

+ * The view name may still be overridden in the define() method if the + * model class is not annotated with IQView. Default: unspecified. + */ + String tableName() default ""; + + /** + * The inherit columns allows this model class to inherit columns from + * its super class. IQTable and IQView annotations present on the super + * class or above are honored. Default: false. + */ + boolean inheritColumns() default false; + + /** + * Whether or not iciql tries to create the view. Default: + * true. + */ + boolean create() default true; + + /** + * If true, only fields that are explicitly annotated as IQColumn are + * mapped. Default: true. + */ + boolean annotationsOnly() default true; + } + + /** + * String snippet defining SQL constraints for a field. Use "this" as + * a placeholder for the column name. "this" will be substituted at + * runtime. + *

+ * IQConstraint("this > 2 AND this <= 7") + *

+ * This snippet may still be overridden in the define() method if the + * model class is not annotated with IQTable or IQView. Default: unspecified. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface IQConstraint { + + String value() default ""; + } + + /** + * Annotation to specify multiple indexes. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface IQIndexes { + IQIndex[] value() default {}; + } + + /** + * Annotation to define a table. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface IQTable { + + /** + * The table name. If not specified the class name is used as the table + * name. + *

+ * The table name may still be overridden in the define() method if the + * model class is not annotated with IQTable. Default: unspecified. + */ + String name() default ""; + + /** + * The primary key may be optionally specified. If it is not specified, + * then no primary key is set by the IQTable annotation. You may specify + * a composite primary key. + *

    + *
  • single column primaryKey: value = "id" + *
  • compound primary key: value = { "id", "name" } + *
+ * The primary key may still be overridden in the define() method if the + * model class is not annotated with IQTable. Default: unspecified. + */ + String[] primaryKey() default {}; + + /** + * The inherit columns allows this model class to inherit columns from + * its super class. IQTable and IQView annotations present on the super + * class or above are honored. Default: false. + */ + boolean inheritColumns() default false; + + /** + * Whether or not iciql tries to create the table and indexes. Default: + * true. + */ + boolean create() default true; + + /** + * If true, only fields that are explicitly annotated as IQColumn are + * mapped. Default: true. + */ + boolean annotationsOnly() default true; + + /** + * If true, this table is created as a memory table where data is + * persistent, but index data is kept in main memory. Valid only for H2 + * and HSQL databases. Default: false. + */ + boolean memoryTable() default false; + } + + /** + * Annotation to define a column. Annotated fields may have any scope + * (however, the JVM may raise a SecurityException if the SecurityManager + * doesn't allow iciql to access the field.) + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface IQColumn { + + /** + * If not specified, the field name is used as the column name. Default: + * the field name. + */ + String name() default ""; + + /** + * This column is the primary key. Default: false. + */ + boolean primaryKey() default false; + + /** + * The column is created with a sequence as the default value. Default: + * false. + */ + boolean autoIncrement() default false; + + /** + * Length is used to define the length of a VARCHAR column or to define + * the precision of a DECIMAL(precision, scale) expression. + *

+ * If larger than zero, it is used during the CREATE TABLE phase. For + * string values it may also be used to prevent database exceptions on + * INSERT and UPDATE statements (see trim). + *

+ * Any length set in define() may override this annotation setting if + * the model class is not annotated with IQTable. Default: 0. + */ + int length() default 0; + + /** + * Scale is used during the CREATE TABLE phase to define the scale of a + * DECIMAL(precision, scale) expression. + *

+ * Any scale set in define() may override this annotation setting if the + * model class is not annotated with IQTable. Default: 0. + */ + int scale() default 0; + + /** + * If true, iciql will automatically trim the string if it exceeds + * length (value.substring(0, length)). Default: false. + */ + boolean trim() default false; + + /** + * If false, iciql will set the column NOT NULL during the CREATE TABLE + * phase. Default: true. + */ + boolean nullable() default true; + + /** + * The default value assigned to the column during the CREATE TABLE + * phase. This field could contain a literal single-quoted value, or a + * function call. Empty strings are considered NULL. Examples: + *

    + *
  • defaultValue="" (null) + *
  • defaultValue="CURRENT_TIMESTAMP" + *
  • defaultValue="''" (empty string) + *
  • defaultValue="'0'" + *
  • defaultValue="'1970-01-01 00:00:01'" + *
+ * if the default value is specified, and auto increment is disabled, + * and primary key is disabled, then this value is included in the + * "DEFAULT ..." phrase of a column during the CREATE TABLE process. + *

+ * Alternatively, you may specify a default object value on the field + * and this will be converted to a properly formatted DEFAULT expression + * during the CREATE TABLE process. + *

+ * Default: unspecified (null). + */ + String defaultValue() default ""; + + } + + /** + * Interface for using the EnumType.ENUMID enumeration mapping strategy. + *

+ * Enumerations wishing to use EnumType.ENUMID must implement this + * interface. + */ + public interface EnumId { + X enumId(); + + Class enumIdClass(); + } + + + /** + * Enumeration representing how to map a java.lang.Enum to a column. + *

+ *

    + *
  • NAME - name() : string + *
  • ORDINAL - ordinal() : int + *
  • ENUMID - enumId() : X + *
+ * + * @see com.iciql.Iciql.EnumId interface + */ + public enum EnumType { + NAME, ORDINAL, ENUMID; + + public static final EnumType DEFAULT_TYPE = NAME; + } + + /** + * Annotation to define how a java.lang.Enum is mapped to a column. + *

+ * This annotation can be used on: + *

    + *
  • a field instance of an enumeration type + *
  • on the enumeration class declaration + *
+ * If you choose to annotate the class declaration, that will be the default + * mapping strategy for all @IQColumn instances of the enum. This can still + * be overridden for an individual field by specifying the IQEnum + * annotation. + *

+ * The default mapping is by NAME. + *

+ *

+     * IQEnum(EnumType.NAME)
+     * 
+ *

+ * A string mapping will generate either a VARCHAR, if IQColumn.length > 0 + * or a TEXT column if IQColumn.length == 0 + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.FIELD, ElementType.TYPE}) + public @interface IQEnum { + EnumType value() default EnumType.NAME; + } + + /** + * Annotation to define an ignored field. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + public @interface IQIgnore { + } + + /** + * The runtime mode for Iciql. + */ + public static enum Mode { + + DEV, TEST, PROD; + + public static Mode fromValue(String value) { + + for (Mode mode : values()) { + if (mode.name().equalsIgnoreCase(value)) { + return mode; + } + } + + return PROD; + } + } + + /** + * This method is called to let the table define the primary key, indexes, + * and the table name. + */ + void defineIQ(); + + /** + * Specify a custom type adapter for a method return type, a class field, or a method + * parameter. Type adapters allow you to transform content received from or inserted into + * a database field. + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) + public @interface TypeAdapter { + Class> value(); + } + + /** + * Interface to allow implementations of custom data type adapters for supporting + * database-specific data types, like the Postgres 'json' or 'xml' types, + * or for supporting other object serialization schemes. + *

NOTE: Data type adapters are not thread-safe!

+ * + * @param + */ + public interface DataTypeAdapter { + + /** + * The SQL data type for this adapter. + * + * @return the SQL data type + */ + String getDataType(); + + /** + * The Java domain type for this adapter. + * + * @return the Java domain type + */ + Class getJavaType(); + + + /** + * Set the runtime mode. + *

+ * Allows type adapters to adapt type mappings based on the runtime + * mode. + *

+ * + * @param mode + */ + void setMode(Mode mode); + + /** + * Serializes your Java object into a JDBC object. + * + * @param value + * @return a JDBC object + */ + Object serialize(T value); + + /** + * Deserializes a JDBC object into your Java object. + * + * @param value + * @return the Java object + */ + T deserialize(Object value); + + } } diff --git a/src/main/java/com/iciql/IciqlException.java b/src/main/java/com/iciql/IciqlException.java index 3db62cf..b5e1ca6 100644 --- a/src/main/java/com/iciql/IciqlException.java +++ b/src/main/java/com/iciql/IciqlException.java @@ -26,170 +26,170 @@ import java.util.regex.Pattern; */ public class IciqlException extends RuntimeException { - public static final int CODE_UNMAPPED_FIELD = 1; - public static final int CODE_DUPLICATE_KEY = 2; - public static final int CODE_OBJECT_NOT_FOUND = 3; - public static final int CODE_OBJECT_ALREADY_EXISTS = 4; - public static final int CODE_CONSTRAINT_VIOLATION = 5; - public static final int CODE_UNCHARACTERIZED = 6; - - private static final String TOKEN_UNMAPPED_FIELD = "\\? (=|\\>|\\<|\\<\\>|!=|\\>=|\\<=|LIKE|BETWEEN) \\?"; - - private static final long serialVersionUID = 1L; - - private String sql; - - private int iciqlCode; - - public IciqlException(Throwable t) { - super(t.getMessage(), t); - configureCode(t); - } - - public IciqlException(String message, Object... parameters) { - super(parameters.length > 0 ? MessageFormat.format(message, parameters) : message); - } - - public IciqlException(Throwable t, String message, Object... parameters) { - super(parameters.length > 0 ? MessageFormat.format(message, parameters) : message, t); - configureCode(t); - } - - public static void checkUnmappedField(String sql) { - if (Pattern.compile(IciqlException.TOKEN_UNMAPPED_FIELD).matcher(sql).find()) { - IciqlException e = new IciqlException("unmapped field in statement!"); - e.sql = sql; - e.iciqlCode = CODE_UNMAPPED_FIELD; - throw e; - } - } - - public static IciqlException fromSQL(String sql, Throwable t) { - if (Pattern.compile(TOKEN_UNMAPPED_FIELD).matcher(sql).find()) { - IciqlException e = new IciqlException(t, "unmapped field in statement!"); - e.sql = sql; - e.iciqlCode = CODE_UNMAPPED_FIELD; - return e; - } else { - IciqlException e = new IciqlException(t, t.getMessage()); - e.sql = sql; - return e; - } - } - - public void setSQL(String sql) { - this.sql = sql; - } - - public String getSQL() { - return sql; - } - - public int getIciqlCode() { - return iciqlCode; - } - - private void configureCode(Throwable t) { - if (t == null) { - return; - } - if (t instanceof SQLException) { - // http://developer.mimer.com/documentation/html_92/Mimer_SQL_Mobile_DocSet/App_Return_Codes2.html - SQLException s = (SQLException) t; - String state = s.getSQLState(); - if ("23000".equals(state)) { - // MySQL duplicate primary key on insert - iciqlCode = CODE_DUPLICATE_KEY; - if (s.getErrorCode() == 1217) { - iciqlCode = CODE_CONSTRAINT_VIOLATION; - } - } else if ("23505".equals(state)) { - // Derby duplicate primary key on insert - iciqlCode = CODE_DUPLICATE_KEY; - } else if ("42000".equals(state)) { - // MySQL duplicate unique index value on insert - iciqlCode = CODE_DUPLICATE_KEY; - } else if ("42Y07".equals(state)) { - // Derby schema not found - iciqlCode = CODE_OBJECT_NOT_FOUND; - } else if ("42X05".equals(state)) { - // Derby table not found - iciqlCode = CODE_OBJECT_NOT_FOUND; - } else if ("42Y55".equals(state)) { - // Derby table not found - iciqlCode = CODE_OBJECT_NOT_FOUND; - } else if ("42S02".equals(state)) { - // H2 table not found - iciqlCode = CODE_OBJECT_NOT_FOUND; - } else if ("42501".equals(state)) { - // HSQL table not found - iciqlCode = CODE_OBJECT_NOT_FOUND; - } else if ("42P01".equals(state)) { - // PostgreSQL table not found - iciqlCode = CODE_OBJECT_NOT_FOUND; - } else if ("X0X05".equals(state)) { - // Derby view/table not found exists - iciqlCode = CODE_OBJECT_NOT_FOUND; - } else if ("X0Y32".equals(state)) { - // Derby table already exists - iciqlCode = CODE_OBJECT_ALREADY_EXISTS; - } else if ("42P07".equals(state)) { - // PostgreSQL table or index already exists - iciqlCode = CODE_OBJECT_ALREADY_EXISTS; - } else if ("42S01".equals(state)) { - // MySQL view already exists - iciqlCode = CODE_OBJECT_ALREADY_EXISTS; - } else if ("42S11".equals(state)) { - // H2 index already exists - iciqlCode = CODE_OBJECT_ALREADY_EXISTS; - } else if ("42504".equals(state)) { - // HSQL index already exists - iciqlCode = CODE_OBJECT_ALREADY_EXISTS; - } else if ("2BP01".equals(state)) { - // PostgreSQL constraint violation - iciqlCode = CODE_CONSTRAINT_VIOLATION; - } else if ("42533".equals(state)) { - // HSQL constraint violation - iciqlCode = CODE_CONSTRAINT_VIOLATION; - } else if ("X0Y25".equals(state)) { - // Derby constraint violation - iciqlCode = CODE_CONSTRAINT_VIOLATION; - } else if (s.getMessage().startsWith("[SQLITE")) { - // SQLite error codes - final String msg = s.getMessage(); - switch (s.getErrorCode()) { - case 1: - iciqlCode = CODE_OBJECT_NOT_FOUND; - break; - case 19: - if (msg.contains("UNIQUE")) { - iciqlCode = CODE_DUPLICATE_KEY; - } else { - iciqlCode = CODE_CONSTRAINT_VIOLATION; - } - break; - default: - iciqlCode = s.getErrorCode(); - break; - } - } else { - // uncharacterized SQL code, we can always rely on iciqlCode != 0 in IciqlException - iciqlCode = s.getErrorCode() == 0 ? CODE_UNCHARACTERIZED : s.getErrorCode(); - } - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(getClass().getName()); - String message = getLocalizedMessage(); - if (message != null) { - sb.append(": ").append(message); - } - if (sql != null) { - sb.append('\n').append(sql); - } - return sb.toString(); - } + public static final int CODE_UNMAPPED_FIELD = 1; + public static final int CODE_DUPLICATE_KEY = 2; + public static final int CODE_OBJECT_NOT_FOUND = 3; + public static final int CODE_OBJECT_ALREADY_EXISTS = 4; + public static final int CODE_CONSTRAINT_VIOLATION = 5; + public static final int CODE_UNCHARACTERIZED = 6; + + private static final String TOKEN_UNMAPPED_FIELD = "\\? (=|\\>|\\<|\\<\\>|!=|\\>=|\\<=|LIKE|BETWEEN) \\?"; + + private static final long serialVersionUID = 1L; + + private String sql; + + private int iciqlCode; + + public IciqlException(Throwable t) { + super(t.getMessage(), t); + configureCode(t); + } + + public IciqlException(String message, Object... parameters) { + super(parameters.length > 0 ? MessageFormat.format(message, parameters) : message); + } + + public IciqlException(Throwable t, String message, Object... parameters) { + super(parameters.length > 0 ? MessageFormat.format(message, parameters) : message, t); + configureCode(t); + } + + public static void checkUnmappedField(String sql) { + if (Pattern.compile(IciqlException.TOKEN_UNMAPPED_FIELD).matcher(sql).find()) { + IciqlException e = new IciqlException("unmapped field in statement!"); + e.sql = sql; + e.iciqlCode = CODE_UNMAPPED_FIELD; + throw e; + } + } + + public static IciqlException fromSQL(String sql, Throwable t) { + if (Pattern.compile(TOKEN_UNMAPPED_FIELD).matcher(sql).find()) { + IciqlException e = new IciqlException(t, "unmapped field in statement!"); + e.sql = sql; + e.iciqlCode = CODE_UNMAPPED_FIELD; + return e; + } else { + IciqlException e = new IciqlException(t, t.getMessage()); + e.sql = sql; + return e; + } + } + + public void setSQL(String sql) { + this.sql = sql; + } + + public String getSQL() { + return sql; + } + + public int getIciqlCode() { + return iciqlCode; + } + + private void configureCode(Throwable t) { + if (t == null) { + return; + } + if (t instanceof SQLException) { + // http://developer.mimer.com/documentation/html_92/Mimer_SQL_Mobile_DocSet/App_Return_Codes2.html + SQLException s = (SQLException) t; + String state = s.getSQLState(); + if ("23000".equals(state)) { + // MySQL duplicate primary key on insert + iciqlCode = CODE_DUPLICATE_KEY; + if (s.getErrorCode() == 1217) { + iciqlCode = CODE_CONSTRAINT_VIOLATION; + } + } else if ("23505".equals(state)) { + // Derby duplicate primary key on insert + iciqlCode = CODE_DUPLICATE_KEY; + } else if ("42000".equals(state)) { + // MySQL duplicate unique index value on insert + iciqlCode = CODE_DUPLICATE_KEY; + } else if ("42Y07".equals(state)) { + // Derby schema not found + iciqlCode = CODE_OBJECT_NOT_FOUND; + } else if ("42X05".equals(state)) { + // Derby table not found + iciqlCode = CODE_OBJECT_NOT_FOUND; + } else if ("42Y55".equals(state)) { + // Derby table not found + iciqlCode = CODE_OBJECT_NOT_FOUND; + } else if ("42S02".equals(state)) { + // H2 table not found + iciqlCode = CODE_OBJECT_NOT_FOUND; + } else if ("42501".equals(state)) { + // HSQL table not found + iciqlCode = CODE_OBJECT_NOT_FOUND; + } else if ("42P01".equals(state)) { + // PostgreSQL table not found + iciqlCode = CODE_OBJECT_NOT_FOUND; + } else if ("X0X05".equals(state)) { + // Derby view/table not found exists + iciqlCode = CODE_OBJECT_NOT_FOUND; + } else if ("X0Y32".equals(state)) { + // Derby table already exists + iciqlCode = CODE_OBJECT_ALREADY_EXISTS; + } else if ("42P07".equals(state)) { + // PostgreSQL table or index already exists + iciqlCode = CODE_OBJECT_ALREADY_EXISTS; + } else if ("42S01".equals(state)) { + // MySQL view already exists + iciqlCode = CODE_OBJECT_ALREADY_EXISTS; + } else if ("42S11".equals(state)) { + // H2 index already exists + iciqlCode = CODE_OBJECT_ALREADY_EXISTS; + } else if ("42504".equals(state)) { + // HSQL index already exists + iciqlCode = CODE_OBJECT_ALREADY_EXISTS; + } else if ("2BP01".equals(state)) { + // PostgreSQL constraint violation + iciqlCode = CODE_CONSTRAINT_VIOLATION; + } else if ("42533".equals(state)) { + // HSQL constraint violation + iciqlCode = CODE_CONSTRAINT_VIOLATION; + } else if ("X0Y25".equals(state)) { + // Derby constraint violation + iciqlCode = CODE_CONSTRAINT_VIOLATION; + } else if (s.getMessage().startsWith("[SQLITE")) { + // SQLite error codes + final String msg = s.getMessage(); + switch (s.getErrorCode()) { + case 1: + iciqlCode = CODE_OBJECT_NOT_FOUND; + break; + case 19: + if (msg.contains("UNIQUE")) { + iciqlCode = CODE_DUPLICATE_KEY; + } else { + iciqlCode = CODE_CONSTRAINT_VIOLATION; + } + break; + default: + iciqlCode = s.getErrorCode(); + break; + } + } else { + // uncharacterized SQL code, we can always rely on iciqlCode != 0 in IciqlException + iciqlCode = s.getErrorCode() == 0 ? CODE_UNCHARACTERIZED : s.getErrorCode(); + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(getClass().getName()); + String message = getLocalizedMessage(); + if (message != null) { + sb.append(": ").append(message); + } + if (sql != null) { + sb.append('\n').append(sql); + } + return sb.toString(); + } } diff --git a/src/main/java/com/iciql/ModelUtils.java b/src/main/java/com/iciql/ModelUtils.java index 7fa1de6..14dc615 100644 --- a/src/main/java/com/iciql/ModelUtils.java +++ b/src/main/java/com/iciql/ModelUtils.java @@ -17,7 +17,8 @@ package com.iciql; -import static com.iciql.util.StringUtils.isNullOrEmpty; +import com.iciql.TableDefinition.FieldDefinition; +import com.iciql.util.StringUtils; import java.lang.reflect.Method; import java.math.BigDecimal; @@ -32,8 +33,7 @@ import java.util.Map; import java.util.UUID; import java.util.regex.Pattern; -import com.iciql.TableDefinition.FieldDefinition; -import com.iciql.util.StringUtils; +import static com.iciql.util.StringUtils.isNullOrEmpty; /** * Utility methods for models related to type mapping, default value validation, @@ -41,469 +41,460 @@ import com.iciql.util.StringUtils; */ class ModelUtils { - /** - * The list of supported data types. It is used by the runtime mapping for - * CREATE statements. - */ - private static final Map, String> SUPPORTED_TYPES = new HashMap, String>(); - - static { - Map, String> m = SUPPORTED_TYPES; - m.put(String.class, "VARCHAR"); - m.put(Boolean.class, "BOOLEAN"); - m.put(Byte.class, "TINYINT"); - m.put(Short.class, "SMALLINT"); - m.put(Integer.class, "INT"); - m.put(Long.class, "BIGINT"); - m.put(Float.class, "REAL"); - m.put(Double.class, "DOUBLE"); - m.put(BigDecimal.class, "DECIMAL"); - m.put(java.sql.Timestamp.class, "TIMESTAMP"); - m.put(java.util.Date.class, "TIMESTAMP"); - m.put(java.sql.Date.class, "DATE"); - m.put(java.sql.Time.class, "TIME"); - m.put(byte[].class, "BLOB"); - m.put(UUID.class, "UUID"); - - // map primitives - m.put(boolean.class, m.get(Boolean.class)); - m.put(byte.class, m.get(Byte.class)); - m.put(short.class, m.get(Short.class)); - m.put(int.class, m.get(Integer.class)); - m.put(long.class, m.get(Long.class)); - m.put(float.class, m.get(Float.class)); - m.put(double.class, m.get(Double.class)); - } - - /** - * Convert SQL type aliases to the list of supported types. This map is used - * by generation and validation. - */ - private static final Map SQL_TYPES = new HashMap(); - - static { - Map m = SQL_TYPES; - m.put("CHAR", "VARCHAR"); - m.put("CHARACTER", "VARCHAR"); - m.put("NCHAR", "VARCHAR"); - m.put("VARCHAR_CASESENSITIVE", "VARCHAR"); - m.put("VARCHAR_IGNORECASE", "VARCHAR"); - m.put("LONGVARCHAR", "VARCHAR"); - m.put("VARCHAR2", "VARCHAR"); - m.put("NVARCHAR", "VARCHAR"); - m.put("NVARCHAR2", "VARCHAR"); - m.put("TEXT", "VARCHAR"); - m.put("NTEXT", "VARCHAR"); - m.put("TINYTEXT", "VARCHAR"); - m.put("MEDIUMTEXT", "VARCHAR"); - m.put("LONGTEXT", "VARCHAR"); - m.put("CLOB", "VARCHAR"); - m.put("NCLOB", "VARCHAR"); - - // logic - m.put("BIT", "BOOLEAN"); - m.put("BOOL", "BOOLEAN"); - - // numeric - m.put("BYTE", "TINYINT"); - m.put("INT2", "SMALLINT"); - m.put("YEAR", "SMALLINT"); - m.put("INTEGER", "INT"); - m.put("MEDIUMINT", "INT"); - m.put("INT4", "INT"); - m.put("SIGNED", "INT"); - m.put("INT8", "BIGINT"); - m.put("IDENTITY", "BIGINT"); - m.put("SERIAL", "INT"); - m.put("BIGSERIAL", "BIGINT"); - - // decimal - m.put("NUMBER", "DECIMAL"); - m.put("DEC", "DECIMAL"); - m.put("NUMERIC", "DECIMAL"); - m.put("FLOAT", "DOUBLE"); - m.put("FLOAT4", "DOUBLE"); - m.put("FLOAT8", "DOUBLE"); - m.put("DOUBLE PRECISION", "DOUBLE"); - - // date - m.put("DATETIME", "TIMESTAMP"); - m.put("SMALLDATETIME", "TIMESTAMP"); - - // binary types - m.put("TINYBLOB", "BLOB"); - m.put("MEDIUMBLOB", "BLOB"); - m.put("LONGBLOB", "BLOB"); - m.put("IMAGE", "BLOB"); - m.put("OID", "BLOB"); - } - - private static final List KEYWORDS = Arrays.asList("abstract", "assert", "boolean", "break", - "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", - "enum", "extends", "final", "finally", "float", "for", "goto", "if", "implements", "import", - "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected", - "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", - "throw", "throws", "transient", "try", "void", "volatile", "while", "false", "null", "true"); - - /** - * Returns a SQL type mapping for a Java class. - * - * @param fieldDef - * the field to map - * @return - */ - static String getDataType(FieldDefinition fieldDef) { - Class fieldClass = fieldDef.field.getType(); - if (fieldClass.isEnum()) { - switch (fieldDef.enumType) { - case ORDINAL: - return "INT"; - case ENUMID: - String sqlType = SUPPORTED_TYPES.get(fieldDef.enumTypeClass); - if (sqlType == null) { - throw new IciqlException("Unsupported enum mapping type {0} for {1}", - fieldDef.enumTypeClass, fieldDef.columnName); - } - return sqlType; - case NAME: - default: - return "VARCHAR"; - } - } - if (SUPPORTED_TYPES.containsKey(fieldClass)) { - return SUPPORTED_TYPES.get(fieldClass); - } - throw new IciqlException("Unsupported type " + fieldClass.getName()); - } - - /** - * Returns the Java class for a given SQL type. - * - * @param sqlType - * @param dateTimeClass - * the preferred date class (java.util.Date or - * java.sql.Timestamp) - * @return - */ - static Class getClassForSqlType(String sqlType, Class dateTimeClass) { - sqlType = sqlType.toUpperCase(); - // XXX dropping "UNSIGNED" or parts like that could be trouble - sqlType = sqlType.split(" ")[0].trim(); - if (sqlType.indexOf('(') > -1) { - // strip out length or precision - sqlType = sqlType.substring(0, sqlType.indexOf('(')); - } - - if (SQL_TYPES.containsKey(sqlType)) { - // convert the sqlType to a standard type - sqlType = SQL_TYPES.get(sqlType); - } - Class mappedClass = null; - for (Class clazz : SUPPORTED_TYPES.keySet()) { - if (clazz.isPrimitive()) { - // do not map from SQL TYPE to primitive type - continue; - } - if (SUPPORTED_TYPES.get(clazz).equalsIgnoreCase(sqlType)) { - mappedClass = clazz; - - break; - } - } - if (mappedClass != null) { - if (mappedClass.equals(java.util.Date.class) || mappedClass.equals(java.sql.Timestamp.class)) { - return dateTimeClass; - } - return mappedClass; - } - return null; - } - - /** - * Tries to create a convert a SQL table name to a camel case class name. - * - * @param tableName - * the SQL table name - * @return the class name - */ - static String convertTableToClassName(String tableName) { - String[] chunks = StringUtils.arraySplit(tableName, '_', false); - StringBuilder className = new StringBuilder(); - for (String chunk : chunks) { - if (chunk.length() == 0) { - // leading or trailing _ - continue; - } - String[] subchunks = StringUtils.arraySplit(chunk, ' ', false); - for (String subchunk : subchunks) { - if (subchunk.length() == 0) { - // leading or trailing space - continue; - } - className.append(Character.toUpperCase(subchunk.charAt(0))); - className.append(subchunk.substring(1).toLowerCase()); - } - } - return className.toString(); - } - - /** - * Ensures that SQL column names don't collide with Java keywords. - * - * @param columnName - * the column name - * @return the Java field name - */ - static String convertColumnToFieldName(String columnName) { - String lower = columnName.toLowerCase(); - if (KEYWORDS.contains(lower)) { - lower += "Value"; - } - return lower; - } - - /** - * Converts a DEFAULT clause value into an object. - * - * @param field - * definition - * @return object - */ - static Object getDefaultValue(FieldDefinition def, Class dateTimeClass) { - Class valueType = getClassForSqlType(def.dataType, dateTimeClass); - if (String.class.isAssignableFrom(valueType)) { - if (StringUtils.isNullOrEmpty(def.defaultValue)) { - // literal default must be specified within single quotes - return null; - } - if (def.defaultValue.charAt(0) == '\'' - && def.defaultValue.charAt(def.defaultValue.length() - 1) == '\'') { - // strip leading and trailing single quotes - return def.defaultValue.substring(1, def.defaultValue.length() - 1).trim(); - } - return def.defaultValue; - } - - if (StringUtils.isNullOrEmpty(def.defaultValue)) { - // can not create object from empty string - return null; - } - - // strip leading and trailing single quotes - String content = def.defaultValue; - if (content.charAt(0) == '\'') { - content = content.substring(1); - } - if (content.charAt(content.length() - 1) == '\'') { - content = content.substring(0, content.length() - 2); - } - - if (StringUtils.isNullOrEmpty(content)) { - // can not create object from empty string - return null; - } - - if (Boolean.class.isAssignableFrom(valueType) || boolean.class.isAssignableFrom(valueType)) { - return Boolean.parseBoolean(content); - } - - if (Number.class.isAssignableFrom(valueType)) { - try { - // delegate to static valueOf() method to parse string - Method m = valueType.getMethod("valueOf", String.class); - return m.invoke(null, content); - } catch (NumberFormatException e) { - throw new IciqlException(e, "Failed to parse {0} as a number!", def.defaultValue); - } catch (Throwable t) { - } - } - - String dateRegex = "[0-9]{1,4}[-/\\.][0-9]{1,2}[-/\\.][0-9]{1,2}"; - String timeRegex = "[0-2]{1}[0-9]{1}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}"; - - if (java.sql.Date.class.isAssignableFrom(valueType)) { - // this may be a little loose.... - // 00-00-00 - // 00/00/00 - // 00.00.00 - Pattern pattern = Pattern.compile(dateRegex); - if (pattern.matcher(content).matches()) { - DateFormat df = DateFormat.getDateInstance(); - try { - return df.parse(content); - } catch (Exception e) { - throw new IciqlException(e, "Failed to parse {0} as a date!", def.defaultValue); - } - } - } - - if (java.sql.Time.class.isAssignableFrom(valueType)) { - // 00:00:00 - Pattern pattern = Pattern.compile(timeRegex); - if (pattern.matcher(content).matches()) { - DateFormat df = DateFormat.getTimeInstance(); - try { - return df.parse(content); - } catch (Exception e) { - throw new IciqlException(e, "Failed to parse {0} as a time!", def.defaultValue); - } - } - } - - if (java.util.Date.class.isAssignableFrom(valueType)) { - // this may be a little loose.... - // 00-00-00 00:00:00 - // 00/00/00T00:00:00 - // 00.00.00T00:00:00 - Pattern pattern = Pattern.compile(dateRegex + "." + timeRegex); - if (pattern.matcher(content).matches()) { - DateFormat df = DateFormat.getDateTimeInstance(); - try { - return df.parse(content); - } catch (Exception e) { - throw new IciqlException(e, "Failed to parse {0} as a datetimestamp!", def.defaultValue); - } - } - } - return content; - } - - /** - * Converts the object into a DEFAULT clause value. - * - * @param o - * the default object - * @return the value formatted for a DEFAULT clause - */ - static String formatDefaultValue(Object o) { - Class objectClass = o.getClass(); - String value = null; - if (Number.class.isAssignableFrom(objectClass)) { - // NUMBER - return ((Number) o).toString(); - } else if (Boolean.class.isAssignableFrom(objectClass)) { - // BOOLEAN - return o.toString(); - } else if (java.sql.Date.class.isAssignableFrom(objectClass)) { - // DATE - value = new SimpleDateFormat("yyyy-MM-dd").format((Date) o); - } else if (java.sql.Time.class.isAssignableFrom(objectClass)) { - // TIME - value = new SimpleDateFormat("HH:mm:ss").format((Date) o); - } else if (Date.class.isAssignableFrom(objectClass)) { - // DATETIME - value = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) o); - } else if (String.class.isAssignableFrom(objectClass)) { - // STRING - value = o.toString(); - } - if (value == null) { - return "''"; - } - return MessageFormat.format("''{0}''", value); - } - - /** - * Checks the formatting of IQColumn.defaultValue(). - * - * @param defaultValue - * the default value - * @return true if it is - */ - static boolean isProperlyFormattedDefaultValue(String defaultValue) { - if (isNullOrEmpty(defaultValue)) { - return true; - } - Pattern literalDefault = Pattern.compile("'.*'"); - Pattern functionDefault = Pattern.compile("[^'].*[^']"); - return literalDefault.matcher(defaultValue).matches() - || functionDefault.matcher(defaultValue).matches(); - } - - /** - * Checks to see if the default value matches the class. - * - * @param modelClass - * the class - * @param defaultValue - * the value - * @return true if it does - */ - static boolean isValidDefaultValue(Class modelClass, String defaultValue) { - - if (defaultValue == null) { - // NULL - return true; - } - if (defaultValue.trim().length() == 0) { - // NULL (effectively) - return true; - } - - // function / variable - Pattern functionDefault = Pattern.compile("[^'].*[^']"); - if (functionDefault.matcher(defaultValue).matches()) { - // hard to validate this since its in the database - // assume it is good - return true; - } - - // STRING - if (modelClass == String.class) { - Pattern stringDefault = Pattern.compile("'(.|\\n)*'"); - return stringDefault.matcher(defaultValue).matches(); - } - - String dateRegex = "[0-9]{1,4}[-/\\.][0-9]{1,2}[-/\\.][0-9]{1,2}"; - String timeRegex = "[0-2]{1}[0-9]{1}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}"; - - // TIMESTAMP - if (modelClass == java.util.Date.class || modelClass == java.sql.Timestamp.class) { - // this may be a little loose.... - // 00-00-00 00:00:00 - // 00/00/00T00:00:00 - // 00.00.00T00:00:00 - Pattern pattern = Pattern.compile("'" + dateRegex + "." + timeRegex + "'"); - return pattern.matcher(defaultValue).matches(); - } - - // DATE - if (modelClass == java.sql.Date.class) { - // this may be a little loose.... - // 00-00-00 - // 00/00/00 - // 00.00.00 - Pattern pattern = Pattern.compile("'" + dateRegex + "'"); - return pattern.matcher(defaultValue).matches(); - } - - // TIME - if (modelClass == java.sql.Time.class) { - // 00:00:00 - Pattern pattern = Pattern.compile("'" + timeRegex + "'"); - return pattern.matcher(defaultValue).matches(); - } - - // NUMBER - if (Number.class.isAssignableFrom(modelClass)) { - // strip single quotes - String unquoted = defaultValue; - if (unquoted.charAt(0) == '\'') { - unquoted = unquoted.substring(1); - } - if (unquoted.charAt(unquoted.length() - 1) == '\'') { - unquoted = unquoted.substring(0, unquoted.length() - 1); - } - - try { - // delegate to static valueOf() method to parse string - Method m = modelClass.getMethod("valueOf", String.class); - m.invoke(null, unquoted); - } catch (NumberFormatException ex) { - return false; - } catch (Throwable t) { - } - } - return true; - } + /** + * The list of supported data types. It is used by the runtime mapping for + * CREATE statements. + */ + private static final Map, String> SUPPORTED_TYPES = new HashMap, String>(); + + static { + Map, String> m = SUPPORTED_TYPES; + m.put(String.class, "VARCHAR"); + m.put(Boolean.class, "BOOLEAN"); + m.put(Byte.class, "TINYINT"); + m.put(Short.class, "SMALLINT"); + m.put(Integer.class, "INT"); + m.put(Long.class, "BIGINT"); + m.put(Float.class, "REAL"); + m.put(Double.class, "DOUBLE"); + m.put(BigDecimal.class, "DECIMAL"); + m.put(java.sql.Timestamp.class, "TIMESTAMP"); + m.put(java.util.Date.class, "TIMESTAMP"); + m.put(java.sql.Date.class, "DATE"); + m.put(java.sql.Time.class, "TIME"); + m.put(byte[].class, "BLOB"); + m.put(UUID.class, "UUID"); + + // map primitives + m.put(boolean.class, m.get(Boolean.class)); + m.put(byte.class, m.get(Byte.class)); + m.put(short.class, m.get(Short.class)); + m.put(int.class, m.get(Integer.class)); + m.put(long.class, m.get(Long.class)); + m.put(float.class, m.get(Float.class)); + m.put(double.class, m.get(Double.class)); + } + + /** + * Convert SQL type aliases to the list of supported types. This map is used + * by generation and validation. + */ + private static final Map SQL_TYPES = new HashMap(); + + static { + Map m = SQL_TYPES; + m.put("CHAR", "VARCHAR"); + m.put("CHARACTER", "VARCHAR"); + m.put("NCHAR", "VARCHAR"); + m.put("VARCHAR_CASESENSITIVE", "VARCHAR"); + m.put("VARCHAR_IGNORECASE", "VARCHAR"); + m.put("LONGVARCHAR", "VARCHAR"); + m.put("VARCHAR2", "VARCHAR"); + m.put("NVARCHAR", "VARCHAR"); + m.put("NVARCHAR2", "VARCHAR"); + m.put("TEXT", "VARCHAR"); + m.put("NTEXT", "VARCHAR"); + m.put("TINYTEXT", "VARCHAR"); + m.put("MEDIUMTEXT", "VARCHAR"); + m.put("LONGTEXT", "VARCHAR"); + m.put("CLOB", "VARCHAR"); + m.put("NCLOB", "VARCHAR"); + + // logic + m.put("BIT", "BOOLEAN"); + m.put("BOOL", "BOOLEAN"); + + // numeric + m.put("BYTE", "TINYINT"); + m.put("INT2", "SMALLINT"); + m.put("YEAR", "SMALLINT"); + m.put("INTEGER", "INT"); + m.put("MEDIUMINT", "INT"); + m.put("INT4", "INT"); + m.put("SIGNED", "INT"); + m.put("INT8", "BIGINT"); + m.put("IDENTITY", "BIGINT"); + m.put("SERIAL", "INT"); + m.put("BIGSERIAL", "BIGINT"); + + // decimal + m.put("NUMBER", "DECIMAL"); + m.put("DEC", "DECIMAL"); + m.put("NUMERIC", "DECIMAL"); + m.put("FLOAT", "DOUBLE"); + m.put("FLOAT4", "DOUBLE"); + m.put("FLOAT8", "DOUBLE"); + m.put("DOUBLE PRECISION", "DOUBLE"); + + // date + m.put("DATETIME", "TIMESTAMP"); + m.put("SMALLDATETIME", "TIMESTAMP"); + + // binary types + m.put("TINYBLOB", "BLOB"); + m.put("MEDIUMBLOB", "BLOB"); + m.put("LONGBLOB", "BLOB"); + m.put("IMAGE", "BLOB"); + m.put("OID", "BLOB"); + } + + private static final List KEYWORDS = Arrays.asList("abstract", "assert", "boolean", "break", + "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", + "enum", "extends", "final", "finally", "float", "for", "goto", "if", "implements", "import", + "instanceof", "int", "interface", "long", "native", "new", "package", "private", "protected", + "public", "return", "short", "static", "strictfp", "super", "switch", "synchronized", "this", + "throw", "throws", "transient", "try", "void", "volatile", "while", "false", "null", "true"); + + /** + * Returns a SQL type mapping for a Java class. + * + * @param fieldDef the field to map + * @return + */ + static String getDataType(FieldDefinition fieldDef) { + Class fieldClass = fieldDef.field.getType(); + if (fieldClass.isEnum()) { + switch (fieldDef.enumType) { + case ORDINAL: + return "INT"; + case ENUMID: + String sqlType = SUPPORTED_TYPES.get(fieldDef.enumTypeClass); + if (sqlType == null) { + throw new IciqlException("Unsupported enum mapping type {0} for {1}", + fieldDef.enumTypeClass, fieldDef.columnName); + } + return sqlType; + case NAME: + default: + return "VARCHAR"; + } + } + if (SUPPORTED_TYPES.containsKey(fieldClass)) { + return SUPPORTED_TYPES.get(fieldClass); + } + throw new IciqlException("Unsupported type " + fieldClass.getName()); + } + + /** + * Returns the Java class for a given SQL type. + * + * @param sqlType + * @param dateTimeClass the preferred date class (java.util.Date or + * java.sql.Timestamp) + * @return + */ + static Class getClassForSqlType(String sqlType, Class dateTimeClass) { + sqlType = sqlType.toUpperCase(); + // XXX dropping "UNSIGNED" or parts like that could be trouble + sqlType = sqlType.split(" ")[0].trim(); + if (sqlType.indexOf('(') > -1) { + // strip out length or precision + sqlType = sqlType.substring(0, sqlType.indexOf('(')); + } + + if (SQL_TYPES.containsKey(sqlType)) { + // convert the sqlType to a standard type + sqlType = SQL_TYPES.get(sqlType); + } + Class mappedClass = null; + for (Class clazz : SUPPORTED_TYPES.keySet()) { + if (clazz.isPrimitive()) { + // do not map from SQL TYPE to primitive type + continue; + } + if (SUPPORTED_TYPES.get(clazz).equalsIgnoreCase(sqlType)) { + mappedClass = clazz; + + break; + } + } + if (mappedClass != null) { + if (mappedClass.equals(java.util.Date.class) || mappedClass.equals(java.sql.Timestamp.class)) { + return dateTimeClass; + } + return mappedClass; + } + return null; + } + + /** + * Tries to create a convert a SQL table name to a camel case class name. + * + * @param tableName the SQL table name + * @return the class name + */ + static String convertTableToClassName(String tableName) { + String[] chunks = StringUtils.arraySplit(tableName, '_', false); + StringBuilder className = new StringBuilder(); + for (String chunk : chunks) { + if (chunk.length() == 0) { + // leading or trailing _ + continue; + } + String[] subchunks = StringUtils.arraySplit(chunk, ' ', false); + for (String subchunk : subchunks) { + if (subchunk.length() == 0) { + // leading or trailing space + continue; + } + className.append(Character.toUpperCase(subchunk.charAt(0))); + className.append(subchunk.substring(1).toLowerCase()); + } + } + return className.toString(); + } + + /** + * Ensures that SQL column names don't collide with Java keywords. + * + * @param columnName the column name + * @return the Java field name + */ + static String convertColumnToFieldName(String columnName) { + String lower = columnName.toLowerCase(); + if (KEYWORDS.contains(lower)) { + lower += "Value"; + } + return lower; + } + + /** + * Converts a DEFAULT clause value into an object. + * + * @param field definition + * @return object + */ + static Object getDefaultValue(FieldDefinition def, Class dateTimeClass) { + Class valueType = getClassForSqlType(def.dataType, dateTimeClass); + if (String.class.isAssignableFrom(valueType)) { + if (StringUtils.isNullOrEmpty(def.defaultValue)) { + // literal default must be specified within single quotes + return null; + } + if (def.defaultValue.charAt(0) == '\'' + && def.defaultValue.charAt(def.defaultValue.length() - 1) == '\'') { + // strip leading and trailing single quotes + return def.defaultValue.substring(1, def.defaultValue.length() - 1).trim(); + } + return def.defaultValue; + } + + if (StringUtils.isNullOrEmpty(def.defaultValue)) { + // can not create object from empty string + return null; + } + + // strip leading and trailing single quotes + String content = def.defaultValue; + if (content.charAt(0) == '\'') { + content = content.substring(1); + } + if (content.charAt(content.length() - 1) == '\'') { + content = content.substring(0, content.length() - 2); + } + + if (StringUtils.isNullOrEmpty(content)) { + // can not create object from empty string + return null; + } + + if (Boolean.class.isAssignableFrom(valueType) || boolean.class.isAssignableFrom(valueType)) { + return Boolean.parseBoolean(content); + } + + if (Number.class.isAssignableFrom(valueType)) { + try { + // delegate to static valueOf() method to parse string + Method m = valueType.getMethod("valueOf", String.class); + return m.invoke(null, content); + } catch (NumberFormatException e) { + throw new IciqlException(e, "Failed to parse {0} as a number!", def.defaultValue); + } catch (Throwable t) { + } + } + + String dateRegex = "[0-9]{1,4}[-/\\.][0-9]{1,2}[-/\\.][0-9]{1,2}"; + String timeRegex = "[0-2]{1}[0-9]{1}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}"; + + if (java.sql.Date.class.isAssignableFrom(valueType)) { + // this may be a little loose.... + // 00-00-00 + // 00/00/00 + // 00.00.00 + Pattern pattern = Pattern.compile(dateRegex); + if (pattern.matcher(content).matches()) { + DateFormat df = DateFormat.getDateInstance(); + try { + return df.parse(content); + } catch (Exception e) { + throw new IciqlException(e, "Failed to parse {0} as a date!", def.defaultValue); + } + } + } + + if (java.sql.Time.class.isAssignableFrom(valueType)) { + // 00:00:00 + Pattern pattern = Pattern.compile(timeRegex); + if (pattern.matcher(content).matches()) { + DateFormat df = DateFormat.getTimeInstance(); + try { + return df.parse(content); + } catch (Exception e) { + throw new IciqlException(e, "Failed to parse {0} as a time!", def.defaultValue); + } + } + } + + if (java.util.Date.class.isAssignableFrom(valueType)) { + // this may be a little loose.... + // 00-00-00 00:00:00 + // 00/00/00T00:00:00 + // 00.00.00T00:00:00 + Pattern pattern = Pattern.compile(dateRegex + "." + timeRegex); + if (pattern.matcher(content).matches()) { + DateFormat df = DateFormat.getDateTimeInstance(); + try { + return df.parse(content); + } catch (Exception e) { + throw new IciqlException(e, "Failed to parse {0} as a datetimestamp!", def.defaultValue); + } + } + } + return content; + } + + /** + * Converts the object into a DEFAULT clause value. + * + * @param o the default object + * @return the value formatted for a DEFAULT clause + */ + static String formatDefaultValue(Object o) { + Class objectClass = o.getClass(); + String value = null; + if (Number.class.isAssignableFrom(objectClass)) { + // NUMBER + return ((Number) o).toString(); + } else if (Boolean.class.isAssignableFrom(objectClass)) { + // BOOLEAN + return o.toString(); + } else if (java.sql.Date.class.isAssignableFrom(objectClass)) { + // DATE + value = new SimpleDateFormat("yyyy-MM-dd").format((Date) o); + } else if (java.sql.Time.class.isAssignableFrom(objectClass)) { + // TIME + value = new SimpleDateFormat("HH:mm:ss").format((Date) o); + } else if (Date.class.isAssignableFrom(objectClass)) { + // DATETIME + value = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format((Date) o); + } else if (String.class.isAssignableFrom(objectClass)) { + // STRING + value = o.toString(); + } + if (value == null) { + return "''"; + } + return MessageFormat.format("''{0}''", value); + } + + /** + * Checks the formatting of IQColumn.defaultValue(). + * + * @param defaultValue the default value + * @return true if it is + */ + static boolean isProperlyFormattedDefaultValue(String defaultValue) { + if (isNullOrEmpty(defaultValue)) { + return true; + } + Pattern literalDefault = Pattern.compile("'.*'"); + Pattern functionDefault = Pattern.compile("[^'].*[^']"); + return literalDefault.matcher(defaultValue).matches() + || functionDefault.matcher(defaultValue).matches(); + } + + /** + * Checks to see if the default value matches the class. + * + * @param modelClass the class + * @param defaultValue the value + * @return true if it does + */ + static boolean isValidDefaultValue(Class modelClass, String defaultValue) { + + if (defaultValue == null) { + // NULL + return true; + } + if (defaultValue.trim().length() == 0) { + // NULL (effectively) + return true; + } + + // function / variable + Pattern functionDefault = Pattern.compile("[^'].*[^']"); + if (functionDefault.matcher(defaultValue).matches()) { + // hard to validate this since its in the database + // assume it is good + return true; + } + + // STRING + if (modelClass == String.class) { + Pattern stringDefault = Pattern.compile("'(.|\\n)*'"); + return stringDefault.matcher(defaultValue).matches(); + } + + String dateRegex = "[0-9]{1,4}[-/\\.][0-9]{1,2}[-/\\.][0-9]{1,2}"; + String timeRegex = "[0-2]{1}[0-9]{1}:[0-5]{1}[0-9]{1}:[0-5]{1}[0-9]{1}"; + + // TIMESTAMP + if (modelClass == java.util.Date.class || modelClass == java.sql.Timestamp.class) { + // this may be a little loose.... + // 00-00-00 00:00:00 + // 00/00/00T00:00:00 + // 00.00.00T00:00:00 + Pattern pattern = Pattern.compile("'" + dateRegex + "." + timeRegex + "'"); + return pattern.matcher(defaultValue).matches(); + } + + // DATE + if (modelClass == java.sql.Date.class) { + // this may be a little loose.... + // 00-00-00 + // 00/00/00 + // 00.00.00 + Pattern pattern = Pattern.compile("'" + dateRegex + "'"); + return pattern.matcher(defaultValue).matches(); + } + + // TIME + if (modelClass == java.sql.Time.class) { + // 00:00:00 + Pattern pattern = Pattern.compile("'" + timeRegex + "'"); + return pattern.matcher(defaultValue).matches(); + } + + // NUMBER + if (Number.class.isAssignableFrom(modelClass)) { + // strip single quotes + String unquoted = defaultValue; + if (unquoted.charAt(0) == '\'') { + unquoted = unquoted.substring(1); + } + if (unquoted.charAt(unquoted.length() - 1) == '\'') { + unquoted = unquoted.substring(0, unquoted.length() - 1); + } + + try { + // delegate to static valueOf() method to parse string + Method m = modelClass.getMethod("valueOf", String.class); + m.invoke(null, unquoted); + } catch (NumberFormatException ex) { + return false; + } catch (Throwable t) { + } + } + return true; + } } diff --git a/src/main/java/com/iciql/NestedConditions.java b/src/main/java/com/iciql/NestedConditions.java index 5c92a86..c60645c 100644 --- a/src/main/java/com/iciql/NestedConditions.java +++ b/src/main/java/com/iciql/NestedConditions.java @@ -19,114 +19,114 @@ package com.iciql; public abstract class NestedConditions { - public static class And extends NestedConditions { + public static class And extends NestedConditions { - public And(Db db, T alias) { - super(db, alias); - } + public And(Db db, T alias) { + super(db, alias); + } - protected QueryCondition and(boolean x) { - return where.and(x); - } + protected QueryCondition and(boolean x) { + return where.and(x); + } - protected QueryCondition and(byte x) { - return where.and(x); - } + protected QueryCondition and(byte x) { + return where.and(x); + } - protected QueryCondition and(short x) { - return where.and(x); - } + protected QueryCondition and(short x) { + return where.and(x); + } - protected QueryCondition and(int x) { - return where.and(x); - } + protected QueryCondition and(int x) { + return where.and(x); + } - protected QueryCondition and(long x) { - return where.and(x); - } + protected QueryCondition and(long x) { + return where.and(x); + } - protected QueryCondition and(float x) { - return where.and(x); - } + protected QueryCondition and(float x) { + return where.and(x); + } - protected QueryCondition and(double x) { - return where.and(x); - } + protected QueryCondition and(double x) { + return where.and(x); + } - protected
QueryCondition and(A x) { - return where.and(x); - } + protected QueryCondition and(A x) { + return where.and(x); + } - protected QueryWhere and(And conditions) { - where.andOpen(); - where.query.addConditionToken(conditions.where.query); - return where.close(); - } + protected QueryWhere and(And conditions) { + where.andOpen(); + where.query.addConditionToken(conditions.where.query); + return where.close(); + } - protected QueryWhere and(Or conditions) { - where.andOpen(); - where.query.addConditionToken(conditions.where.query); - return where.close(); - } + protected QueryWhere and(Or conditions) { + where.andOpen(); + where.query.addConditionToken(conditions.where.query); + return where.close(); + } - } + } - public static class Or extends NestedConditions { + public static class Or extends NestedConditions { - public Or(Db db, T alias) { - super(db, alias); - } + public Or(Db db, T alias) { + super(db, alias); + } - protected QueryCondition or(boolean x) { - return where.or(x); - } + protected QueryCondition or(boolean x) { + return where.or(x); + } - protected QueryCondition or(byte x) { - return where.or(x); - } + protected QueryCondition or(byte x) { + return where.or(x); + } - protected QueryCondition or(short x) { - return where.or(x); - } + protected QueryCondition or(short x) { + return where.or(x); + } - protected QueryCondition or(int x) { - return where.or(x); - } + protected QueryCondition or(int x) { + return where.or(x); + } - protected QueryCondition or(long x) { - return where.or(x); - } + protected QueryCondition or(long x) { + return where.or(x); + } - protected QueryCondition or(float x) { - return where.or(x); - } + protected QueryCondition or(float x) { + return where.or(x); + } - protected QueryCondition or(double x) { - return where.or(x); - } + protected QueryCondition or(double x) { + return where.or(x); + } - protected QueryCondition or(A x) { - return where.or(x); - } + protected QueryCondition or(A x) { + return where.or(x); + } - protected QueryWhere or(And conditions) { - where.orOpen(); - where.query.addConditionToken(conditions.where.query); - return where.close(); - } + protected QueryWhere or(And conditions) { + where.orOpen(); + where.query.addConditionToken(conditions.where.query); + return where.close(); + } - protected QueryWhere or(Or conditions) { - where.orOpen(); - where.query.addConditionToken(conditions.where.query); - return where.close(); - } + protected QueryWhere or(Or conditions) { + where.orOpen(); + where.query.addConditionToken(conditions.where.query); + return where.close(); + } - } + } - QueryWhere where; + QueryWhere where; - private NestedConditions(Db db, T alias) { - where = new QueryWhere(Query.rebuild(db, alias)); - } + private NestedConditions(Db db, T alias) { + where = new QueryWhere(Query.rebuild(db, alias)); + } } diff --git a/src/main/java/com/iciql/OrderExpression.java b/src/main/java/com/iciql/OrderExpression.java index f450bfb..9694da1 100644 --- a/src/main/java/com/iciql/OrderExpression.java +++ b/src/main/java/com/iciql/OrderExpression.java @@ -19,37 +19,36 @@ package com.iciql; /** * An expression to order by in a query. - * - * @param - * the query data type + * + * @param the query data type */ class OrderExpression { - private Query query; - private Object expression; - private boolean desc; - private boolean nullsFirst; - private boolean nullsLast; + private Query query; + private Object expression; + private boolean desc; + private boolean nullsFirst; + private boolean nullsLast; - OrderExpression(Query query, Object expression, boolean desc, boolean nullsFirst, boolean nullsLast) { - this.query = query; - this.expression = expression; - this.desc = desc; - this.nullsFirst = nullsFirst; - this.nullsLast = nullsLast; - } + OrderExpression(Query query, Object expression, boolean desc, boolean nullsFirst, boolean nullsLast) { + this.query = query; + this.expression = expression; + this.desc = desc; + this.nullsFirst = nullsFirst; + this.nullsLast = nullsLast; + } - void appendSQL(SQLStatement stat) { - query.appendSQL(stat, null, expression); - if (desc) { - stat.appendSQL(" DESC"); - } - if (nullsLast) { - stat.appendSQL(" NULLS LAST"); - } - if (nullsFirst) { - stat.appendSQL(" NULLS FIRST"); - } - } + void appendSQL(SQLStatement stat) { + query.appendSQL(stat, null, expression); + if (desc) { + stat.appendSQL(" DESC"); + } + if (nullsLast) { + stat.appendSQL(" NULLS LAST"); + } + if (nullsFirst) { + stat.appendSQL(" NULLS FIRST"); + } + } } diff --git a/src/main/java/com/iciql/Query.java b/src/main/java/com/iciql/Query.java index 7a8a1ff..d26e7b7 100644 --- a/src/main/java/com/iciql/Query.java +++ b/src/main/java/com/iciql/Query.java @@ -17,15 +17,6 @@ package com.iciql; -import java.lang.reflect.Field; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.List; - import com.iciql.Iciql.DataTypeAdapter; import com.iciql.Iciql.EnumType; import com.iciql.NestedConditions.And; @@ -35,1015 +26,1002 @@ import com.iciql.util.IciqlLogger; import com.iciql.util.JdbcUtils; import com.iciql.util.Utils; +import java.lang.reflect.Field; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; + /** * This class represents a query. * - * @param - * the return type + * @param the return type */ public class Query { - private Db db; - private SelectTable from; - private ArrayList conditions = Utils.newArrayList(); - private ArrayList updateColumnDeclarations = Utils.newArrayList(); - private int conditionDepth = 0; - private ArrayList> joins = Utils.newArrayList(); - private final IdentityHashMap> aliasMap = Utils.newIdentityHashMap(); - private ArrayList> orderByList = Utils.newArrayList(); - private ArrayList groupByExpressions = Utils.newArrayList(); - private long limit; - private long offset; - - private Query(Db db) { - this.db = db; - } - - /** - * from() is a static factory method to build a Query object. - * - * @param db - * @param alias - * @return a query object - */ - @SuppressWarnings("unchecked") - static Query from(Db db, T alias) { - Query query = new Query(db); - TableDefinition def = (TableDefinition) db.define(alias.getClass()); - query.from = new SelectTable(db, query, alias, false); - def.initSelectObject(query.from, alias, query.aliasMap, false); - return query; - } - - @SuppressWarnings("unchecked") - static Query rebuild(Db db, T alias) { - Query query = new Query(db); - TableDefinition def = (TableDefinition) db.define(alias.getClass()); - query.from = new SelectTable(db, query, alias, false); - def.initSelectObject(query.from, alias, query.aliasMap, true); - return query; - } - - public long selectCount() { - SQLStatement stat = getSelectStatement(false); - stat.appendSQL("COUNT(*) "); - appendFromWhere(stat); - ResultSet rs = stat.executeQuery(); - try { - rs.next(); - long value = rs.getLong(1); - return value; - } catch (SQLException e) { - throw IciqlException.fromSQL(stat.getSQL(), e); - } finally { - JdbcUtils.closeSilently(rs, true); - } - } - - public List select() { - return select(false); - } - - public T selectFirst() { - List list = limit(1).select(false); - return list.isEmpty() ? null : list.get(0); - } - - public List selectDistinct() { - return select(true); - } - - public X selectFirst(Z x) { - List list = limit(1).select(x); - return list.isEmpty() ? null : list.get(0); - } - - public void createView(Class viewClass) { - TableDefinition viewDef = db.define(viewClass); - - SQLStatement fromWhere = new SQLStatement(db); - appendFromWhere(fromWhere, false); - - SQLStatement stat = new SQLStatement(db); - db.getDialect().prepareCreateView(stat, viewDef, fromWhere.toSQL()); - IciqlLogger.create(stat.toSQL()); - stat.execute(); - } - - public void replaceView(Class viewClass) { - db.dropView(viewClass); - createView(viewClass); - } - - public String getSQL() { - SQLStatement stat = getSelectStatement(false); - stat.appendSQL("*"); - appendFromWhere(stat); - return stat.getSQL().trim(); - } - - /** - * toSQL returns a static string version of the query with runtime variables - * properly encoded. This method is also useful when combined with the where - * clause methods like isParameter() or atLeastParameter() which allows - * iciql to generate re-usable parameterized string statements. - * - * @return the sql query as plain text - */ - public String toSQL() { - return toSQL(false); - } - - /** - * toSQL returns a static string version of the query with runtime variables - * properly encoded. This method is also useful when combined with the where - * clause methods like isParameter() or atLeastParameter() which allows - * iciql to generate re-usable parameterized string statements. - * - * @param distinct - * if true SELECT DISTINCT is used for the query - * @return the sql query as plain text - */ - public String toSQL(boolean distinct) { - return toSQL(distinct, null); - } - - /** - * toSQL returns a static string version of the query with runtime variables - * properly encoded. This method is also useful when combined with the where - * clause methods like isParameter() or atLeastParameter() which allows - * iciql to generate re-usable parameterized string statements. - * - * @param distinct - * if true SELECT DISTINCT is used for the query - * @param k - * k is used to select only the columns of the specified alias - * for an inner join statement. An example of a generated - * statement is: SELECT DISTINCT t1.* FROM sometable AS t1 INNER - * JOIN othertable AS t2 ON t1.id = t2.id WHERE t2.flag = true - * without the alias parameter the statement would start with - * SELECT DISTINCT * FROM... - * @return the sql query as plain text - */ - public String toSQL(boolean distinct, K k) { - SQLStatement stat = new SQLStatement(getDb()); - if (updateColumnDeclarations.size() > 0) { - stat.appendSQL("UPDATE "); - from.appendSQL(stat); - stat.appendSQL(" SET "); - int i = 0; - for (UpdateColumn declaration : updateColumnDeclarations) { - if (i++ > 0) { - stat.appendSQL(", "); - } - declaration.appendSQL(stat); - } - appendWhere(stat); - } else { - stat.appendSQL("SELECT "); - if (distinct) { - stat.appendSQL("DISTINCT "); - } - if (k != null) { - SelectTable sel = getSelectTable(k); - if (sel == null) { - // unknown alias, use wildcard - IciqlLogger.warn("Alias {0} is not defined in the statement!", k.getClass()); - stat.appendSQL("*"); - } else if (isJoin()) { - // join query, use AS alias - String as = sel.getAs(); - stat.appendSQL(as + ".*"); - } else { - // schema.table.* - String schema = sel.getAliasDefinition().schemaName; - String table = sel.getAliasDefinition().tableName; - String as = getDb().getDialect().prepareTableName(schema, table); - stat.appendSQL(as + ".*"); - } - } else { - // alias unspecified, use wildcard - stat.appendSQL("*"); - } - appendFromWhere(stat); - } - return stat.toSQL().trim(); - } - - String toSubQuery(Z z) { - SQLStatement stat = getSelectStatement(false); - SelectColumn col = aliasMap.get(z); - String columnName = col.getFieldDefinition().columnName; - stat.appendColumn(columnName); - appendFromWhere(stat); - return stat.toSQL(); - } - - private List select(boolean distinct) { - List result = Utils.newArrayList(); - TableDefinition def = from.getAliasDefinition(); - SQLStatement stat = getSelectStatement(distinct); - def.appendSelectList(stat); - appendFromWhere(stat); - ResultSet rs = stat.executeQuery(); - try { - // SQLite returns pre-closed ResultSets for query results with 0 rows - if (!rs.isClosed()) { - int[] columns = def.mapColumns(db.getDialect(), false, rs); - while (rs.next()) { - T item = from.newObject(); - def.readRow(db.getDialect(), item, rs, columns); - result.add(item); - } - } - } catch (SQLException e) { - throw IciqlException.fromSQL(stat.getSQL(), e); - } finally { - JdbcUtils.closeSilently(rs, true); - } - return result; - } - - public int delete() { - SQLStatement stat = new SQLStatement(db); - stat.appendSQL("DELETE FROM "); - from.appendSQL(stat); - appendWhere(stat); - IciqlLogger.delete(stat.getSQL()); - return stat.executeUpdate(); - } - - public Query setNull(A field) { - return set(field).to(null); - } - - public UpdateColumnSet set(A field) { - from.getAliasDefinition().checkMultipleEnums(field); - return new UpdateColumnSet(this, field); - } - - public UpdateColumnSet set(boolean field) { - from.getAliasDefinition().checkMultipleBooleans(); - return setPrimitive(field); - } - - public UpdateColumnSet set(byte field) { - return setPrimitive(field); - } - - public UpdateColumnSet set(short field) { - return setPrimitive(field); - } - - public UpdateColumnSet set(int field) { - return setPrimitive(field); - } - - public UpdateColumnSet set(long field) { - return setPrimitive(field); - } - - public UpdateColumnSet set(float field) { - return setPrimitive(field); - } - - public UpdateColumnSet set(double field) { - return setPrimitive(field); - } - - private UpdateColumnSet setPrimitive(A field) { - A alias = getPrimitiveAliasByValue(field); - if (alias == null) { - // this will result in an unmapped field exception - return set(field); - } - return set(alias); - } - - public UpdateColumnIncrement increment(A field) { - return new UpdateColumnIncrement(this, field); - } - - public UpdateColumnIncrement increment(byte field) { - return incrementPrimitive(field); - } - - public UpdateColumnIncrement increment(short field) { - return incrementPrimitive(field); - } - - public UpdateColumnIncrement increment(int field) { - return incrementPrimitive(field); - } - - public UpdateColumnIncrement increment(long field) { - return incrementPrimitive(field); - } - - public UpdateColumnIncrement increment(float field) { - return incrementPrimitive(field); - } - - public UpdateColumnIncrement increment(double field) { - return incrementPrimitive(field); - } - - private UpdateColumnIncrement incrementPrimitive(A field) { - A alias = getPrimitiveAliasByValue(field); - if (alias == null) { - // this will result in an unmapped field exception - return increment(field); - } - return increment(alias); - } - - public int update() { - if (updateColumnDeclarations.size() == 0) { - throw new IciqlException("Missing set or increment call."); - } - SQLStatement stat = new SQLStatement(db); - stat.appendSQL("UPDATE "); - from.appendSQL(stat); - stat.appendSQL(" SET "); - int i = 0; - for (UpdateColumn declaration : updateColumnDeclarations) { - if (i++ > 0) { - stat.appendSQL(", "); - } - declaration.appendSQL(stat); - } - appendWhere(stat); - IciqlLogger.update(stat.getSQL()); - return stat.executeUpdate(); - } - - public List selectDistinct(Z x) { - return select(x, true); - } - - public List select(Z x) { - return select(x, false); - } - - @SuppressWarnings("unchecked") - private List select(Z x, boolean distinct) { - Class clazz = x.getClass(); - if (Db.isToken(x)) { - // selecting a function - return selectFunction((X) x, distinct); - } else { - // selecting a column - SelectColumn col = getColumnByReference(x); - if (col == null) { - col = getColumnByReference(getPrimitiveAliasByValue(x)); - } - if (col != null) { - return (List) selectColumn(col, clazz, distinct); - } - } - - // selecting into a new object type - Class enclosingClass = clazz.getEnclosingClass(); - if (enclosingClass != null) { - // anonymous inner class - clazz = clazz.getSuperclass(); - } - return select((Class) clazz, (X) x, distinct); - } - - private List select(Class clazz, X x, boolean distinct) { - List result = Utils.newArrayList(); - TableDefinition def = db.define(clazz); - SQLStatement stat = getSelectStatement(distinct); - def.appendSelectList(stat, this, x); - appendFromWhere(stat); - ResultSet rs = stat.executeQuery(); - try { - // SQLite returns pre-closed ResultSets for query results with 0 rows - if (!rs.isClosed()) { - int[] columns = def.mapColumns(db.getDialect(), false, rs); - while (rs.next()) { - X row = Utils.newObject(clazz); - def.readRow(db.getDialect(), row, rs, columns); - result.add(row); - } - } - } catch (SQLException e) { - throw IciqlException.fromSQL(stat.getSQL(), e); - } finally { - JdbcUtils.closeSilently(rs, true); - } - return result; - } - - @SuppressWarnings("unchecked") - private List selectFunction(X x, boolean distinct) { - SQLStatement stat = getSelectStatement(distinct); - appendSQL(stat, null, x); - appendFromWhere(stat); - ResultSet rs = stat.executeQuery(); - List result = Utils.newArrayList(); - try { - // SQLite returns pre-closed ResultSets for query results with 0 rows - if (!rs.isClosed()) { - while (rs.next()) { - X value = (X) rs.getObject(1); - result.add(value); - } - } - } catch (Exception e) { - throw IciqlException.fromSQL(stat.getSQL(), e); - } finally { - JdbcUtils.closeSilently(rs, true); - } - return result; - } - - @SuppressWarnings("unchecked") - private List selectColumn(SelectColumn col, Class clazz, boolean distinct) { - SQLStatement stat = getSelectStatement(distinct); - col.appendSQL(stat); - appendFromWhere(stat); - ResultSet rs = stat.executeQuery(); - List result = Utils.newArrayList(); - Class> typeAdapter = col.getFieldDefinition().typeAdapter; - try { - // SQLite returns pre-closed ResultSets for query results with 0 rows - if (!rs.isClosed()) { - while (rs.next()) { - X value = (X) db.getDialect().deserialize(rs, 1, clazz, typeAdapter); - result.add(value); - } - } - } catch (Exception e) { - throw IciqlException.fromSQL(stat.getSQL(), e); - } finally { - JdbcUtils.closeSilently(rs, true); - } - return result; - } - - private SQLStatement getSelectStatement(boolean distinct) { - SQLStatement stat = new SQLStatement(db); - stat.appendSQL("SELECT "); - if (distinct) { - stat.appendSQL("DISTINCT "); - } - return stat; - } - - /** - * Begin a primitive boolean field condition clause. - * - * @param x - * the primitive boolean field to query - * @return a query condition to continue building the condition - */ - public QueryCondition where(boolean x) { - from.getAliasDefinition().checkMultipleBooleans(); - return wherePrimitive(x); - } - - /** - * Begin a primitive short field condition clause. - * - * @param x - * the primitive short field to query - * @return a query condition to continue building the condition - */ - public QueryCondition where(byte x) { - return wherePrimitive(x); - } - - /** - * Begin a primitive short field condition clause. - * - * @param x - * the primitive short field to query - * @return a query condition to continue building the condition - */ - public QueryCondition where(short x) { - return wherePrimitive(x); - } - - /** - * Begin a primitive int field condition clause. - * - * @param x - * the primitive int field to query - * @return a query condition to continue building the condition - */ - public QueryCondition where(int x) { - return wherePrimitive(x); - } - - /** - * Begin a primitive long field condition clause. - * - * @param x - * the primitive long field to query - * @return a query condition to continue building the condition - */ - public QueryCondition where(long x) { - return wherePrimitive(x); - } - - /** - * Begin a primitive float field condition clause. - * - * @param x - * the primitive float field to query - * @return a query condition to continue building the condition - */ - public QueryCondition where(float x) { - return wherePrimitive(x); - } - - /** - * Begin a primitive double field condition clause. - * - * @param x - * the primitive double field to query - * @return a query condition to continue building the condition - */ - public QueryCondition where(double x) { - return wherePrimitive(x); - } - - /** - * Begins a primitive field condition clause. - * - * @param value - * @return a query condition to continue building the condition - */ - private QueryCondition wherePrimitive(A value) { - A alias = getPrimitiveAliasByValue(value); - if (alias == null) { - // this will result in an unmapped field exception - return where(value); - } - return where(alias); - } - - /** - * Begin an Object field condition clause. - * - * @param x - * the mapped object to query - * @return a query condition to continue building the condition - */ - public QueryCondition where(A x) { - from.getAliasDefinition().checkMultipleEnums(x); - return new QueryCondition(this, x); - } - - public QueryWhere where(Filter filter) { - HashMap fieldMap = Utils.newHashMap(); - for (Field f : filter.getClass().getDeclaredFields()) { - f.setAccessible(true); - try { - Object obj = f.get(filter); - if (obj == from.getAlias()) { - List fields = from.getAliasDefinition().getFields(); - String name = f.getName(); - for (TableDefinition.FieldDefinition field : fields) { - String n = name + "." + field.field.getName(); - Object o = field.field.get(obj); - fieldMap.put(n, o); - } - } - fieldMap.put(f.getName(), f.get(filter)); - } catch (Exception e) { - throw new IciqlException(e); - } - } - Token filterCode = new ClassReader().decompile(filter, fieldMap, "where"); - // String filterQuery = filterCode.toString(); - conditions.add(filterCode); - return new QueryWhere(this); - } - - public QueryWhere where(String fragment, List args) { - return this.where(fragment, args.toArray()); - } - - public QueryWhere where(String fragment, Object... args) { - conditions.add(new RuntimeToken(fragment, args)); - return new QueryWhere(this); - } - - public Query where(And conditions) { - whereTrue(); - addConditionToken(conditions.where.query); - return this; - } - - public Query where(Or conditions) { - whereFalse(); - addConditionToken(conditions.where.query); - return this; - } - - public QueryWhere whereTrue() { - return whereTrue(true); - } - - public QueryWhere whereFalse() { - return whereTrue(false); - } - - public QueryWhere whereTrue(Boolean condition) { - Token token = new Function("", condition); - addConditionToken(token); - return new QueryWhere(this); - } - - /** - * Sets the Limit and Offset of a query. - * - * @return the query - */ - - public Query limit(long limit) { - this.limit = limit; - return this; - } - - public Query offset(long offset) { - this.offset = offset; - return this; - } - - public Query orderBy(boolean field) { - from.getAliasDefinition().checkMultipleBooleans(); - return orderByPrimitive(field); - } - - public Query orderBy(byte field) { - return orderByPrimitive(field); - } - - public Query orderBy(short field) { - return orderByPrimitive(field); - } - - public Query orderBy(int field) { - return orderByPrimitive(field); - } - - public Query orderBy(long field) { - return orderByPrimitive(field); - } - - public Query orderBy(float field) { - return orderByPrimitive(field); - } - - public Query orderBy(double field) { - return orderByPrimitive(field); - } - - Query orderByPrimitive(Object field) { - Object alias = getPrimitiveAliasByValue(field); - if (alias == null) { - return orderBy(field); - } - return orderBy(alias); - } - - public Query orderBy(Object expr) { - from.getAliasDefinition().checkMultipleEnums(expr); - OrderExpression e = new OrderExpression(this, expr, false, false, false); - addOrderBy(e); - return this; - } - - /** - * Order by a number of columns. - * - * @param expressions - * the columns - * @return the query - */ - - public Query orderBy(Object... expressions) { - for (Object expr : expressions) { - from.getAliasDefinition().checkMultipleEnums(expr); - OrderExpression e = new OrderExpression(this, expr, false, false, false); - addOrderBy(e); - } - return this; - } - - public Query orderByDesc(byte field) { - return orderByDescPrimitive(field); - } - - public Query orderByDesc(short field) { - return orderByDescPrimitive(field); - } - - public Query orderByDesc(int field) { - return orderByDescPrimitive(field); - } - - public Query orderByDesc(long field) { - return orderByDescPrimitive(field); - } - - public Query orderByDesc(float field) { - return orderByDescPrimitive(field); - } - - public Query orderByDesc(double field) { - return orderByDescPrimitive(field); - } - - Query orderByDescPrimitive(Object field) { - Object alias = getPrimitiveAliasByValue(field); - if (alias == null) { - return orderByDesc(field); - } - return orderByDesc(alias); - } - - public Query orderByDesc(Object expr) { - OrderExpression e = new OrderExpression(this, expr, true, false, false); - addOrderBy(e); - return this; - } - - public Query groupBy(boolean field) { - from.getAliasDefinition().checkMultipleBooleans(); - return groupByPrimitive(field); - } - - public Query groupBy(byte field) { - return groupByPrimitive(field); - } - - public Query groupBy(short field) { - return groupByPrimitive(field); - } - - public Query groupBy(int field) { - return groupByPrimitive(field); - } - - public Query groupBy(long field) { - return groupByPrimitive(field); - } - - public Query groupBy(float field) { - return groupByPrimitive(field); - } - - public Query groupBy(double field) { - return groupByPrimitive(field); - } - - Query groupByPrimitive(Object field) { - Object alias = getPrimitiveAliasByValue(field); - if (alias == null) { - return groupBy(field); - } - return groupBy(alias); - } - - public Query groupBy(Object expr) { - from.getAliasDefinition().checkMultipleEnums(expr); - groupByExpressions.add(expr); - return this; - } - - public Query groupBy(Object... groupBy) { - this.groupByExpressions.addAll(Arrays.asList(groupBy)); - return this; - } - - /** - * INTERNAL - * - * @param stat - * the statement - * @param alias - * the alias object (can be null) - * @param value - * the value - */ - public void appendSQL(SQLStatement stat, Object alias, Object value) { - if (Function.count() == value) { - stat.appendSQL("COUNT(*)"); - return; - } - if (RuntimeParameter.PARAMETER == value) { - stat.appendSQL("?"); - addParameter(stat, alias, value); - return; - } - Token token = Db.getToken(value); - if (token != null) { - token.appendSQL(stat, this); - return; - } - if (alias != null && value != null && value.getClass().isEnum()) { - // special case: - // value is first enum constant which is also the alias object. - // the first enum constant is used as the alias because we can not - // instantiate an enum reflectively. - stat.appendSQL("?"); - addParameter(stat, alias, value); - return; - } - SelectColumn col = getColumnByReference(value); - if (col != null) { - col.appendSQL(stat); - return; - } - stat.appendSQL("?"); - addParameter(stat, alias, value); - } - - /** - * INTERNAL - * - * @param stat - * the statement - * @param alias - * the alias object (can be null) - * @param valueLeft - * the value on the left of the compound clause - * @param valueRight - * the value on the right of the compound clause - * @param compareType - * the current compare type (e.g. BETWEEN) - */ - public void appendSQL(SQLStatement stat, Object alias, Object valueLeft, Object valueRight, - CompareType compareType) { - stat.appendSQL("?"); - stat.appendSQL(" "); - switch (compareType) { - case BETWEEN: - stat.appendSQL("AND"); - break; - } - stat.appendSQL(" "); - stat.appendSQL("?"); - addParameter(stat, alias, valueLeft); - addParameter(stat, alias, valueRight); - } - - public void appendSQL(SQLStatement stat, Object alias, Iterable values, - CompareType compareType) { - boolean first = true; - stat.appendSQL("("); - for (Object value : values) { - if (first) { - first = false; - } else { - stat.appendSQL(", "); - } - stat.appendSQL("?"); - addParameter(stat, alias, value); - } - stat.appendSQL(")"); - } - - private void addParameter(SQLStatement stat, Object alias, Object value) { - SelectColumn col = getColumnByReference(alias); - if (col != null && value != null && value.getClass().isEnum()) { - // enum - TableDefinition.FieldDefinition field = col.getFieldDefinition(); - EnumType type = field.enumType; - Enum anEnum = (Enum) value; - Object y = Utils.convertEnum(anEnum, type); - stat.addParameter(y); - } else if (col != null) { - // object - TableDefinition.FieldDefinition field = col.getFieldDefinition(); - Class> typeAdapter = field.typeAdapter; - if (value != null && value instanceof String) { - if (field.trim && field.length > 0) { - // clip strings (issue-15) - String s = (String) value; - if (s.length() > field.length) { - value = s.substring(0, field.length); - } - } - } - Object parameter = db.getDialect().serialize(value, typeAdapter); - stat.addParameter(parameter); - } else { - // primitive - stat.addParameter(value); - } - } - - void addConditionToken(Token condition) { - if (condition == ConditionOpenClose.OPEN) { - conditionDepth ++; - } else if (condition == ConditionOpenClose.CLOSE) { - conditionDepth --; - if (conditionDepth < 0) { - throw new IciqlException("unmatch condition open-close count"); - } - } - conditions.add(condition); - } - - void addConditionToken(Query other) { - for (Token condition : other.conditions) { - addConditionToken(condition); - } - } - - void addUpdateColumnDeclaration(UpdateColumn declaration) { - updateColumnDeclarations.add(declaration); - } - - void appendWhere(SQLStatement stat) { - if (conditionDepth != 0) { - throw new IciqlException("unmatch condition open-close count"); - } - if (!conditions.isEmpty()) { - stat.appendSQL(" WHERE "); - - boolean skipNextConjunction = false; - - for (Token token : conditions) { - - if (skipNextConjunction && token instanceof ConditionAndOr) { - skipNextConjunction = false; - continue; - } - - token.appendSQL(stat, this); - stat.appendSQL(" "); - - if (ConditionOpenClose.OPEN == token) { - skipNextConjunction = true; - } - } - } - } - - void appendFromWhere(SQLStatement stat) { - appendFromWhere(stat, true); - } - - void appendFromWhere(SQLStatement stat, boolean log) { - stat.appendSQL(" FROM "); - from.appendSQL(stat); - for (SelectTable join : joins) { - join.appendSQLAsJoin(stat, this); - } - appendWhere(stat); - if (!groupByExpressions.isEmpty()) { - stat.appendSQL(" GROUP BY "); - int i = 0; - for (Object obj : groupByExpressions) { - if (i++ > 0) { - stat.appendSQL(", "); - } - appendSQL(stat, null, obj); - stat.appendSQL(" "); - } - } - if (!orderByList.isEmpty()) { - stat.appendSQL(" ORDER BY "); - int i = 0; - for (OrderExpression o : orderByList) { - if (i++ > 0) { - stat.appendSQL(", "); - } - o.appendSQL(stat); - stat.appendSQL(" "); - } - } - db.getDialect().appendLimitOffset(stat, limit, offset); - if (log) { - IciqlLogger.select(stat.getSQL()); - } - } - - /** - * Join another table. - * - * @param alias - * an alias for the table to join - * @return the joined query - */ - - public QueryJoin innerJoin(A alias) { + private Db db; + private SelectTable from; + private ArrayList conditions = Utils.newArrayList(); + private ArrayList updateColumnDeclarations = Utils.newArrayList(); + private int conditionDepth = 0; + private ArrayList> joins = Utils.newArrayList(); + private final IdentityHashMap> aliasMap = Utils.newIdentityHashMap(); + private ArrayList> orderByList = Utils.newArrayList(); + private ArrayList groupByExpressions = Utils.newArrayList(); + private long limit; + private long offset; + + private Query(Db db) { + this.db = db; + } + + /** + * from() is a static factory method to build a Query object. + * + * @param db + * @param alias + * @return a query object + */ + @SuppressWarnings("unchecked") + static Query from(Db db, T alias) { + Query query = new Query(db); + TableDefinition def = (TableDefinition) db.define(alias.getClass()); + query.from = new SelectTable(db, query, alias, false); + def.initSelectObject(query.from, alias, query.aliasMap, false); + return query; + } + + @SuppressWarnings("unchecked") + static Query rebuild(Db db, T alias) { + Query query = new Query(db); + TableDefinition def = (TableDefinition) db.define(alias.getClass()); + query.from = new SelectTable(db, query, alias, false); + def.initSelectObject(query.from, alias, query.aliasMap, true); + return query; + } + + public long selectCount() { + SQLStatement stat = getSelectStatement(false); + stat.appendSQL("COUNT(*) "); + appendFromWhere(stat); + ResultSet rs = stat.executeQuery(); + try { + rs.next(); + long value = rs.getLong(1); + return value; + } catch (SQLException e) { + throw IciqlException.fromSQL(stat.getSQL(), e); + } finally { + JdbcUtils.closeSilently(rs, true); + } + } + + public List select() { + return select(false); + } + + public T selectFirst() { + List list = limit(1).select(false); + return list.isEmpty() ? null : list.get(0); + } + + public List selectDistinct() { + return select(true); + } + + public X selectFirst(Z x) { + List list = limit(1).select(x); + return list.isEmpty() ? null : list.get(0); + } + + public void createView(Class viewClass) { + TableDefinition viewDef = db.define(viewClass); + + SQLStatement fromWhere = new SQLStatement(db); + appendFromWhere(fromWhere, false); + + SQLStatement stat = new SQLStatement(db); + db.getDialect().prepareCreateView(stat, viewDef, fromWhere.toSQL()); + IciqlLogger.create(stat.toSQL()); + stat.execute(); + } + + public void replaceView(Class viewClass) { + db.dropView(viewClass); + createView(viewClass); + } + + public String getSQL() { + SQLStatement stat = getSelectStatement(false); + stat.appendSQL("*"); + appendFromWhere(stat); + return stat.getSQL().trim(); + } + + /** + * toSQL returns a static string version of the query with runtime variables + * properly encoded. This method is also useful when combined with the where + * clause methods like isParameter() or atLeastParameter() which allows + * iciql to generate re-usable parameterized string statements. + * + * @return the sql query as plain text + */ + public String toSQL() { + return toSQL(false); + } + + /** + * toSQL returns a static string version of the query with runtime variables + * properly encoded. This method is also useful when combined with the where + * clause methods like isParameter() or atLeastParameter() which allows + * iciql to generate re-usable parameterized string statements. + * + * @param distinct if true SELECT DISTINCT is used for the query + * @return the sql query as plain text + */ + public String toSQL(boolean distinct) { + return toSQL(distinct, null); + } + + /** + * toSQL returns a static string version of the query with runtime variables + * properly encoded. This method is also useful when combined with the where + * clause methods like isParameter() or atLeastParameter() which allows + * iciql to generate re-usable parameterized string statements. + * + * @param distinct if true SELECT DISTINCT is used for the query + * @param k k is used to select only the columns of the specified alias + * for an inner join statement. An example of a generated + * statement is: SELECT DISTINCT t1.* FROM sometable AS t1 INNER + * JOIN othertable AS t2 ON t1.id = t2.id WHERE t2.flag = true + * without the alias parameter the statement would start with + * SELECT DISTINCT * FROM... + * @return the sql query as plain text + */ + public String toSQL(boolean distinct, K k) { + SQLStatement stat = new SQLStatement(getDb()); + if (updateColumnDeclarations.size() > 0) { + stat.appendSQL("UPDATE "); + from.appendSQL(stat); + stat.appendSQL(" SET "); + int i = 0; + for (UpdateColumn declaration : updateColumnDeclarations) { + if (i++ > 0) { + stat.appendSQL(", "); + } + declaration.appendSQL(stat); + } + appendWhere(stat); + } else { + stat.appendSQL("SELECT "); + if (distinct) { + stat.appendSQL("DISTINCT "); + } + if (k != null) { + SelectTable sel = getSelectTable(k); + if (sel == null) { + // unknown alias, use wildcard + IciqlLogger.warn("Alias {0} is not defined in the statement!", k.getClass()); + stat.appendSQL("*"); + } else if (isJoin()) { + // join query, use AS alias + String as = sel.getAs(); + stat.appendSQL(as + ".*"); + } else { + // schema.table.* + String schema = sel.getAliasDefinition().schemaName; + String table = sel.getAliasDefinition().tableName; + String as = getDb().getDialect().prepareTableName(schema, table); + stat.appendSQL(as + ".*"); + } + } else { + // alias unspecified, use wildcard + stat.appendSQL("*"); + } + appendFromWhere(stat); + } + return stat.toSQL().trim(); + } + + String toSubQuery(Z z) { + SQLStatement stat = getSelectStatement(false); + SelectColumn col = aliasMap.get(z); + String columnName = col.getFieldDefinition().columnName; + stat.appendColumn(columnName); + appendFromWhere(stat); + return stat.toSQL(); + } + + private List select(boolean distinct) { + List result = Utils.newArrayList(); + TableDefinition def = from.getAliasDefinition(); + SQLStatement stat = getSelectStatement(distinct); + def.appendSelectList(stat); + appendFromWhere(stat); + ResultSet rs = stat.executeQuery(); + try { + // SQLite returns pre-closed ResultSets for query results with 0 rows + if (!rs.isClosed()) { + int[] columns = def.mapColumns(db.getDialect(), false, rs); + while (rs.next()) { + T item = from.newObject(); + def.readRow(db.getDialect(), item, rs, columns); + result.add(item); + } + } + } catch (SQLException e) { + throw IciqlException.fromSQL(stat.getSQL(), e); + } finally { + JdbcUtils.closeSilently(rs, true); + } + return result; + } + + public int delete() { + SQLStatement stat = new SQLStatement(db); + stat.appendSQL("DELETE FROM "); + from.appendSQL(stat); + appendWhere(stat); + IciqlLogger.delete(stat.getSQL()); + return stat.executeUpdate(); + } + + public Query setNull(A field) { + return set(field).to(null); + } + + public UpdateColumnSet set(A field) { + from.getAliasDefinition().checkMultipleEnums(field); + return new UpdateColumnSet(this, field); + } + + public UpdateColumnSet set(boolean field) { + from.getAliasDefinition().checkMultipleBooleans(); + return setPrimitive(field); + } + + public UpdateColumnSet set(byte field) { + return setPrimitive(field); + } + + public UpdateColumnSet set(short field) { + return setPrimitive(field); + } + + public UpdateColumnSet set(int field) { + return setPrimitive(field); + } + + public UpdateColumnSet set(long field) { + return setPrimitive(field); + } + + public UpdateColumnSet set(float field) { + return setPrimitive(field); + } + + public UpdateColumnSet set(double field) { + return setPrimitive(field); + } + + private UpdateColumnSet setPrimitive(A field) { + A alias = getPrimitiveAliasByValue(field); + if (alias == null) { + // this will result in an unmapped field exception + return set(field); + } + return set(alias); + } + + public UpdateColumnIncrement increment(A field) { + return new UpdateColumnIncrement(this, field); + } + + public UpdateColumnIncrement increment(byte field) { + return incrementPrimitive(field); + } + + public UpdateColumnIncrement increment(short field) { + return incrementPrimitive(field); + } + + public UpdateColumnIncrement increment(int field) { + return incrementPrimitive(field); + } + + public UpdateColumnIncrement increment(long field) { + return incrementPrimitive(field); + } + + public UpdateColumnIncrement increment(float field) { + return incrementPrimitive(field); + } + + public UpdateColumnIncrement increment(double field) { + return incrementPrimitive(field); + } + + private UpdateColumnIncrement incrementPrimitive(A field) { + A alias = getPrimitiveAliasByValue(field); + if (alias == null) { + // this will result in an unmapped field exception + return increment(field); + } + return increment(alias); + } + + public int update() { + if (updateColumnDeclarations.size() == 0) { + throw new IciqlException("Missing set or increment call."); + } + SQLStatement stat = new SQLStatement(db); + stat.appendSQL("UPDATE "); + from.appendSQL(stat); + stat.appendSQL(" SET "); + int i = 0; + for (UpdateColumn declaration : updateColumnDeclarations) { + if (i++ > 0) { + stat.appendSQL(", "); + } + declaration.appendSQL(stat); + } + appendWhere(stat); + IciqlLogger.update(stat.getSQL()); + return stat.executeUpdate(); + } + + public List selectDistinct(Z x) { + return select(x, true); + } + + public List select(Z x) { + return select(x, false); + } + + @SuppressWarnings("unchecked") + private List select(Z x, boolean distinct) { + Class clazz = x.getClass(); + if (Db.isToken(x)) { + // selecting a function + return selectFunction((X) x, distinct); + } else { + // selecting a column + SelectColumn col = getColumnByReference(x); + if (col == null) { + col = getColumnByReference(getPrimitiveAliasByValue(x)); + } + if (col != null) { + return (List) selectColumn(col, clazz, distinct); + } + } + + // selecting into a new object type + Class enclosingClass = clazz.getEnclosingClass(); + if (enclosingClass != null) { + // anonymous inner class + clazz = clazz.getSuperclass(); + } + return select((Class) clazz, (X) x, distinct); + } + + private List select(Class clazz, X x, boolean distinct) { + List result = Utils.newArrayList(); + TableDefinition def = db.define(clazz); + SQLStatement stat = getSelectStatement(distinct); + def.appendSelectList(stat, this, x); + appendFromWhere(stat); + ResultSet rs = stat.executeQuery(); + try { + // SQLite returns pre-closed ResultSets for query results with 0 rows + if (!rs.isClosed()) { + int[] columns = def.mapColumns(db.getDialect(), false, rs); + while (rs.next()) { + X row = Utils.newObject(clazz); + def.readRow(db.getDialect(), row, rs, columns); + result.add(row); + } + } + } catch (SQLException e) { + throw IciqlException.fromSQL(stat.getSQL(), e); + } finally { + JdbcUtils.closeSilently(rs, true); + } + return result; + } + + @SuppressWarnings("unchecked") + private List selectFunction(X x, boolean distinct) { + SQLStatement stat = getSelectStatement(distinct); + appendSQL(stat, null, x); + appendFromWhere(stat); + ResultSet rs = stat.executeQuery(); + List result = Utils.newArrayList(); + try { + // SQLite returns pre-closed ResultSets for query results with 0 rows + if (!rs.isClosed()) { + while (rs.next()) { + X value = (X) rs.getObject(1); + result.add(value); + } + } + } catch (Exception e) { + throw IciqlException.fromSQL(stat.getSQL(), e); + } finally { + JdbcUtils.closeSilently(rs, true); + } + return result; + } + + @SuppressWarnings("unchecked") + private List selectColumn(SelectColumn col, Class clazz, boolean distinct) { + SQLStatement stat = getSelectStatement(distinct); + col.appendSQL(stat); + appendFromWhere(stat); + ResultSet rs = stat.executeQuery(); + List result = Utils.newArrayList(); + Class> typeAdapter = col.getFieldDefinition().typeAdapter; + try { + // SQLite returns pre-closed ResultSets for query results with 0 rows + if (!rs.isClosed()) { + while (rs.next()) { + X value = (X) db.getDialect().deserialize(rs, 1, clazz, typeAdapter); + result.add(value); + } + } + } catch (Exception e) { + throw IciqlException.fromSQL(stat.getSQL(), e); + } finally { + JdbcUtils.closeSilently(rs, true); + } + return result; + } + + private SQLStatement getSelectStatement(boolean distinct) { + SQLStatement stat = new SQLStatement(db); + stat.appendSQL("SELECT "); + if (distinct) { + stat.appendSQL("DISTINCT "); + } + return stat; + } + + /** + * Begin a primitive boolean field condition clause. + * + * @param x the primitive boolean field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(boolean x) { + from.getAliasDefinition().checkMultipleBooleans(); + return wherePrimitive(x); + } + + /** + * Begin a primitive short field condition clause. + * + * @param x the primitive short field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(byte x) { + return wherePrimitive(x); + } + + /** + * Begin a primitive short field condition clause. + * + * @param x the primitive short field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(short x) { + return wherePrimitive(x); + } + + /** + * Begin a primitive int field condition clause. + * + * @param x the primitive int field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(int x) { + return wherePrimitive(x); + } + + /** + * Begin a primitive long field condition clause. + * + * @param x the primitive long field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(long x) { + return wherePrimitive(x); + } + + /** + * Begin a primitive float field condition clause. + * + * @param x the primitive float field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(float x) { + return wherePrimitive(x); + } + + /** + * Begin a primitive double field condition clause. + * + * @param x the primitive double field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(double x) { + return wherePrimitive(x); + } + + /** + * Begins a primitive field condition clause. + * + * @param value + * @return a query condition to continue building the condition + */ + private QueryCondition wherePrimitive(A value) { + A alias = getPrimitiveAliasByValue(value); + if (alias == null) { + // this will result in an unmapped field exception + return where(value); + } + return where(alias); + } + + /** + * Begin an Object field condition clause. + * + * @param x the mapped object to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(A x) { + from.getAliasDefinition().checkMultipleEnums(x); + return new QueryCondition(this, x); + } + + public QueryWhere where(Filter filter) { + HashMap fieldMap = Utils.newHashMap(); + for (Field f : filter.getClass().getDeclaredFields()) { + f.setAccessible(true); + try { + Object obj = f.get(filter); + if (obj == from.getAlias()) { + List fields = from.getAliasDefinition().getFields(); + String name = f.getName(); + for (TableDefinition.FieldDefinition field : fields) { + String n = name + "." + field.field.getName(); + Object o = field.field.get(obj); + fieldMap.put(n, o); + } + } + fieldMap.put(f.getName(), f.get(filter)); + } catch (Exception e) { + throw new IciqlException(e); + } + } + Token filterCode = new ClassReader().decompile(filter, fieldMap, "where"); + // String filterQuery = filterCode.toString(); + conditions.add(filterCode); + return new QueryWhere(this); + } + + public QueryWhere where(String fragment, List args) { + return this.where(fragment, args.toArray()); + } + + public QueryWhere where(String fragment, Object... args) { + conditions.add(new RuntimeToken(fragment, args)); + return new QueryWhere(this); + } + + public Query where(And conditions) { + whereTrue(); + addConditionToken(conditions.where.query); + return this; + } + + public Query where(Or conditions) { + whereFalse(); + addConditionToken(conditions.where.query); + return this; + } + + public QueryWhere whereTrue() { + return whereTrue(true); + } + + public QueryWhere whereFalse() { + return whereTrue(false); + } + + public QueryWhere whereTrue(Boolean condition) { + Token token = new Function("", condition); + addConditionToken(token); + return new QueryWhere(this); + } + + /** + * Sets the Limit and Offset of a query. + * + * @return the query + */ + + public Query limit(long limit) { + this.limit = limit; + return this; + } + + public Query offset(long offset) { + this.offset = offset; + return this; + } + + public Query orderBy(boolean field) { + from.getAliasDefinition().checkMultipleBooleans(); + return orderByPrimitive(field); + } + + public Query orderBy(byte field) { + return orderByPrimitive(field); + } + + public Query orderBy(short field) { + return orderByPrimitive(field); + } + + public Query orderBy(int field) { + return orderByPrimitive(field); + } + + public Query orderBy(long field) { + return orderByPrimitive(field); + } + + public Query orderBy(float field) { + return orderByPrimitive(field); + } + + public Query orderBy(double field) { + return orderByPrimitive(field); + } + + Query orderByPrimitive(Object field) { + Object alias = getPrimitiveAliasByValue(field); + if (alias == null) { + return orderBy(field); + } + return orderBy(alias); + } + + public Query orderBy(Object expr) { + from.getAliasDefinition().checkMultipleEnums(expr); + OrderExpression e = new OrderExpression(this, expr, false, false, false); + addOrderBy(e); + return this; + } + + /** + * Order by a number of columns. + * + * @param expressions the columns + * @return the query + */ + + public Query orderBy(Object... expressions) { + for (Object expr : expressions) { + from.getAliasDefinition().checkMultipleEnums(expr); + OrderExpression e = new OrderExpression(this, expr, false, false, false); + addOrderBy(e); + } + return this; + } + + public Query orderByDesc(byte field) { + return orderByDescPrimitive(field); + } + + public Query orderByDesc(short field) { + return orderByDescPrimitive(field); + } + + public Query orderByDesc(int field) { + return orderByDescPrimitive(field); + } + + public Query orderByDesc(long field) { + return orderByDescPrimitive(field); + } + + public Query orderByDesc(float field) { + return orderByDescPrimitive(field); + } + + public Query orderByDesc(double field) { + return orderByDescPrimitive(field); + } + + Query orderByDescPrimitive(Object field) { + Object alias = getPrimitiveAliasByValue(field); + if (alias == null) { + return orderByDesc(field); + } + return orderByDesc(alias); + } + + public Query orderByDesc(Object expr) { + OrderExpression e = new OrderExpression(this, expr, true, false, false); + addOrderBy(e); + return this; + } + + public Query groupBy(boolean field) { + from.getAliasDefinition().checkMultipleBooleans(); + return groupByPrimitive(field); + } + + public Query groupBy(byte field) { + return groupByPrimitive(field); + } + + public Query groupBy(short field) { + return groupByPrimitive(field); + } + + public Query groupBy(int field) { + return groupByPrimitive(field); + } + + public Query groupBy(long field) { + return groupByPrimitive(field); + } + + public Query groupBy(float field) { + return groupByPrimitive(field); + } + + public Query groupBy(double field) { + return groupByPrimitive(field); + } + + Query groupByPrimitive(Object field) { + Object alias = getPrimitiveAliasByValue(field); + if (alias == null) { + return groupBy(field); + } + return groupBy(alias); + } + + public Query groupBy(Object expr) { + from.getAliasDefinition().checkMultipleEnums(expr); + groupByExpressions.add(expr); + return this; + } + + public Query groupBy(Object... groupBy) { + this.groupByExpressions.addAll(Arrays.asList(groupBy)); + return this; + } + + /** + * INTERNAL + * + * @param stat the statement + * @param alias the alias object (can be null) + * @param value the value + */ + public void appendSQL(SQLStatement stat, Object alias, Object value) { + if (Function.count() == value) { + stat.appendSQL("COUNT(*)"); + return; + } + if (RuntimeParameter.PARAMETER == value) { + stat.appendSQL("?"); + addParameter(stat, alias, value); + return; + } + Token token = Db.getToken(value); + if (token != null) { + token.appendSQL(stat, this); + return; + } + if (alias != null && value != null && value.getClass().isEnum()) { + // special case: + // value is first enum constant which is also the alias object. + // the first enum constant is used as the alias because we can not + // instantiate an enum reflectively. + stat.appendSQL("?"); + addParameter(stat, alias, value); + return; + } + SelectColumn col = getColumnByReference(value); + if (col != null) { + col.appendSQL(stat); + return; + } + stat.appendSQL("?"); + addParameter(stat, alias, value); + } + + /** + * INTERNAL + * + * @param stat the statement + * @param alias the alias object (can be null) + * @param valueLeft the value on the left of the compound clause + * @param valueRight the value on the right of the compound clause + * @param compareType the current compare type (e.g. BETWEEN) + */ + public void appendSQL(SQLStatement stat, Object alias, Object valueLeft, Object valueRight, + CompareType compareType) { + stat.appendSQL("?"); + stat.appendSQL(" "); + switch (compareType) { + case BETWEEN: + stat.appendSQL("AND"); + break; + } + stat.appendSQL(" "); + stat.appendSQL("?"); + addParameter(stat, alias, valueLeft); + addParameter(stat, alias, valueRight); + } + + public void appendSQL(SQLStatement stat, Object alias, Iterable values, + CompareType compareType) { + boolean first = true; + stat.appendSQL("("); + for (Object value : values) { + if (first) { + first = false; + } else { + stat.appendSQL(", "); + } + stat.appendSQL("?"); + addParameter(stat, alias, value); + } + stat.appendSQL(")"); + } + + private void addParameter(SQLStatement stat, Object alias, Object value) { + SelectColumn col = getColumnByReference(alias); + if (col != null && value != null && value.getClass().isEnum()) { + // enum + TableDefinition.FieldDefinition field = col.getFieldDefinition(); + EnumType type = field.enumType; + Enum anEnum = (Enum) value; + Object y = Utils.convertEnum(anEnum, type); + stat.addParameter(y); + } else if (col != null) { + // object + TableDefinition.FieldDefinition field = col.getFieldDefinition(); + Class> typeAdapter = field.typeAdapter; + if (value != null && value instanceof String) { + if (field.trim && field.length > 0) { + // clip strings (issue-15) + String s = (String) value; + if (s.length() > field.length) { + value = s.substring(0, field.length); + } + } + } + Object parameter = db.getDialect().serialize(value, typeAdapter); + stat.addParameter(parameter); + } else { + // primitive + stat.addParameter(value); + } + } + + void addConditionToken(Token condition) { + if (condition == ConditionOpenClose.OPEN) { + conditionDepth++; + } else if (condition == ConditionOpenClose.CLOSE) { + conditionDepth--; + if (conditionDepth < 0) { + throw new IciqlException("unmatch condition open-close count"); + } + } + conditions.add(condition); + } + + void addConditionToken(Query other) { + for (Token condition : other.conditions) { + addConditionToken(condition); + } + } + + void addUpdateColumnDeclaration(UpdateColumn declaration) { + updateColumnDeclarations.add(declaration); + } + + void appendWhere(SQLStatement stat) { + if (conditionDepth != 0) { + throw new IciqlException("unmatch condition open-close count"); + } + if (!conditions.isEmpty()) { + stat.appendSQL(" WHERE "); + + boolean skipNextConjunction = false; + + for (Token token : conditions) { + + if (skipNextConjunction && token instanceof ConditionAndOr) { + skipNextConjunction = false; + continue; + } + + token.appendSQL(stat, this); + stat.appendSQL(" "); + + if (ConditionOpenClose.OPEN == token) { + skipNextConjunction = true; + } + } + } + } + + void appendFromWhere(SQLStatement stat) { + appendFromWhere(stat, true); + } + + void appendFromWhere(SQLStatement stat, boolean log) { + stat.appendSQL(" FROM "); + from.appendSQL(stat); + for (SelectTable join : joins) { + join.appendSQLAsJoin(stat, this); + } + appendWhere(stat); + if (!groupByExpressions.isEmpty()) { + stat.appendSQL(" GROUP BY "); + int i = 0; + for (Object obj : groupByExpressions) { + if (i++ > 0) { + stat.appendSQL(", "); + } + appendSQL(stat, null, obj); + stat.appendSQL(" "); + } + } + if (!orderByList.isEmpty()) { + stat.appendSQL(" ORDER BY "); + int i = 0; + for (OrderExpression o : orderByList) { + if (i++ > 0) { + stat.appendSQL(", "); + } + o.appendSQL(stat); + stat.appendSQL(" "); + } + } + db.getDialect().appendLimitOffset(stat, limit, offset); + if (log) { + IciqlLogger.select(stat.getSQL()); + } + } + + /** + * Join another table. + * + * @param alias an alias for the table to join + * @return the joined query + */ + + public QueryJoin innerJoin(A alias) { return join(alias, false); - } + } public QueryJoin leftJoin(A alias) { return join(alias, true); } - @SuppressWarnings({ "unchecked", "rawtypes" }) + @SuppressWarnings({"unchecked", "rawtypes"}) private QueryJoin join(A alias, boolean outerJoin) { TableDefinition def = (TableDefinition) db.define(alias.getClass()); SelectTable join = new SelectTable(db, this, alias, outerJoin); @@ -1052,63 +1030,63 @@ public class Query { return new QueryJoin(this, join); } - Db getDb() { - return db; - } - - SelectTable getFrom() { - return from; - } - - boolean isJoin() { - return !joins.isEmpty(); - } - - SelectTable getSelectTable(Object alias) { - if (from.getAlias() == alias) { - return from; - } else { - for (SelectTable join : joins) { - if (join.getAlias() == alias) { - return join; - } - } - } - return null; - } - - /** - * This method returns a mapped Object field by its reference. - * - * @param obj - * @return - */ - private SelectColumn getColumnByReference(Object obj) { - SelectColumn col = aliasMap.get(obj); - return col; - } - - /** - * This method returns the alias of a mapped primitive field by its value. - * - * @param obj - * @return - */ - @SuppressWarnings("unchecked") - A getPrimitiveAliasByValue(A obj) { - for (Object alias : aliasMap.keySet()) { - if (alias.equals(obj)) { - SelectColumn match = aliasMap.get(alias); - if (match.getFieldDefinition().isPrimitive) { - return (A) alias; - } - } - } - return null; - } - - void addOrderBy(OrderExpression expr) { - orderByList.add(expr); - } + Db getDb() { + return db; + } + + SelectTable getFrom() { + return from; + } + + boolean isJoin() { + return !joins.isEmpty(); + } + + SelectTable getSelectTable(Object alias) { + if (from.getAlias() == alias) { + return from; + } else { + for (SelectTable join : joins) { + if (join.getAlias() == alias) { + return join; + } + } + } + return null; + } + + /** + * This method returns a mapped Object field by its reference. + * + * @param obj + * @return + */ + private SelectColumn getColumnByReference(Object obj) { + SelectColumn col = aliasMap.get(obj); + return col; + } + + /** + * This method returns the alias of a mapped primitive field by its value. + * + * @param obj + * @return + */ + @SuppressWarnings("unchecked") + A getPrimitiveAliasByValue(A obj) { + for (Object alias : aliasMap.keySet()) { + if (alias.equals(obj)) { + SelectColumn match = aliasMap.get(alias); + if (match.getFieldDefinition().isPrimitive) { + return (A) alias; + } + } + } + return null; + } + + void addOrderBy(OrderExpression expr) { + orderByList.add(expr); + } } diff --git a/src/main/java/com/iciql/QueryBetween.java b/src/main/java/com/iciql/QueryBetween.java index 72d19dc..f3a5561 100644 --- a/src/main/java/com/iciql/QueryBetween.java +++ b/src/main/java/com/iciql/QueryBetween.java @@ -18,43 +18,37 @@ package com.iciql; /** * This class represents a "between y and z" condition. - * - * @param - * the return type of the query - * @param - * the incomplete condition data type + * + * @param the return type of the query + * @param the incomplete condition data type */ public class QueryBetween { - private Query query; - private A x; - private A y; + private Query query; + private A x; + private A y; - /** - * Construct a between condition. - * - * @param query - * the query - * @param x - * the alias - * @param y - * the lower bound of the between condition - */ - public QueryBetween(Query query, A x, A y) { - this.query = query; - this.x = x; - this.y = y; - } + /** + * Construct a between condition. + * + * @param query the query + * @param x the alias + * @param y the lower bound of the between condition + */ + public QueryBetween(Query query, A x, A y) { + this.query = query; + this.x = x; + this.y = y; + } - /** - * Set the upper bound of the between condition. - * - * @param z - * the upper bound of the between condition - * @return the query - */ - public QueryWhere and(A z) { - query.addConditionToken(new Condition(x, y, z, CompareType.BETWEEN)); - return new QueryWhere(query); - } + /** + * Set the upper bound of the between condition. + * + * @param z the upper bound of the between condition + * @return the query + */ + public QueryWhere and(A z) { + query.addConditionToken(new Condition(x, y, z, CompareType.BETWEEN)); + return new QueryWhere(query); + } } diff --git a/src/main/java/com/iciql/QueryCondition.java b/src/main/java/com/iciql/QueryCondition.java index cef95c1..fce66af 100644 --- a/src/main/java/com/iciql/QueryCondition.java +++ b/src/main/java/com/iciql/QueryCondition.java @@ -21,128 +21,126 @@ import com.iciql.util.Utils; /** * This class represents a query with an incomplete condition. - * - * @param - * the return type of the query - * @param - * the incomplete condition data type + * + * @param the return type of the query + * @param the incomplete condition data type */ public class QueryCondition { - private Query query; - private A x; - - QueryCondition(Query query, A x) { - this.query = query; - this.x = x; - } - - public QueryWhere in(SubQuery q) { - query.addConditionToken(new SubQueryCondition(x, q)); - return new QueryWhere(query); - } - - public QueryWhere oneOf(A... a) { - return oneOf(Utils.newArrayIterable(a)); - } - - public QueryWhere oneOf(Iterable i) { - query.addConditionToken(new Condition(x, i, CompareType.IN)); - return new QueryWhere(query); - } - - public QueryWhere noneOf(A... a) { - return noneOf(Utils.newArrayIterable(a)); - } - - public QueryWhere noneOf(Iterable i) { - query.addConditionToken(new Condition(x, i, CompareType.NOT_IN)); - return new QueryWhere(query); - } - - public QueryWhere is(A y) { - query.addConditionToken(new Condition(x, y, CompareType.EQUAL)); - return new QueryWhere(query); - } - - public QueryWhere isNot(A y) { - query.addConditionToken(new Condition(x, y, CompareType.NOT_EQUAL)); - return new QueryWhere(query); - } - - public QueryWhere isNull() { - query.addConditionToken(new Condition(x, CompareType.IS_NULL)); - return new QueryWhere(query); - } - - public QueryWhere isNotNull() { - query.addConditionToken(new Condition(x, CompareType.IS_NOT_NULL)); - return new QueryWhere(query); - } - - public QueryWhere exceeds(A y) { - query.addConditionToken(new Condition(x, y, CompareType.EXCEEDS)); - return new QueryWhere(query); - } - - public QueryWhere atLeast(A y) { - query.addConditionToken(new Condition(x, y, CompareType.AT_LEAST)); - return new QueryWhere(query); - } - - public QueryWhere lessThan(A y) { - query.addConditionToken(new Condition(x, y, CompareType.LESS_THAN)); - return new QueryWhere(query); - } - - public QueryWhere atMost(A y) { - query.addConditionToken(new Condition(x, y, CompareType.AT_MOST)); - return new QueryWhere(query); - } - - public QueryBetween between(A y) { - return new QueryBetween(query, x, y); - } - - public QueryWhere like(A pattern) { - query.addConditionToken(new Condition(x, pattern, CompareType.LIKE)); - return new QueryWhere(query); - } - - /* - * These method allows you to generate "x=?", "x!=?", etc where conditions. - * Parameter substitution must be done manually later with db.executeQuery. - * This allows for building re-usable SQL string statements from your model - * classes. - */ - public QueryWhere isParameter() { - query.addConditionToken(new RuntimeParameter(x, CompareType.EQUAL)); - return new QueryWhere(query); - } - - public QueryWhere isNotParameter() { - query.addConditionToken(new RuntimeParameter(x, CompareType.NOT_EQUAL)); - return new QueryWhere(query); - } - - public QueryWhere exceedsParameter() { - query.addConditionToken(new RuntimeParameter(x, CompareType.EXCEEDS)); - return new QueryWhere(query); - } - - public QueryWhere lessThanParameter() { - query.addConditionToken(new RuntimeParameter(x, CompareType.LESS_THAN)); - return new QueryWhere(query); - } - - public QueryWhere atMostParameter() { - query.addConditionToken(new RuntimeParameter(x, CompareType.AT_MOST)); - return new QueryWhere(query); - } - - public QueryWhere likeParameter() { - query.addConditionToken(new RuntimeParameter(x, CompareType.LIKE)); - return new QueryWhere(query); - } + private Query query; + private A x; + + QueryCondition(Query query, A x) { + this.query = query; + this.x = x; + } + + public QueryWhere in(SubQuery q) { + query.addConditionToken(new SubQueryCondition(x, q)); + return new QueryWhere(query); + } + + public QueryWhere oneOf(A... a) { + return oneOf(Utils.newArrayIterable(a)); + } + + public QueryWhere oneOf(Iterable i) { + query.addConditionToken(new Condition(x, i, CompareType.IN)); + return new QueryWhere(query); + } + + public QueryWhere noneOf(A... a) { + return noneOf(Utils.newArrayIterable(a)); + } + + public QueryWhere noneOf(Iterable i) { + query.addConditionToken(new Condition(x, i, CompareType.NOT_IN)); + return new QueryWhere(query); + } + + public QueryWhere is(A y) { + query.addConditionToken(new Condition(x, y, CompareType.EQUAL)); + return new QueryWhere(query); + } + + public QueryWhere isNot(A y) { + query.addConditionToken(new Condition(x, y, CompareType.NOT_EQUAL)); + return new QueryWhere(query); + } + + public QueryWhere isNull() { + query.addConditionToken(new Condition(x, CompareType.IS_NULL)); + return new QueryWhere(query); + } + + public QueryWhere isNotNull() { + query.addConditionToken(new Condition(x, CompareType.IS_NOT_NULL)); + return new QueryWhere(query); + } + + public QueryWhere exceeds(A y) { + query.addConditionToken(new Condition(x, y, CompareType.EXCEEDS)); + return new QueryWhere(query); + } + + public QueryWhere atLeast(A y) { + query.addConditionToken(new Condition(x, y, CompareType.AT_LEAST)); + return new QueryWhere(query); + } + + public QueryWhere lessThan(A y) { + query.addConditionToken(new Condition(x, y, CompareType.LESS_THAN)); + return new QueryWhere(query); + } + + public QueryWhere atMost(A y) { + query.addConditionToken(new Condition(x, y, CompareType.AT_MOST)); + return new QueryWhere(query); + } + + public QueryBetween between(A y) { + return new QueryBetween(query, x, y); + } + + public QueryWhere like(A pattern) { + query.addConditionToken(new Condition(x, pattern, CompareType.LIKE)); + return new QueryWhere(query); + } + + /* + * These method allows you to generate "x=?", "x!=?", etc where conditions. + * Parameter substitution must be done manually later with db.executeQuery. + * This allows for building re-usable SQL string statements from your model + * classes. + */ + public QueryWhere isParameter() { + query.addConditionToken(new RuntimeParameter(x, CompareType.EQUAL)); + return new QueryWhere(query); + } + + public QueryWhere isNotParameter() { + query.addConditionToken(new RuntimeParameter(x, CompareType.NOT_EQUAL)); + return new QueryWhere(query); + } + + public QueryWhere exceedsParameter() { + query.addConditionToken(new RuntimeParameter(x, CompareType.EXCEEDS)); + return new QueryWhere(query); + } + + public QueryWhere lessThanParameter() { + query.addConditionToken(new RuntimeParameter(x, CompareType.LESS_THAN)); + return new QueryWhere(query); + } + + public QueryWhere atMostParameter() { + query.addConditionToken(new RuntimeParameter(x, CompareType.AT_MOST)); + return new QueryWhere(query); + } + + public QueryWhere likeParameter() { + query.addConditionToken(new RuntimeParameter(x, CompareType.LIKE)); + return new QueryWhere(query); + } } diff --git a/src/main/java/com/iciql/QueryJoin.java b/src/main/java/com/iciql/QueryJoin.java index 6d0484e..325ae50 100644 --- a/src/main/java/com/iciql/QueryJoin.java +++ b/src/main/java/com/iciql/QueryJoin.java @@ -23,53 +23,53 @@ package com.iciql; public class QueryJoin { - private Query query; - private SelectTable join; + private Query query; + private SelectTable join; - QueryJoin(Query query, SelectTable join) { - this.query = query; - this.join = join; - } + QueryJoin(Query query, SelectTable join) { + this.query = query; + this.join = join; + } - public QueryJoinCondition on(boolean x) { - query.getFrom().getAliasDefinition().checkMultipleBooleans(); - return addPrimitive(x); - } + public QueryJoinCondition on(boolean x) { + query.getFrom().getAliasDefinition().checkMultipleBooleans(); + return addPrimitive(x); + } - public QueryJoinCondition on(byte x) { - return addPrimitive(x); - } + public QueryJoinCondition on(byte x) { + return addPrimitive(x); + } - public QueryJoinCondition on(short x) { - return addPrimitive(x); - } + public QueryJoinCondition on(short x) { + return addPrimitive(x); + } - public QueryJoinCondition on(int x) { - return addPrimitive(x); - } + public QueryJoinCondition on(int x) { + return addPrimitive(x); + } - public QueryJoinCondition on(long x) { - return addPrimitive(x); - } + public QueryJoinCondition on(long x) { + return addPrimitive(x); + } - public QueryJoinCondition on(float x) { - return addPrimitive(x); - } + public QueryJoinCondition on(float x) { + return addPrimitive(x); + } - public QueryJoinCondition on(double x) { - return addPrimitive(x); - } + public QueryJoinCondition on(double x) { + return addPrimitive(x); + } - private QueryJoinCondition addPrimitive(A x) { - A alias = query.getPrimitiveAliasByValue(x); - if (alias == null) { - // this will result in an unmapped field exception - return new QueryJoinCondition(query, join, x); - } - return new QueryJoinCondition(query, join, alias); - } + private QueryJoinCondition addPrimitive(A x) { + A alias = query.getPrimitiveAliasByValue(x); + if (alias == null) { + // this will result in an unmapped field exception + return new QueryJoinCondition(query, join, x); + } + return new QueryJoinCondition(query, join, alias); + } - public QueryJoinCondition on(A x) { - return new QueryJoinCondition(query, join, x); - } + public QueryJoinCondition on(A x) { + return new QueryJoinCondition(query, join, x); + } } diff --git a/src/main/java/com/iciql/QueryJoinCondition.java b/src/main/java/com/iciql/QueryJoinCondition.java index 6dfd218..d1a82d9 100644 --- a/src/main/java/com/iciql/QueryJoinCondition.java +++ b/src/main/java/com/iciql/QueryJoinCondition.java @@ -20,64 +20,63 @@ package com.iciql; /** * This class represents a query with join and an incomplete condition. - * - * @param - * the incomplete condition data type + * + * @param the incomplete condition data type */ public class QueryJoinCondition { - private Query query; - private SelectTable join; - private A x; + private Query query; + private SelectTable join; + private A x; + + QueryJoinCondition(Query query, SelectTable join, A x) { + this.query = query; + this.join = join; + this.x = x; + } - QueryJoinCondition(Query query, SelectTable join, A x) { - this.query = query; - this.join = join; - this.x = x; - } + public Query is(boolean y) { + return addPrimitive(y); + } - public Query is(boolean y) { - return addPrimitive(y); - } + public Query is(byte y) { + return addPrimitive(y); + } - public Query is(byte y) { - return addPrimitive(y); - } + public Query is(short y) { + return addPrimitive(y); + } - public Query is(short y) { - return addPrimitive(y); - } + public Query is(int y) { + return addPrimitive(y); + } - public Query is(int y) { - return addPrimitive(y); - } - - public Query is(long y) { - return addPrimitive(y); - } + public Query is(long y) { + return addPrimitive(y); + } - public Query is(float y) { - return addPrimitive(y); - } + public Query is(float y) { + return addPrimitive(y); + } - public Query is(double y) { - return addPrimitive(y); - } + public Query is(double y) { + return addPrimitive(y); + } - @SuppressWarnings("unchecked") - private Query addPrimitive(Object o) { - A alias = query.getPrimitiveAliasByValue((A) o); - if (alias == null) { - join.addConditionToken(new Condition(x, (A) o, CompareType.EQUAL)); - } else { - join.addConditionToken(new Condition(x, alias, CompareType.EQUAL)); - } - return query; - } + @SuppressWarnings("unchecked") + private Query addPrimitive(Object o) { + A alias = query.getPrimitiveAliasByValue((A) o); + if (alias == null) { + join.addConditionToken(new Condition(x, (A) o, CompareType.EQUAL)); + } else { + join.addConditionToken(new Condition(x, alias, CompareType.EQUAL)); + } + return query; + } - public Query is(A y) { - join.addConditionToken(new Condition(x, y, CompareType.EQUAL)); - return query; - } + public Query is(A y) { + join.addConditionToken(new Condition(x, y, CompareType.EQUAL)); + return query; + } } diff --git a/src/main/java/com/iciql/QueryWhere.java b/src/main/java/com/iciql/QueryWhere.java index fdce1cb..16f1397 100644 --- a/src/main/java/com/iciql/QueryWhere.java +++ b/src/main/java/com/iciql/QueryWhere.java @@ -17,626 +17,592 @@ package com.iciql; -import java.util.List; - import com.iciql.NestedConditions.And; import com.iciql.NestedConditions.Or; +import java.util.List; + /** * This class represents a query with a condition. * - * @param - * the return type + * @param the return type */ public class QueryWhere { - Query query; - - QueryWhere(Query query) { - this.query = query; - } - - /** - * Specify an AND condition with a mapped primitive boolean. - * - * @param x - * the primitive boolean field to query - * @return a query condition to continue building the condition - */ - public QueryCondition and(boolean x) { - query.getFrom().getAliasDefinition().checkMultipleBooleans(); - return addPrimitive(ConditionAndOr.AND, x); - } - - /** - * Specify an AND condition with a mapped primitive byte. - * - * @param x - * the primitive byte field to query - * @return a query condition to continue building the condition - */ - public QueryCondition and(byte x) { - return addPrimitive(ConditionAndOr.AND, x); - } - - /** - * Specify an AND condition with a mapped primitive short. - * - * @param x - * the primitive short field to query - * @return a query condition to continue building the condition - */ - public QueryCondition and(short x) { - return addPrimitive(ConditionAndOr.AND, x); - } - - /** - * Specify an AND condition with a mapped primitive int. - * - * @param x - * the primitive int field to query - * @return a query condition to continue building the condition - */ - public QueryCondition and(int x) { - return addPrimitive(ConditionAndOr.AND, x); - } - - /** - * Specify an AND condition with a mapped primitive long. - * - * @param x - * the primitive long field to query - * @return a query condition to continue building the condition - */ - public QueryCondition and(long x) { - return addPrimitive(ConditionAndOr.AND, x); - } - - /** - * Specify an AND condition with a mapped primitive float. - * - * @param x - * the primitive float field to query - * @return a query condition to continue building the condition - */ - public QueryCondition and(float x) { - return addPrimitive(ConditionAndOr.AND, x); - } - - /** - * Specify an AND condition with a mapped primitive double. - * - * @param x - * the primitive double field to query - * @return a query condition to continue building the condition - */ - public QueryCondition and(double x) { - return addPrimitive(ConditionAndOr.AND, x); - } - - private QueryCondition addPrimitive(ConditionAndOr condition, A x) { - query.addConditionToken(condition); - A alias = query.getPrimitiveAliasByValue(x); - if (alias == null) { - // this will result in an unmapped field exception - return new QueryCondition(query, x); - } - return new QueryCondition(query, alias); - } - - /** - * Specify an AND condition with a mapped Object field. - * - * @param x - * the Object field to query - * @return a query condition to continue building the condition - */ - public QueryCondition and(A x) { - query.getFrom().getAliasDefinition().checkMultipleEnums(x); - query.addConditionToken(ConditionAndOr.AND); - return new QueryCondition(query, x); - } - - public QueryWhere and(And conditions) { - andOpen(); - query.addConditionToken(conditions.where.query); - return close(); - } - - public QueryWhere and(Or conditions) { - andOpen(); - query.addConditionToken(conditions.where.query); - return close(); - } - - public QueryWhere andOpen() { - return open(ConditionAndOr.AND); - } - - /** - * Specify an OR condition with a mapped primitive boolean. - * - * @param x - * the primitive boolean field to query - * @return a query condition to continue building the condition - */ - public QueryCondition or(boolean x) { - query.getFrom().getAliasDefinition().checkMultipleBooleans(); - return addPrimitive(ConditionAndOr.OR, x); - } - - /** - * Specify an OR condition with a mapped primitive byte. - * - * @param x - * the primitive byte field to query - * @return a query condition to continue building the condition - */ - public QueryCondition or(byte x) { - return addPrimitive(ConditionAndOr.OR, x); - } - - /** - * Specify an OR condition with a mapped primitive short. - * - * @param x - * the primitive short field to query - * @return a query condition to continue building the condition - */ - public QueryCondition or(short x) { - return addPrimitive(ConditionAndOr.OR, x); - } - - /** - * Specify an OR condition with a mapped primitive int. - * - * @param x - * the primitive int field to query - * @return a query condition to continue building the condition - */ - public QueryCondition or(int x) { - return addPrimitive(ConditionAndOr.OR, x); - } - - /** - * Specify an OR condition with a mapped primitive long. - * - * @param x - * the primitive long field to query - * @return a query condition to continue building the condition - */ - public QueryCondition or(long x) { - return addPrimitive(ConditionAndOr.OR, x); - } - - /** - * Specify an OR condition with a mapped primitive float. - * - * @param x - * the primitive float field to query - * @return a query condition to continue building the condition - */ - public QueryCondition or(float x) { - return addPrimitive(ConditionAndOr.OR, x); - } - - /** - * Specify an OR condition with a mapped primitive double. - * - * @param x - * the primitive double field to query - * @return a query condition to continue building the condition - */ - public QueryCondition or(double x) { - return addPrimitive(ConditionAndOr.OR, x); - } - - /** - * Specify an OR condition with a mapped Object field. - * - * @param x - * the Object field to query - * @return a query condition to continue building the condition - */ - public QueryCondition or(A x) { - query.getFrom().getAliasDefinition().checkMultipleEnums(x); - query.addConditionToken(ConditionAndOr.OR); - return new QueryCondition(query, x); - } - - public QueryWhere or(And conditions) { - orOpen(); - query.addConditionToken(conditions.where.query); - return close(); - } - - public QueryWhere or(Or conditions) { - orOpen(); - query.addConditionToken(conditions.where.query); - return close(); - } - - public QueryWhere orOpen() { - return open(ConditionAndOr.OR); - } - - private QueryWhere open(ConditionAndOr andOr) { - query.addConditionToken(andOr); - query.addConditionToken(ConditionOpenClose.OPEN); - return this; - } - - public QueryWhere close() { - query.addConditionToken(ConditionOpenClose.CLOSE); - return this; - } - - public QueryWhere limit(long limit) { - query.limit(limit); - return this; - } - - public QueryWhere offset(long offset) { - query.offset(offset); - return this; - } - - public String getSQL() { - SQLStatement stat = new SQLStatement(query.getDb()); - stat.appendSQL("SELECT *"); - query.appendFromWhere(stat); - return stat.getSQL().trim(); - } - - /** - * toSQL returns a static string version of the query with runtime variables - * properly encoded. This method is also useful when combined with the where - * clause methods like isParameter() or atLeastParameter() which allows - * iciql to generate re-usable parameterized string statements. - * - * @return the sql query as plain text - */ - public String toSQL() { - return query.toSQL(false); - } - - /** - * toSQL returns a static string version of the query with runtime variables - * properly encoded. This method is also useful when combined with the where - * clause methods like isParameter() or atLeastParameter() which allows - * iciql to generate re-usable parameterized string statements. - * - * @param distinct - * if true SELECT DISTINCT is used for the query - * @return the sql query as plain text - */ - public String toSQL(boolean distinct) { - return query.toSQL(distinct); - } - - /** - * toSQL returns a static string version of the query with runtime variables - * properly encoded. This method is also useful when combined with the where - * clause methods like isParameter() or atLeastParameter() which allows - * iciql to generate re-usable parameterized string statements. - * - * @param distinct - * if true SELECT DISTINCT is used for the query - * @param k - * k is used to select only the columns of the specified alias - * for an inner join statement. An example of a generated - * statement is: SELECT DISTINCT t1.* FROM sometable AS t1 INNER - * JOIN othertable AS t2 ON t1.id = t2.id WHERE t2.flag = true - * without the alias parameter the statement would start with - * SELECT DISTINCT * FROM... - * @return the sql query as plain text - */ - public String toSQL(boolean distinct, K k) { - return query.toSQL(distinct, k); - } - - public SubQuery subQuery(Z x) { - return new SubQuery(query, x); - } - - public SubQuery subQuery(boolean x) { - return subQuery(query.getPrimitiveAliasByValue(x)); - } - - public SubQuery subQuery(byte x) { - return subQuery(query.getPrimitiveAliasByValue(x)); - } - - public SubQuery subQuery(short x) { - return subQuery(query.getPrimitiveAliasByValue(x)); - } - - public SubQuery subQuery(int x) { - return subQuery(query.getPrimitiveAliasByValue(x)); - } - - public SubQuery subQuery(long x) { - return subQuery(query.getPrimitiveAliasByValue(x)); - } - - public SubQuery subQuery(float x) { - return subQuery(query.getPrimitiveAliasByValue(x)); - } - - public SubQuery subQuery(double x) { - return subQuery(query.getPrimitiveAliasByValue(x)); - } - - public List select(Z x) { - return query.select(x); - } - - public List selectDistinct(Z x) { - return query.selectDistinct(x); - } - - public X selectFirst(Z x) { - List list = query.select(x); - return list.isEmpty() ? null : list.get(0); - } - - public List select() { - return query.select(); - } - - public T selectFirst() { - List list = select(); - return list.isEmpty() ? null : list.get(0); - } - - public List selectDistinct() { - return query.selectDistinct(); - } - - public void createView(Class viewClass) { - query.createView(viewClass); - } - - public void replaceView(Class viewClass) { - query.replaceView(viewClass); - } - - /** - * Order by primitive boolean field - * - * @param field - * a primitive boolean field - * @return the query - */ - public QueryWhere orderBy(boolean field) { - query.getFrom().getAliasDefinition().checkMultipleBooleans(); - return orderByPrimitive(field); - } - - /** - * Order by primitive byte field - * - * @param field - * a primitive byte field - * @return the query - */ - public QueryWhere orderBy(byte field) { - return orderByPrimitive(field); - } - - /** - * Order by primitive short field - * - * @param field - * a primitive short field - * @return the query - */ - public QueryWhere orderBy(short field) { - return orderByPrimitive(field); - } - - public QueryWhere orderBy(int field) { - return orderByPrimitive(field); - } - - /** - * Order by primitive long field - * - * @param field - * a primitive long field - * @return the query - */ - public QueryWhere orderBy(long field) { - return orderByPrimitive(field); - } - - /** - * Order by primitive float field - * - * @param field - * a primitive float field - * @return the query - */ - public QueryWhere orderBy(float field) { - return orderByPrimitive(field); - } - - /** - * Order by primitive double field - * - * @param field - * a primitive double field - * @return the query - */ - public QueryWhere orderBy(double field) { - return orderByPrimitive(field); - } - - private QueryWhere orderByPrimitive(Object field) { - query.orderByPrimitive(field); - return this; - } - - public QueryWhere orderBy(Object field) { - query.getFrom().getAliasDefinition().checkMultipleEnums(field); - query.orderBy(field); - return this; - } - - /** - * Order by a number of Object columns. - * - * @param expressions - * the order by expressions - * @return the query - */ - - public QueryWhere orderBy(Object... expressions) { - query.orderBy(expressions); - return this; - } - - public QueryWhere orderByNullsFirst(Object expr) { - query.getFrom().getAliasDefinition().checkMultipleEnums(expr); - OrderExpression e = new OrderExpression(query, expr, false, true, false); - query.addOrderBy(e); - return this; - } - - public QueryWhere orderByNullsLast(Object expr) { - query.getFrom().getAliasDefinition().checkMultipleEnums(expr); - OrderExpression e = new OrderExpression(query, expr, false, false, true); - query.addOrderBy(e); - return this; - } - - public QueryWhere orderByDesc(Object expr) { - query.getFrom().getAliasDefinition().checkMultipleEnums(expr); - OrderExpression e = new OrderExpression(query, expr, true, false, false); - query.addOrderBy(e); - return this; - } - - public QueryWhere orderByDescNullsFirst(Object expr) { - query.getFrom().getAliasDefinition().checkMultipleEnums(expr); - OrderExpression e = new OrderExpression(query, expr, true, true, false); - query.addOrderBy(e); - return this; - } - - public QueryWhere orderByDescNullsLast(Object expr) { - query.getFrom().getAliasDefinition().checkMultipleEnums(expr); - OrderExpression e = new OrderExpression(query, expr, true, false, true); - query.addOrderBy(e); - return this; - } - - /** - * Group by primitive boolean field - * - * @param field - * a primitive boolean field - * @return the query - */ - public QueryWhere groupBy(boolean field) { - query.getFrom().getAliasDefinition().checkMultipleBooleans(); - return groupByPrimitive(field); - } - - /** - * Group by primitive byte field - * - * @param field - * a primitive byte field - * @return the query - */ - public QueryWhere groupBy(byte field) { - return groupByPrimitive(field); - } - - /** - * Group by primitive short field - * - * @param field - * a primitive short field - * @return the query - */ - public QueryWhere groupBy(short field) { - return groupByPrimitive(field); - } - - public QueryWhere groupBy(int field) { - return groupByPrimitive(field); - } - - /** - * Group by primitive long field - * - * @param field - * a primitive long field - * @return the query - */ - public QueryWhere groupBy(long field) { - return groupByPrimitive(field); - } - - /** - * Group by primitive float field - * - * @param field - * a primitive float field - * @return the query - */ - public QueryWhere groupBy(float field) { - return groupByPrimitive(field); - } - - /** - * Group by primitive double field - * - * @param field - * a primitive double field - * @return the query - */ - public QueryWhere groupBy(double field) { - return groupByPrimitive(field); - } - - private QueryWhere groupByPrimitive(Object field) { - query.groupByPrimitive(field); - return this; - } - - public QueryWhere groupBy(Object field) { - query.getFrom().getAliasDefinition().checkMultipleEnums(field); - query.groupBy(field); - return this; - } - - /** - * Group by a number of Object columns. - * - * @param expressions - * the group by expressions - * @return the query - */ - - public QueryWhere groupBy(Object... expressions) { - query.groupBy(expressions); - return this; - } - - public int delete() { - return query.delete(); - } - - public int update() { - return query.update(); - } - - public long selectCount() { - return query.selectCount(); - } + Query query; + + QueryWhere(Query query) { + this.query = query; + } + + /** + * Specify an AND condition with a mapped primitive boolean. + * + * @param x the primitive boolean field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(boolean x) { + query.getFrom().getAliasDefinition().checkMultipleBooleans(); + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive byte. + * + * @param x the primitive byte field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(byte x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive short. + * + * @param x the primitive short field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(short x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive int. + * + * @param x the primitive int field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(int x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive long. + * + * @param x the primitive long field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(long x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive float. + * + * @param x the primitive float field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(float x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive double. + * + * @param x the primitive double field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(double x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + private QueryCondition addPrimitive(ConditionAndOr condition, A x) { + query.addConditionToken(condition); + A alias = query.getPrimitiveAliasByValue(x); + if (alias == null) { + // this will result in an unmapped field exception + return new QueryCondition(query, x); + } + return new QueryCondition(query, alias); + } + + /** + * Specify an AND condition with a mapped Object field. + * + * @param x the Object field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(A x) { + query.getFrom().getAliasDefinition().checkMultipleEnums(x); + query.addConditionToken(ConditionAndOr.AND); + return new QueryCondition(query, x); + } + + public QueryWhere and(And conditions) { + andOpen(); + query.addConditionToken(conditions.where.query); + return close(); + } + + public QueryWhere and(Or conditions) { + andOpen(); + query.addConditionToken(conditions.where.query); + return close(); + } + + public QueryWhere andOpen() { + return open(ConditionAndOr.AND); + } + + /** + * Specify an OR condition with a mapped primitive boolean. + * + * @param x the primitive boolean field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(boolean x) { + query.getFrom().getAliasDefinition().checkMultipleBooleans(); + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped primitive byte. + * + * @param x the primitive byte field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(byte x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped primitive short. + * + * @param x the primitive short field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(short x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped primitive int. + * + * @param x the primitive int field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(int x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped primitive long. + * + * @param x the primitive long field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(long x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped primitive float. + * + * @param x the primitive float field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(float x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped primitive double. + * + * @param x the primitive double field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(double x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped Object field. + * + * @param x the Object field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(A x) { + query.getFrom().getAliasDefinition().checkMultipleEnums(x); + query.addConditionToken(ConditionAndOr.OR); + return new QueryCondition(query, x); + } + + public QueryWhere or(And conditions) { + orOpen(); + query.addConditionToken(conditions.where.query); + return close(); + } + + public QueryWhere or(Or conditions) { + orOpen(); + query.addConditionToken(conditions.where.query); + return close(); + } + + public QueryWhere orOpen() { + return open(ConditionAndOr.OR); + } + + private QueryWhere open(ConditionAndOr andOr) { + query.addConditionToken(andOr); + query.addConditionToken(ConditionOpenClose.OPEN); + return this; + } + + public QueryWhere close() { + query.addConditionToken(ConditionOpenClose.CLOSE); + return this; + } + + public QueryWhere limit(long limit) { + query.limit(limit); + return this; + } + + public QueryWhere offset(long offset) { + query.offset(offset); + return this; + } + + public String getSQL() { + SQLStatement stat = new SQLStatement(query.getDb()); + stat.appendSQL("SELECT *"); + query.appendFromWhere(stat); + return stat.getSQL().trim(); + } + + /** + * toSQL returns a static string version of the query with runtime variables + * properly encoded. This method is also useful when combined with the where + * clause methods like isParameter() or atLeastParameter() which allows + * iciql to generate re-usable parameterized string statements. + * + * @return the sql query as plain text + */ + public String toSQL() { + return query.toSQL(false); + } + + /** + * toSQL returns a static string version of the query with runtime variables + * properly encoded. This method is also useful when combined with the where + * clause methods like isParameter() or atLeastParameter() which allows + * iciql to generate re-usable parameterized string statements. + * + * @param distinct if true SELECT DISTINCT is used for the query + * @return the sql query as plain text + */ + public String toSQL(boolean distinct) { + return query.toSQL(distinct); + } + + /** + * toSQL returns a static string version of the query with runtime variables + * properly encoded. This method is also useful when combined with the where + * clause methods like isParameter() or atLeastParameter() which allows + * iciql to generate re-usable parameterized string statements. + * + * @param distinct if true SELECT DISTINCT is used for the query + * @param k k is used to select only the columns of the specified alias + * for an inner join statement. An example of a generated + * statement is: SELECT DISTINCT t1.* FROM sometable AS t1 INNER + * JOIN othertable AS t2 ON t1.id = t2.id WHERE t2.flag = true + * without the alias parameter the statement would start with + * SELECT DISTINCT * FROM... + * @return the sql query as plain text + */ + public String toSQL(boolean distinct, K k) { + return query.toSQL(distinct, k); + } + + public SubQuery subQuery(Z x) { + return new SubQuery(query, x); + } + + public SubQuery subQuery(boolean x) { + return subQuery(query.getPrimitiveAliasByValue(x)); + } + + public SubQuery subQuery(byte x) { + return subQuery(query.getPrimitiveAliasByValue(x)); + } + + public SubQuery subQuery(short x) { + return subQuery(query.getPrimitiveAliasByValue(x)); + } + + public SubQuery subQuery(int x) { + return subQuery(query.getPrimitiveAliasByValue(x)); + } + + public SubQuery subQuery(long x) { + return subQuery(query.getPrimitiveAliasByValue(x)); + } + + public SubQuery subQuery(float x) { + return subQuery(query.getPrimitiveAliasByValue(x)); + } + + public SubQuery subQuery(double x) { + return subQuery(query.getPrimitiveAliasByValue(x)); + } + + public List select(Z x) { + return query.select(x); + } + + public List selectDistinct(Z x) { + return query.selectDistinct(x); + } + + public X selectFirst(Z x) { + List list = query.select(x); + return list.isEmpty() ? null : list.get(0); + } + + public List select() { + return query.select(); + } + + public T selectFirst() { + List list = select(); + return list.isEmpty() ? null : list.get(0); + } + + public List selectDistinct() { + return query.selectDistinct(); + } + + public void createView(Class viewClass) { + query.createView(viewClass); + } + + public void replaceView(Class viewClass) { + query.replaceView(viewClass); + } + + /** + * Order by primitive boolean field + * + * @param field a primitive boolean field + * @return the query + */ + public QueryWhere orderBy(boolean field) { + query.getFrom().getAliasDefinition().checkMultipleBooleans(); + return orderByPrimitive(field); + } + + /** + * Order by primitive byte field + * + * @param field a primitive byte field + * @return the query + */ + public QueryWhere orderBy(byte field) { + return orderByPrimitive(field); + } + + /** + * Order by primitive short field + * + * @param field a primitive short field + * @return the query + */ + public QueryWhere orderBy(short field) { + return orderByPrimitive(field); + } + + public QueryWhere orderBy(int field) { + return orderByPrimitive(field); + } + + /** + * Order by primitive long field + * + * @param field a primitive long field + * @return the query + */ + public QueryWhere orderBy(long field) { + return orderByPrimitive(field); + } + + /** + * Order by primitive float field + * + * @param field a primitive float field + * @return the query + */ + public QueryWhere orderBy(float field) { + return orderByPrimitive(field); + } + + /** + * Order by primitive double field + * + * @param field a primitive double field + * @return the query + */ + public QueryWhere orderBy(double field) { + return orderByPrimitive(field); + } + + private QueryWhere orderByPrimitive(Object field) { + query.orderByPrimitive(field); + return this; + } + + public QueryWhere orderBy(Object field) { + query.getFrom().getAliasDefinition().checkMultipleEnums(field); + query.orderBy(field); + return this; + } + + /** + * Order by a number of Object columns. + * + * @param expressions the order by expressions + * @return the query + */ + + public QueryWhere orderBy(Object... expressions) { + query.orderBy(expressions); + return this; + } + + public QueryWhere orderByNullsFirst(Object expr) { + query.getFrom().getAliasDefinition().checkMultipleEnums(expr); + OrderExpression e = new OrderExpression(query, expr, false, true, false); + query.addOrderBy(e); + return this; + } + + public QueryWhere orderByNullsLast(Object expr) { + query.getFrom().getAliasDefinition().checkMultipleEnums(expr); + OrderExpression e = new OrderExpression(query, expr, false, false, true); + query.addOrderBy(e); + return this; + } + + public QueryWhere orderByDesc(Object expr) { + query.getFrom().getAliasDefinition().checkMultipleEnums(expr); + OrderExpression e = new OrderExpression(query, expr, true, false, false); + query.addOrderBy(e); + return this; + } + + public QueryWhere orderByDescNullsFirst(Object expr) { + query.getFrom().getAliasDefinition().checkMultipleEnums(expr); + OrderExpression e = new OrderExpression(query, expr, true, true, false); + query.addOrderBy(e); + return this; + } + + public QueryWhere orderByDescNullsLast(Object expr) { + query.getFrom().getAliasDefinition().checkMultipleEnums(expr); + OrderExpression e = new OrderExpression(query, expr, true, false, true); + query.addOrderBy(e); + return this; + } + + /** + * Group by primitive boolean field + * + * @param field a primitive boolean field + * @return the query + */ + public QueryWhere groupBy(boolean field) { + query.getFrom().getAliasDefinition().checkMultipleBooleans(); + return groupByPrimitive(field); + } + + /** + * Group by primitive byte field + * + * @param field a primitive byte field + * @return the query + */ + public QueryWhere groupBy(byte field) { + return groupByPrimitive(field); + } + + /** + * Group by primitive short field + * + * @param field a primitive short field + * @return the query + */ + public QueryWhere groupBy(short field) { + return groupByPrimitive(field); + } + + public QueryWhere groupBy(int field) { + return groupByPrimitive(field); + } + + /** + * Group by primitive long field + * + * @param field a primitive long field + * @return the query + */ + public QueryWhere groupBy(long field) { + return groupByPrimitive(field); + } + + /** + * Group by primitive float field + * + * @param field a primitive float field + * @return the query + */ + public QueryWhere groupBy(float field) { + return groupByPrimitive(field); + } + + /** + * Group by primitive double field + * + * @param field a primitive double field + * @return the query + */ + public QueryWhere groupBy(double field) { + return groupByPrimitive(field); + } + + private QueryWhere groupByPrimitive(Object field) { + query.groupByPrimitive(field); + return this; + } + + public QueryWhere groupBy(Object field) { + query.getFrom().getAliasDefinition().checkMultipleEnums(field); + query.groupBy(field); + return this; + } + + /** + * Group by a number of Object columns. + * + * @param expressions the group by expressions + * @return the query + */ + + public QueryWhere groupBy(Object... expressions) { + query.groupBy(expressions); + return this; + } + + public int delete() { + return query.delete(); + } + + public int update() { + return query.update(); + } + + public long selectCount() { + return query.selectCount(); + } } diff --git a/src/main/java/com/iciql/RuntimeParameter.java b/src/main/java/com/iciql/RuntimeParameter.java index 0fbedba..f60007e 100644 --- a/src/main/java/com/iciql/RuntimeParameter.java +++ b/src/main/java/com/iciql/RuntimeParameter.java @@ -20,30 +20,29 @@ package com.iciql; * A runtime parameter is used to generate x=? conditions so that iciql can * build re-usable dynamic queries with parameter substitution done manually at * runtime. - * - * @param - * the operand type + * + * @param the operand type */ class RuntimeParameter implements Token { - - public final static String PARAMETER = ""; - - A x; - CompareType compareType; - RuntimeParameter(A x, CompareType type) { - this.x = x; - this.compareType = type; - } + public final static String PARAMETER = ""; + + A x; + CompareType compareType; + + RuntimeParameter(A x, CompareType type) { + this.x = x; + this.compareType = type; + } - public void appendSQL(SQLStatement stat, Query query) { - query.appendSQL(stat, null, x); - stat.appendSQL(" "); - stat.appendSQL(compareType.getString()); - if (compareType.hasRightExpression()) { - stat.appendSQL(" "); - query.appendSQL(stat, x, PARAMETER); - } - } + public void appendSQL(SQLStatement stat, Query query) { + query.appendSQL(stat, null, x); + stat.appendSQL(" "); + stat.appendSQL(compareType.getString()); + if (compareType.hasRightExpression()) { + stat.appendSQL(" "); + query.appendSQL(stat, x, PARAMETER); + } + } } diff --git a/src/main/java/com/iciql/RuntimeToken.java b/src/main/java/com/iciql/RuntimeToken.java index cbfd882..cc747a4 100644 --- a/src/main/java/com/iciql/RuntimeToken.java +++ b/src/main/java/com/iciql/RuntimeToken.java @@ -15,43 +15,40 @@ */ package com.iciql; -import java.text.MessageFormat; - import com.iciql.util.StringUtils; +import java.text.MessageFormat; + /** * Represents a traditional PreparedStatment fragment like "id=?, name=?". - * */ public class RuntimeToken implements Token { - final String fragment; - final Object[] args; + final String fragment; + final Object[] args; - public RuntimeToken(String fragment, Object... args) { - this.fragment = fragment; - this.args = args == null ? new Object[0] : args; - } + public RuntimeToken(String fragment, Object... args) { + this.fragment = fragment; + this.args = args == null ? new Object[0] : args; + } - /** - * Append the SQL to the given statement using the given query. - * - * @param stat - * the statement to append the SQL to - * @param query - * the query to use - */ - @Override - public void appendSQL(SQLStatement stat, Query query) { - int tokenCount = StringUtils.count('?', fragment); - if (tokenCount != args.length) { - throw new IciqlException(MessageFormat.format( - "Fragment \"{0}\" specifies {1} tokens but you supplied {2} args", fragment, tokenCount, - args.length)); - } - stat.appendSQL(fragment); - for (Object arg : args) { - stat.addParameter(arg); - } - } + /** + * Append the SQL to the given statement using the given query. + * + * @param stat the statement to append the SQL to + * @param query the query to use + */ + @Override + public void appendSQL(SQLStatement stat, Query query) { + int tokenCount = StringUtils.count('?', fragment); + if (tokenCount != args.length) { + throw new IciqlException(MessageFormat.format( + "Fragment \"{0}\" specifies {1} tokens but you supplied {2} args", fragment, tokenCount, + args.length)); + } + stat.appendSQL(fragment); + for (Object arg : args) { + stat.addParameter(arg); + } + } } diff --git a/src/main/java/com/iciql/SQLDialect.java b/src/main/java/com/iciql/SQLDialect.java index 8e6361a..1cb4931 100644 --- a/src/main/java/com/iciql/SQLDialect.java +++ b/src/main/java/com/iciql/SQLDialect.java @@ -18,210 +18,188 @@ package com.iciql; -import java.sql.ResultSet; - import com.iciql.Iciql.DataTypeAdapter; import com.iciql.TableDefinition.IndexDefinition; +import java.sql.ResultSet; + /** * This interface defines points where iciql can build different statements * depending on the database used. */ public interface SQLDialect { - /** - * Registers the type adapter instance. - * - * @param typeAdapter - */ - void registerAdapter(DataTypeAdapter typeAdapter); - - /** - * Returns the registered instance of the type adapter. - * - * @param typeAdapter - * @return the type adapter instance - */ - DataTypeAdapter getAdapter(Class> typeAdapter); - - /** - * Serialize the Java object into a type or format that the database will accept. - * - * @param value - * @param typeAdapter - * @return the serialized object - */ - Object serialize(T value, Class> typeAdapter); - - /** - * Deserialize the object received from the database into a Java type. - * - * @param rs - * @param columnIndex - * @param targetType - * @param typeAdapter - * @return the deserialized object - */ - Object deserialize(ResultSet rs, int columnIndex, Class targetType, Class> typeAdapter); - - /** - * Configure the dialect. - * - * @param db - */ - void configureDialect(Db db); - - /** - * Returns true if savepoints are supported. - * - * @return true if savepoints may be used. - */ - boolean supportsSavePoints(); - - /** - * Allows a dialect to substitute an SQL type. - * - * @param sqlType - * @return the dialect-safe type - */ - String convertSqlType(String sqlType); - - /** - * Returns a properly formatted table name for the dialect. - * - * @param schemaName - * the schema name, or null for no schema - * @param tableName - * the properly formatted table name - * @return the SQL snippet - */ - String prepareTableName(String schemaName, String tableName); - - /** - * Returns a properly formatted column name for the dialect. - * - * @param name - * the column name - * @return the properly formatted column name - */ - String prepareColumnName(String name); - - /** - * Get the CREATE TABLE statement. - * - * @param stat - * @param def - */ - void prepareCreateTable(SQLStatement stat, TableDefinition def); - - /** - * Get the DROP TABLE statement. - * - * @param stat - * @param def - */ - void prepareDropTable(SQLStatement stat, TableDefinition def); - - - /** - * Get the CREATE VIEW statement. - * - * @param stat - * return the SQL statement - * @param def - * table definition - */ - void prepareCreateView(SQLStatement stat, TableDefinition def); - - /** - * Get the CREATE VIEW statement. - * - * @param stat - * return the SQL statement - * @param def - * table definition - * @param fromWhere - */ - void prepareCreateView(SQLStatement stat, TableDefinition def, String fromWhere); - - /** - * Get the DROP VIEW statement. - * - * @param stat - * return the SQL statement - * @param def - * table definition - */ - void prepareDropView(SQLStatement stat, TableDefinition 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 - */ - void prepareCreateIndex(SQLStatement stat, String schemaName, String tableName, IndexDefinition index); - - /** - * Get a MERGE or REPLACE INTO statement. - * - * @param stat - * return the SQL statement - * @param schemaName - * the schema name - * @param tableName - * the table name - * @param def - * the table definition - * @param obj - * values - */ - void prepareMerge(SQLStatement stat, String schemaName, String tableName, TableDefinition def, - Object obj); - - /** - * Append "LIMIT limit OFFSET offset" to the SQL statement. - * - * @param stat - * the statement - * @param limit - * the limit - * @param offset - * the offset - */ - void appendLimitOffset(SQLStatement stat, long limit, long offset); - - /** - * Returns the preferred DATETIME class for the database. - *

- * Either java.util.Date or java.sql.Timestamp - * - * @return preferred DATETIME class - */ - Class getDateTimeClass(); - - /** - * When building static string statements this method flattens an object to - * a string representation suitable for a static string statement. - * - * @param o - * @return the string equivalent of this object - */ - String prepareStringParameter(Object o); - - /** - * Returns the name of a formatted column identifier for the dialect. - * - * @param name - * the column name - * @return the column name without formatting syntax - */ - String extractColumnName(String name); + /** + * Registers the type adapter instance. + * + * @param typeAdapter + */ + void registerAdapter(DataTypeAdapter typeAdapter); + + /** + * Returns the registered instance of the type adapter. + * + * @param typeAdapter + * @return the type adapter instance + */ + DataTypeAdapter getAdapter(Class> typeAdapter); + + /** + * Serialize the Java object into a type or format that the database will accept. + * + * @param value + * @param typeAdapter + * @return the serialized object + */ + Object serialize(T value, Class> typeAdapter); + + /** + * Deserialize the object received from the database into a Java type. + * + * @param rs + * @param columnIndex + * @param targetType + * @param typeAdapter + * @return the deserialized object + */ + Object deserialize(ResultSet rs, int columnIndex, Class targetType, Class> typeAdapter); + + /** + * Configure the dialect. + * + * @param db + */ + void configureDialect(Db db); + + /** + * Returns true if savepoints are supported. + * + * @return true if savepoints may be used. + */ + boolean supportsSavePoints(); + + /** + * Allows a dialect to substitute an SQL type. + * + * @param sqlType + * @return the dialect-safe type + */ + String convertSqlType(String sqlType); + + /** + * Returns a properly formatted table name for the dialect. + * + * @param schemaName the schema name, or null for no schema + * @param tableName the properly formatted table name + * @return the SQL snippet + */ + String prepareTableName(String schemaName, String tableName); + + /** + * Returns a properly formatted column name for the dialect. + * + * @param name the column name + * @return the properly formatted column name + */ + String prepareColumnName(String name); + + /** + * Get the CREATE TABLE statement. + * + * @param stat + * @param def + */ + void prepareCreateTable(SQLStatement stat, TableDefinition def); + + /** + * Get the DROP TABLE statement. + * + * @param stat + * @param def + */ + void prepareDropTable(SQLStatement stat, TableDefinition def); + + + /** + * Get the CREATE VIEW statement. + * + * @param stat return the SQL statement + * @param def table definition + */ + void prepareCreateView(SQLStatement stat, TableDefinition def); + + /** + * Get the CREATE VIEW statement. + * + * @param stat return the SQL statement + * @param def table definition + * @param fromWhere + */ + void prepareCreateView(SQLStatement stat, TableDefinition def, String fromWhere); + + /** + * Get the DROP VIEW statement. + * + * @param stat return the SQL statement + * @param def table definition + */ + void prepareDropView(SQLStatement stat, TableDefinition 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 + */ + void prepareCreateIndex(SQLStatement stat, String schemaName, String tableName, IndexDefinition index); + + /** + * Get a MERGE or REPLACE INTO statement. + * + * @param stat return the SQL statement + * @param schemaName the schema name + * @param tableName the table name + * @param def the table definition + * @param obj values + */ + void prepareMerge(SQLStatement stat, String schemaName, String tableName, TableDefinition def, + Object obj); + + /** + * Append "LIMIT limit OFFSET offset" to the SQL statement. + * + * @param stat the statement + * @param limit the limit + * @param offset the offset + */ + void appendLimitOffset(SQLStatement stat, long limit, long offset); + + /** + * Returns the preferred DATETIME class for the database. + *

+ * Either java.util.Date or java.sql.Timestamp + * + * @return preferred DATETIME class + */ + Class getDateTimeClass(); + + /** + * When building static string statements this method flattens an object to + * a string representation suitable for a static string statement. + * + * @param o + * @return the string equivalent of this object + */ + String prepareStringParameter(Object o); + + /** + * Returns the name of a formatted column identifier for the dialect. + * + * @param name the column name + * @return the column name without formatting syntax + */ + String extractColumnName(String name); } diff --git a/src/main/java/com/iciql/SQLDialectDefault.java b/src/main/java/com/iciql/SQLDialectDefault.java index ec3f605..4f635a9 100644 --- a/src/main/java/com/iciql/SQLDialectDefault.java +++ b/src/main/java/com/iciql/SQLDialectDefault.java @@ -18,17 +18,6 @@ package com.iciql; -import java.sql.Blob; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.text.MessageFormat; -import java.text.SimpleDateFormat; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - import com.iciql.Iciql.ConstraintDeleteType; import com.iciql.Iciql.ConstraintUpdateType; import com.iciql.Iciql.DataTypeAdapter; @@ -42,491 +31,502 @@ import com.iciql.util.StatementBuilder; import com.iciql.util.StringUtils; import com.iciql.util.Utils; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + /** * Default implementation of an SQL dialect. */ public class SQLDialectDefault implements SQLDialect { - final String LITERAL = "'"; - - protected float databaseVersion; - protected int databaseMajorVersion; - protected int databaseMinorVersion; - protected String databaseName; - protected String productVersion; - protected Mode mode; - protected Map>, DataTypeAdapter> typeAdapters; - - public SQLDialectDefault() { - typeAdapters = new ConcurrentHashMap>, DataTypeAdapter>(); - } - - @Override - public String toString() { - return getClass().getName() + ": " + databaseName + " " + productVersion; - } - - @Override - public void configureDialect(Db db) { - Connection conn = db.getConnection(); - DatabaseMetaData data = null; - try { - data = conn.getMetaData(); - databaseName = data.getDatabaseProductName(); - databaseMajorVersion = data.getDatabaseMajorVersion(); - databaseMinorVersion = data.getDatabaseMinorVersion(); - databaseVersion = Float.parseFloat(databaseMajorVersion + "." - + databaseMinorVersion); - productVersion = data.getDatabaseProductVersion(); - } catch (SQLException e) { - throw new IciqlException(e, "failed to retrieve database metadata!"); - } - - mode = db.getMode(); - } - - @Override - public boolean supportsSavePoints() { - return true; - } - - /** - * Allows subclasses to change the type of a column for a CREATE statement. - * - * @param sqlType - * @return the SQL type or a preferred alternative - */ - @Override - public String convertSqlType(String sqlType) { - return sqlType; - } - - @Override - public Class getDateTimeClass() { - return java.util.Date.class; - } - - @Override - public String prepareTableName(String schemaName, String tableName) { - if (StringUtils.isNullOrEmpty(schemaName)) { - return tableName; - } - return schemaName + "." + tableName; - } - - @Override - public String prepareColumnName(String name) { - return name; - } - - @Override - public String extractColumnName(String name) { - return name.replace('\"', ' ').replace('\'', ' ').trim(); - } - - @Override - public void prepareDropTable(SQLStatement stat, TableDefinition def) { - StatementBuilder buff = new StatementBuilder("DROP TABLE IF EXISTS " - + prepareTableName(def.schemaName, def.tableName)); - stat.setSQL(buff.toString()); - return; - } - - protected String prepareCreateTable(TableDefinition def) { - return "CREATE TABLE"; - } - - @Override - public void prepareCreateTable(SQLStatement stat, TableDefinition def) { - StatementBuilder buff = new StatementBuilder(); - buff.append(prepareCreateTable(def)); - buff.append(" "); - buff.append(prepareTableName(def.schemaName, def.tableName)).append('('); - - boolean hasIdentityColumn = false; - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(field.columnName)).append(' '); - String dataType = field.dataType; - if (dataType.equals("VARCHAR")) { - // check to see if we should use VARCHAR or CLOB - if (field.length <= 0) { - dataType = "CLOB"; - } - buff.append(convertSqlType(dataType)); - if (field.length > 0) { - buff.append('(').append(field.length).append(')'); - } - } else if (dataType.equals("DECIMAL")) { - // DECIMAL(precision,scale) - buff.append(convertSqlType(dataType)); - if (field.length > 0) { - buff.append('(').append(field.length); - if (field.scale > 0) { - buff.append(',').append(field.scale); - } - buff.append(')'); - } - } else { - // other - hasIdentityColumn |= prepareColumnDefinition(buff, convertSqlType(dataType), - field.isAutoIncrement, field.isPrimaryKey); - } - - // default values - if (!field.isAutoIncrement && !field.isPrimaryKey) { - String dv = field.defaultValue; - if (!StringUtils.isNullOrEmpty(dv)) { - if (ModelUtils.isProperlyFormattedDefaultValue(dv) - && ModelUtils.isValidDefaultValue(field.field.getType(), dv)) { - buff.append(" DEFAULT " + dv); - } - } - } - - if (!field.nullable) { - buff.append(" NOT NULL"); - } - } - - // if table does not have identity column then specify primary key - if (!hasIdentityColumn) { - if (def.primaryKeyColumnNames != null && def.primaryKeyColumnNames.size() > 0) { - buff.append(", PRIMARY KEY("); - buff.resetCount(); - for (String n : def.primaryKeyColumnNames) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(n)); - } - buff.append(')'); - } - } - - // create unique constraints - if (def.constraintsUnique.size() > 0) { - buff.append(", "); - buff.resetCount(); - for (ConstraintUniqueDefinition constraint : def.constraintsUnique) { - buff.append("CONSTRAINT "); - buff.append(constraint.constraintName); - buff.append(" UNIQUE "); - buff.append(" ("); - for (String col : constraint.uniqueColumns) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(col)); - } - buff.append(") "); - } - } - - // create foreign key constraints - if (def.constraintsForeignKey.size() > 0) { - buff.append(", "); - buff.resetCount(); - for (ConstraintForeignKeyDefinition constraint : def.constraintsForeignKey) { - buff.appendExceptFirst(", "); - buff.append(String.format("CONSTRAINT %s FOREIGN KEY(%s) REFERENCES %s(%s)", - constraint.constraintName, - constraint.foreignColumns.get(0), - constraint.referenceTable, - constraint.referenceColumns.get(0))); - - if (constraint.deleteType != ConstraintDeleteType.UNSET) { - buff.append(" ON DELETE "); - switch (constraint.deleteType) { - case CASCADE: - buff.append("CASCADE "); - break; - case RESTRICT: - buff.append("RESTRICT "); - break; - case SET_NULL: - buff.append("SET NULL "); - break; - case NO_ACTION: - buff.append("NO ACTION "); - break; - case SET_DEFAULT: - buff.append("SET DEFAULT "); - break; - } - } - if (constraint.updateType != ConstraintUpdateType.UNSET) { - buff.append(" ON UPDATE "); - switch (constraint.updateType) { - case CASCADE: - buff.append("CASCADE "); - break; - case RESTRICT: - buff.append("RESTRICT "); - break; - case SET_NULL: - buff.append("SET NULL "); - break; - case NO_ACTION: - buff.append("NO ACTION "); - break; - case SET_DEFAULT: - buff.append("SET DEFAULT "); - break; - } - } - switch (constraint.deferrabilityType) { - case DEFERRABLE_INITIALLY_DEFERRED: - buff.append("DEFERRABLE INITIALLY DEFERRED "); - break; - case DEFERRABLE_INITIALLY_IMMEDIATE: - buff.append("DEFERRABLE INITIALLY IMMEDIATE "); - break; - case NOT_DEFERRABLE: - buff.append("NOT DEFERRABLE "); - break; - case UNSET: - break; - } - } - } - - buff.append(')'); - stat.setSQL(buff.toString()); - } - - @Override - public void prepareDropView(SQLStatement stat, TableDefinition def) { - StatementBuilder buff = new StatementBuilder("DROP VIEW " - + prepareTableName(def.schemaName, def.tableName)); - stat.setSQL(buff.toString()); - return; - } - - protected String prepareCreateView(TableDefinition def) { - return "CREATE VIEW"; - } - - @Override - public void prepareCreateView(SQLStatement stat, TableDefinition def) { - StatementBuilder buff = new StatementBuilder(); - buff.append(" FROM "); - buff.append(prepareTableName(def.schemaName, def.viewTableName)); - - StatementBuilder where = new StatementBuilder(); - for (FieldDefinition field : def.fields) { - if (!StringUtils.isNullOrEmpty(field.constraint)) { - where.appendExceptFirst(", "); - String col = prepareColumnName(field.columnName); - String constraint = field.constraint.replace("{0}", col).replace("this", col); - where.append(constraint); - } - } - if (where.length() > 0) { - buff.append(" WHERE "); - buff.append(where.toString()); - } - - prepareCreateView(stat, def, buff.toString()); - } - - @Override - public void prepareCreateView(SQLStatement stat, TableDefinition def, String fromWhere) { - StatementBuilder buff = new StatementBuilder(); - buff.append(prepareCreateView(def)); - buff.append(" "); - buff.append(prepareTableName(def.schemaName, def.tableName)); - - buff.append(" AS SELECT "); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(field.columnName)); - } - buff.append(fromWhere); - stat.setSQL(buff.toString()); - } - - protected boolean isIntegerType(String dataType) { - if ("INT".equals(dataType)) { - return true; - } else if ("INTEGER".equals(dataType)) { - return true; - } else if ("TINYINT".equals(dataType)) { - return true; - } else if ("SMALLINT".equals(dataType)) { - return true; - } else if ("MEDIUMINT".equals(dataType)) { - return true; - } else if ("BIGINT".equals(dataType)) { - return true; - } - return false; - } - - protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, - boolean isAutoIncrement, boolean isPrimaryKey) { - buff.append(dataType); - if (isAutoIncrement) { - buff.append(" AUTO_INCREMENT"); - } - return false; - } - - @Override - public void prepareCreateIndex(SQLStatement stat, String schemaName, String tableName, - IndexDefinition index) { - StatementBuilder buff = new StatementBuilder(); - buff.append("CREATE "); - switch (index.type) { - case UNIQUE: - buff.append("UNIQUE "); - break; - case UNIQUE_HASH: - buff.append("UNIQUE "); - break; - default: - IciqlLogger.warn("{0} does not support hash indexes", getClass().getSimpleName()); - } - buff.append("INDEX "); - buff.append(index.indexName); - buff.append(" ON "); - // FIXME maybe we can use schemaName ? - // buff.append(prepareTableName(schemaName, tableName)); - buff.append(tableName); - buff.append("("); - for (String col : index.columnNames) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(col)); - } - buff.append(") "); - - stat.setSQL(buff.toString().trim()); - } - - /** - * PostgreSQL and Derby do not support the SQL2003 MERGE syntax, but we can - * use a trick to insert a row if it does not exist and call update() in - * Db.merge() if the affected row count is 0. - *

- * Databases that do support a MERGE syntax should override this method. - *

- * http://stackoverflow.com/questions/407688 - */ - @Override - public void prepareMerge(SQLStatement stat, String schemaName, String tableName, - TableDefinition def, Object obj) { - StatementBuilder buff = new StatementBuilder("INSERT INTO "); - buff.append(prepareTableName(schemaName, tableName)); - buff.append(" ("); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(field.columnName)); - } - buff.append(") (SELECT "); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append('?'); - Object value = def.getValue(obj, field); - Object parameter = serialize(value, field.typeAdapter); - stat.addParameter(parameter); - } - buff.append(" FROM "); - buff.append(prepareTableName(schemaName, tableName)); - buff.append(" WHERE "); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - if (field.isPrimaryKey) { - buff.appendExceptFirst(" AND "); - buff.append(MessageFormat.format("{0} = ?", prepareColumnName(field.columnName))); - Object value = def.getValue(obj, field); - Object parameter = serialize(value, field.typeAdapter); - stat.addParameter(parameter); - } - } - buff.append(" HAVING count(*)=0)"); - stat.setSQL(buff.toString()); - } - - @Override - public void appendLimitOffset(SQLStatement stat, long limit, long offset) { - if (limit > 0) { - stat.appendSQL(" LIMIT " + limit); - } - if (offset > 0) { - stat.appendSQL(" OFFSET " + offset); - } - } - - @Override - public void registerAdapter(DataTypeAdapter typeAdapter) { - typeAdapters.put((Class>) typeAdapter.getClass(), typeAdapter); - } - - @Override - public DataTypeAdapter getAdapter(Class> typeAdapter) { - DataTypeAdapter dta = typeAdapters.get(typeAdapter); - if (dta == null) { - dta = Utils.newObject(typeAdapter); - typeAdapters.put(typeAdapter, dta); - } - dta.setMode(mode); - return dta; - } - - @SuppressWarnings("unchecked") - @Override - public Object serialize(T value, Class> typeAdapter) { - if (typeAdapter == null) { - // pass-through - return value; - } - - DataTypeAdapter dta = (DataTypeAdapter) getAdapter(typeAdapter); - return dta.serialize(value); - } - - @Override - public Object deserialize(ResultSet rs, int columnIndex, Class targetType, Class> typeAdapter) { - Object value = null; - try { - if (typeAdapter == null) { - // standard object deserialization - Object o = rs.getObject(columnIndex); - if (o == null) { - // no-op - value = null; - } else if (Clob.class.isAssignableFrom(o.getClass())) { - value = Utils.convert(o, String.class); - } else if (Blob.class.isAssignableFrom(o.getClass())) { - value = Utils.convert(o, byte[].class); - } else { - value = Utils.convert(o, targetType); - } - } else { - // custom object deserialization with a DataTypeAdapter - DataTypeAdapter dta = getAdapter(typeAdapter); - Object object = rs.getObject(columnIndex); - value = dta.deserialize(object); - } - } catch (SQLException e) { - throw new IciqlException(e, "Can not convert the value at column {0} to {1}", - columnIndex, targetType.getName()); - } - return value; - } - - @Override - public String prepareStringParameter(Object o) { - if (o instanceof String) { - return LITERAL + o.toString().replace(LITERAL, "''") + LITERAL; - } else if (o instanceof Character) { - return LITERAL + o.toString() + LITERAL; - } else if (o instanceof java.sql.Time) { - return LITERAL + new SimpleDateFormat("HH:mm:ss").format(o) + LITERAL; - } else if (o instanceof java.sql.Date) { - return LITERAL + new SimpleDateFormat("yyyy-MM-dd").format(o) + LITERAL; - } else if (o instanceof java.util.Date) { - return LITERAL + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(o) + LITERAL; - } - return o.toString(); - } + final String LITERAL = "'"; + + protected float databaseVersion; + protected int databaseMajorVersion; + protected int databaseMinorVersion; + protected String databaseName; + protected String productVersion; + protected Mode mode; + protected Map>, DataTypeAdapter> typeAdapters; + + public SQLDialectDefault() { + typeAdapters = new ConcurrentHashMap>, DataTypeAdapter>(); + } + + @Override + public String toString() { + return getClass().getName() + ": " + databaseName + " " + productVersion; + } + + @Override + public void configureDialect(Db db) { + Connection conn = db.getConnection(); + DatabaseMetaData data = null; + try { + data = conn.getMetaData(); + databaseName = data.getDatabaseProductName(); + databaseMajorVersion = data.getDatabaseMajorVersion(); + databaseMinorVersion = data.getDatabaseMinorVersion(); + databaseVersion = Float.parseFloat(databaseMajorVersion + "." + + databaseMinorVersion); + productVersion = data.getDatabaseProductVersion(); + } catch (SQLException e) { + throw new IciqlException(e, "failed to retrieve database metadata!"); + } + + mode = db.getMode(); + } + + @Override + public boolean supportsSavePoints() { + return true; + } + + /** + * Allows subclasses to change the type of a column for a CREATE statement. + * + * @param sqlType + * @return the SQL type or a preferred alternative + */ + @Override + public String convertSqlType(String sqlType) { + return sqlType; + } + + @Override + public Class getDateTimeClass() { + return java.util.Date.class; + } + + @Override + public String prepareTableName(String schemaName, String tableName) { + if (StringUtils.isNullOrEmpty(schemaName)) { + return tableName; + } + return schemaName + "." + tableName; + } + + @Override + public String prepareColumnName(String name) { + return name; + } + + @Override + public String extractColumnName(String name) { + return name.replace('\"', ' ').replace('\'', ' ').trim(); + } + + @Override + public void prepareDropTable(SQLStatement stat, TableDefinition def) { + StatementBuilder buff = new StatementBuilder("DROP TABLE IF EXISTS " + + prepareTableName(def.schemaName, def.tableName)); + stat.setSQL(buff.toString()); + return; + } + + protected String prepareCreateTable(TableDefinition def) { + return "CREATE TABLE"; + } + + @Override + public void prepareCreateTable(SQLStatement stat, TableDefinition def) { + StatementBuilder buff = new StatementBuilder(); + buff.append(prepareCreateTable(def)); + buff.append(" "); + buff.append(prepareTableName(def.schemaName, def.tableName)).append('('); + + boolean hasIdentityColumn = false; + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(field.columnName)).append(' '); + String dataType = field.dataType; + if (dataType.equals("VARCHAR")) { + // check to see if we should use VARCHAR or CLOB + if (field.length <= 0) { + dataType = "CLOB"; + } + buff.append(convertSqlType(dataType)); + if (field.length > 0) { + buff.append('(').append(field.length).append(')'); + } + } else if (dataType.equals("DECIMAL")) { + // DECIMAL(precision,scale) + buff.append(convertSqlType(dataType)); + if (field.length > 0) { + buff.append('(').append(field.length); + if (field.scale > 0) { + buff.append(',').append(field.scale); + } + buff.append(')'); + } + } else { + // other + hasIdentityColumn |= prepareColumnDefinition(buff, convertSqlType(dataType), + field.isAutoIncrement, field.isPrimaryKey); + } + + // default values + if (!field.isAutoIncrement && !field.isPrimaryKey) { + String dv = field.defaultValue; + if (!StringUtils.isNullOrEmpty(dv)) { + if (ModelUtils.isProperlyFormattedDefaultValue(dv) + && ModelUtils.isValidDefaultValue(field.field.getType(), dv)) { + buff.append(" DEFAULT " + dv); + } + } + } + + if (!field.nullable) { + buff.append(" NOT NULL"); + } + } + + // if table does not have identity column then specify primary key + if (!hasIdentityColumn) { + if (def.primaryKeyColumnNames != null && def.primaryKeyColumnNames.size() > 0) { + buff.append(", PRIMARY KEY("); + buff.resetCount(); + for (String n : def.primaryKeyColumnNames) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(n)); + } + buff.append(')'); + } + } + + // create unique constraints + if (def.constraintsUnique.size() > 0) { + buff.append(", "); + buff.resetCount(); + for (ConstraintUniqueDefinition constraint : def.constraintsUnique) { + buff.append("CONSTRAINT "); + buff.append(constraint.constraintName); + buff.append(" UNIQUE "); + buff.append(" ("); + for (String col : constraint.uniqueColumns) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(col)); + } + buff.append(") "); + } + } + + // create foreign key constraints + if (def.constraintsForeignKey.size() > 0) { + buff.append(", "); + buff.resetCount(); + for (ConstraintForeignKeyDefinition constraint : def.constraintsForeignKey) { + buff.appendExceptFirst(", "); + buff.append(String.format("CONSTRAINT %s FOREIGN KEY(%s) REFERENCES %s(%s)", + constraint.constraintName, + constraint.foreignColumns.get(0), + constraint.referenceTable, + constraint.referenceColumns.get(0))); + + if (constraint.deleteType != ConstraintDeleteType.UNSET) { + buff.append(" ON DELETE "); + switch (constraint.deleteType) { + case CASCADE: + buff.append("CASCADE "); + break; + case RESTRICT: + buff.append("RESTRICT "); + break; + case SET_NULL: + buff.append("SET NULL "); + break; + case NO_ACTION: + buff.append("NO ACTION "); + break; + case SET_DEFAULT: + buff.append("SET DEFAULT "); + break; + } + } + if (constraint.updateType != ConstraintUpdateType.UNSET) { + buff.append(" ON UPDATE "); + switch (constraint.updateType) { + case CASCADE: + buff.append("CASCADE "); + break; + case RESTRICT: + buff.append("RESTRICT "); + break; + case SET_NULL: + buff.append("SET NULL "); + break; + case NO_ACTION: + buff.append("NO ACTION "); + break; + case SET_DEFAULT: + buff.append("SET DEFAULT "); + break; + } + } + switch (constraint.deferrabilityType) { + case DEFERRABLE_INITIALLY_DEFERRED: + buff.append("DEFERRABLE INITIALLY DEFERRED "); + break; + case DEFERRABLE_INITIALLY_IMMEDIATE: + buff.append("DEFERRABLE INITIALLY IMMEDIATE "); + break; + case NOT_DEFERRABLE: + buff.append("NOT DEFERRABLE "); + break; + case UNSET: + break; + } + } + } + + buff.append(')'); + stat.setSQL(buff.toString()); + } + + @Override + public void prepareDropView(SQLStatement stat, TableDefinition def) { + StatementBuilder buff = new StatementBuilder("DROP VIEW " + + prepareTableName(def.schemaName, def.tableName)); + stat.setSQL(buff.toString()); + return; + } + + protected String prepareCreateView(TableDefinition def) { + return "CREATE VIEW"; + } + + @Override + public void prepareCreateView(SQLStatement stat, TableDefinition def) { + StatementBuilder buff = new StatementBuilder(); + buff.append(" FROM "); + buff.append(prepareTableName(def.schemaName, def.viewTableName)); + + StatementBuilder where = new StatementBuilder(); + for (FieldDefinition field : def.fields) { + if (!StringUtils.isNullOrEmpty(field.constraint)) { + where.appendExceptFirst(", "); + String col = prepareColumnName(field.columnName); + String constraint = field.constraint.replace("{0}", col).replace("this", col); + where.append(constraint); + } + } + if (where.length() > 0) { + buff.append(" WHERE "); + buff.append(where.toString()); + } + + prepareCreateView(stat, def, buff.toString()); + } + + @Override + public void prepareCreateView(SQLStatement stat, TableDefinition def, String fromWhere) { + StatementBuilder buff = new StatementBuilder(); + buff.append(prepareCreateView(def)); + buff.append(" "); + buff.append(prepareTableName(def.schemaName, def.tableName)); + + buff.append(" AS SELECT "); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(field.columnName)); + } + buff.append(fromWhere); + stat.setSQL(buff.toString()); + } + + protected boolean isIntegerType(String dataType) { + if ("INT".equals(dataType)) { + return true; + } else if ("INTEGER".equals(dataType)) { + return true; + } else if ("TINYINT".equals(dataType)) { + return true; + } else if ("SMALLINT".equals(dataType)) { + return true; + } else if ("MEDIUMINT".equals(dataType)) { + return true; + } else if ("BIGINT".equals(dataType)) { + return true; + } + return false; + } + + protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, + boolean isAutoIncrement, boolean isPrimaryKey) { + buff.append(dataType); + if (isAutoIncrement) { + buff.append(" AUTO_INCREMENT"); + } + return false; + } + + @Override + public void prepareCreateIndex(SQLStatement stat, String schemaName, String tableName, + IndexDefinition index) { + StatementBuilder buff = new StatementBuilder(); + buff.append("CREATE "); + switch (index.type) { + case UNIQUE: + buff.append("UNIQUE "); + break; + case UNIQUE_HASH: + buff.append("UNIQUE "); + break; + default: + IciqlLogger.warn("{0} does not support hash indexes", getClass().getSimpleName()); + } + buff.append("INDEX "); + buff.append(index.indexName); + buff.append(" ON "); + // FIXME maybe we can use schemaName ? + // buff.append(prepareTableName(schemaName, tableName)); + buff.append(tableName); + buff.append("("); + for (String col : index.columnNames) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(col)); + } + buff.append(") "); + + stat.setSQL(buff.toString().trim()); + } + + /** + * PostgreSQL and Derby do not support the SQL2003 MERGE syntax, but we can + * use a trick to insert a row if it does not exist and call update() in + * Db.merge() if the affected row count is 0. + *

+ * Databases that do support a MERGE syntax should override this method. + *

+ * http://stackoverflow.com/questions/407688 + */ + @Override + public void prepareMerge(SQLStatement stat, String schemaName, String tableName, + TableDefinition def, Object obj) { + StatementBuilder buff = new StatementBuilder("INSERT INTO "); + buff.append(prepareTableName(schemaName, tableName)); + buff.append(" ("); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(field.columnName)); + } + buff.append(") (SELECT "); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append('?'); + Object value = def.getValue(obj, field); + Object parameter = serialize(value, field.typeAdapter); + stat.addParameter(parameter); + } + buff.append(" FROM "); + buff.append(prepareTableName(schemaName, tableName)); + buff.append(" WHERE "); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + if (field.isPrimaryKey) { + buff.appendExceptFirst(" AND "); + buff.append(MessageFormat.format("{0} = ?", prepareColumnName(field.columnName))); + Object value = def.getValue(obj, field); + Object parameter = serialize(value, field.typeAdapter); + stat.addParameter(parameter); + } + } + buff.append(" HAVING count(*)=0)"); + stat.setSQL(buff.toString()); + } + + @Override + public void appendLimitOffset(SQLStatement stat, long limit, long offset) { + if (limit > 0) { + stat.appendSQL(" LIMIT " + limit); + } + if (offset > 0) { + stat.appendSQL(" OFFSET " + offset); + } + } + + @Override + public void registerAdapter(DataTypeAdapter typeAdapter) { + typeAdapters.put((Class>) typeAdapter.getClass(), typeAdapter); + } + + @Override + public DataTypeAdapter getAdapter(Class> typeAdapter) { + DataTypeAdapter dta = typeAdapters.get(typeAdapter); + if (dta == null) { + dta = Utils.newObject(typeAdapter); + typeAdapters.put(typeAdapter, dta); + } + dta.setMode(mode); + return dta; + } + + @SuppressWarnings("unchecked") + @Override + public Object serialize(T value, Class> typeAdapter) { + if (typeAdapter == null) { + // pass-through + return value; + } + + DataTypeAdapter dta = (DataTypeAdapter) getAdapter(typeAdapter); + return dta.serialize(value); + } + + @Override + public Object deserialize(ResultSet rs, int columnIndex, Class targetType, Class> typeAdapter) { + Object value = null; + try { + if (typeAdapter == null) { + // standard object deserialization + Object o = rs.getObject(columnIndex); + if (o == null) { + // no-op + value = null; + } else if (Clob.class.isAssignableFrom(o.getClass())) { + value = Utils.convert(o, String.class); + } else if (Blob.class.isAssignableFrom(o.getClass())) { + value = Utils.convert(o, byte[].class); + } else { + value = Utils.convert(o, targetType); + } + } else { + // custom object deserialization with a DataTypeAdapter + DataTypeAdapter dta = getAdapter(typeAdapter); + Object object = rs.getObject(columnIndex); + value = dta.deserialize(object); + } + } catch (SQLException e) { + throw new IciqlException(e, "Can not convert the value at column {0} to {1}", + columnIndex, targetType.getName()); + } + return value; + } + + @Override + public String prepareStringParameter(Object o) { + if (o instanceof String) { + return LITERAL + o.toString().replace(LITERAL, "''") + LITERAL; + } else if (o instanceof Character) { + return LITERAL + o.toString() + LITERAL; + } else if (o instanceof java.sql.Time) { + return LITERAL + new SimpleDateFormat("HH:mm:ss").format(o) + LITERAL; + } else if (o instanceof java.sql.Date) { + return LITERAL + new SimpleDateFormat("yyyy-MM-dd").format(o) + LITERAL; + } else if (o instanceof java.util.Date) { + return LITERAL + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(o) + LITERAL; + } + return o.toString(); + } } diff --git a/src/main/java/com/iciql/SQLDialectDerby.java b/src/main/java/com/iciql/SQLDialectDerby.java index 99405b7..77fc73e 100644 --- a/src/main/java/com/iciql/SQLDialectDerby.java +++ b/src/main/java/com/iciql/SQLDialectDerby.java @@ -23,49 +23,49 @@ import com.iciql.util.StatementBuilder; */ public class SQLDialectDerby extends SQLDialectDefault { - @Override - public Class getDateTimeClass() { - return java.sql.Timestamp.class; - } + @Override + public Class getDateTimeClass() { + return java.sql.Timestamp.class; + } - @Override - public String convertSqlType(String sqlType) { - if ("TINYINT".equals(sqlType)) { - // Derby does not have a TINYINT/BYTE type - return "SMALLINT"; - } - return sqlType; - } + @Override + public String convertSqlType(String sqlType) { + if ("TINYINT".equals(sqlType)) { + // Derby does not have a TINYINT/BYTE type + return "SMALLINT"; + } + return sqlType; + } - @Override - public void appendLimitOffset(SQLStatement stat, long limit, long offset) { - // FETCH/OFFSET added in 10.5 - if (databaseMajorVersion >= 10 && databaseMinorVersion >= 5) { - if (offset > 0) { - stat.appendSQL(" OFFSET " + offset + (offset == 1 ? " ROW" : " ROWS")); - } - if (limit > 0) { - stat.appendSQL(" FETCH NEXT " + limit + (limit == 1 ? " ROW" : " ROWS") + " ONLY"); - } - } - } + @Override + public void appendLimitOffset(SQLStatement stat, long limit, long offset) { + // FETCH/OFFSET added in 10.5 + if (databaseMajorVersion >= 10 && databaseMinorVersion >= 5) { + if (offset > 0) { + stat.appendSQL(" OFFSET " + offset + (offset == 1 ? " ROW" : " ROWS")); + } + if (limit > 0) { + stat.appendSQL(" FETCH NEXT " + limit + (limit == 1 ? " ROW" : " ROWS") + " ONLY"); + } + } + } - @Override - protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, - boolean isAutoIncrement, boolean isPrimaryKey) { - String convertedType = convertSqlType(dataType); - buff.append(convertedType); - if (isIntegerType(dataType) && isAutoIncrement) { - buff.append(" GENERATED BY DEFAULT AS IDENTITY"); - } - return false; - } + @Override + protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, + boolean isAutoIncrement, boolean isPrimaryKey) { + String convertedType = convertSqlType(dataType); + buff.append(convertedType); + if (isIntegerType(dataType) && isAutoIncrement) { + buff.append(" GENERATED BY DEFAULT AS IDENTITY"); + } + return false; + } - @Override - public void prepareDropTable(SQLStatement stat, TableDefinition def) { - StatementBuilder buff = new StatementBuilder("DROP TABLE " - + prepareTableName(def.schemaName, def.tableName)); - stat.setSQL(buff.toString()); - return; - } + @Override + public void prepareDropTable(SQLStatement stat, TableDefinition def) { + StatementBuilder buff = new StatementBuilder("DROP TABLE " + + prepareTableName(def.schemaName, def.tableName)); + stat.setSQL(buff.toString()); + return; + } } \ No newline at end of file diff --git a/src/main/java/com/iciql/SQLDialectH2.java b/src/main/java/com/iciql/SQLDialectH2.java index 2d7d0fd..12cc161 100644 --- a/src/main/java/com/iciql/SQLDialectH2.java +++ b/src/main/java/com/iciql/SQLDialectH2.java @@ -25,112 +25,112 @@ import com.iciql.util.StatementBuilder; */ public class SQLDialectH2 extends SQLDialectDefault { - /** - * CACHED tables are created by default. MEMORY tables are created upon - * request. - */ - @Override - protected String prepareCreateTable(TableDefinition def) { - if (def.memoryTable) { - return "CREATE MEMORY TABLE IF NOT EXISTS"; - } else { - return "CREATE CACHED TABLE IF NOT EXISTS"; - } - } + /** + * CACHED tables are created by default. MEMORY tables are created upon + * request. + */ + @Override + protected String prepareCreateTable(TableDefinition def) { + if (def.memoryTable) { + return "CREATE MEMORY TABLE IF NOT EXISTS"; + } else { + return "CREATE CACHED TABLE IF NOT EXISTS"; + } + } - @Override - protected String prepareCreateView(TableDefinition def) { - return "CREATE VIEW IF NOT EXISTS"; - } + @Override + protected String prepareCreateView(TableDefinition def) { + return "CREATE VIEW IF NOT EXISTS"; + } - @Override - public void prepareDropView(SQLStatement stat, TableDefinition def) { - StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS " - + prepareTableName(def.schemaName, def.tableName)); - stat.setSQL(buff.toString()); - return; - } + @Override + public void prepareDropView(SQLStatement stat, TableDefinition def) { + StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS " + + prepareTableName(def.schemaName, def.tableName)); + stat.setSQL(buff.toString()); + return; + } - @Override - protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, - boolean isAutoIncrement, boolean isPrimaryKey) { - String convertedType = convertSqlType(dataType); - boolean isIdentity = false; - if (isIntegerType(dataType)) { - if (isAutoIncrement && isPrimaryKey) { - buff.append("IDENTITY"); - isIdentity = true; - } else if (isAutoIncrement) { - buff.append(convertedType); - buff.append(" AUTO_INCREMENT"); - } else { - buff.append(convertedType); - } - } else { - buff.append(convertedType); - } - return isIdentity; - } + @Override + protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, + boolean isAutoIncrement, boolean isPrimaryKey) { + String convertedType = convertSqlType(dataType); + boolean isIdentity = false; + if (isIntegerType(dataType)) { + if (isAutoIncrement && isPrimaryKey) { + buff.append("IDENTITY"); + isIdentity = true; + } else if (isAutoIncrement) { + buff.append(convertedType); + buff.append(" AUTO_INCREMENT"); + } else { + buff.append(convertedType); + } + } else { + buff.append(convertedType); + } + return isIdentity; + } - @Override - public void prepareCreateIndex(SQLStatement stat, String schema, String table, IndexDefinition index) { - StatementBuilder buff = new StatementBuilder(); - buff.append("CREATE "); - switch (index.type) { - case STANDARD: - break; - case UNIQUE: - buff.append("UNIQUE "); - break; - case HASH: - buff.append("HASH "); - break; - case UNIQUE_HASH: - buff.append("UNIQUE HASH "); - break; - } - buff.append("INDEX IF NOT EXISTS "); - buff.append(index.indexName); - buff.append(" ON "); - buff.append(table); - buff.append("("); - for (String col : index.columnNames) { - buff.appendExceptFirst(", "); - buff.append(col); - } - buff.append(")"); - stat.setSQL(buff.toString()); - } + @Override + public void prepareCreateIndex(SQLStatement stat, String schema, String table, IndexDefinition index) { + StatementBuilder buff = new StatementBuilder(); + buff.append("CREATE "); + switch (index.type) { + case STANDARD: + break; + case UNIQUE: + buff.append("UNIQUE "); + break; + case HASH: + buff.append("HASH "); + break; + case UNIQUE_HASH: + buff.append("UNIQUE HASH "); + break; + } + buff.append("INDEX IF NOT EXISTS "); + buff.append(index.indexName); + buff.append(" ON "); + buff.append(table); + buff.append("("); + for (String col : index.columnNames) { + buff.appendExceptFirst(", "); + buff.append(col); + } + buff.append(")"); + stat.setSQL(buff.toString()); + } - @Override - public void prepareMerge(SQLStatement stat, String schemaName, String tableName, - TableDefinition def, Object obj) { - StatementBuilder buff = new StatementBuilder("MERGE INTO "); - buff.append(prepareTableName(schemaName, tableName)).append(" ("); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(field.columnName); - } - buff.append(") KEY("); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - if (field.isPrimaryKey) { - buff.appendExceptFirst(", "); - buff.append(field.columnName); - } - } - buff.append(") "); - buff.resetCount(); - buff.append("VALUES ("); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append('?'); - Object value = def.getValue(obj, field); - Object parameter = serialize(value, field.typeAdapter); - stat.addParameter(parameter); - } - buff.append(')'); - stat.setSQL(buff.toString()); - } + @Override + public void prepareMerge(SQLStatement stat, String schemaName, String tableName, + TableDefinition def, Object obj) { + StatementBuilder buff = new StatementBuilder("MERGE INTO "); + buff.append(prepareTableName(schemaName, tableName)).append(" ("); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(field.columnName); + } + buff.append(") KEY("); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + if (field.isPrimaryKey) { + buff.appendExceptFirst(", "); + buff.append(field.columnName); + } + } + buff.append(") "); + buff.resetCount(); + buff.append("VALUES ("); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append('?'); + Object value = def.getValue(obj, field); + Object parameter = serialize(value, field.typeAdapter); + stat.addParameter(parameter); + } + buff.append(')'); + stat.setSQL(buff.toString()); + } } \ No newline at end of file diff --git a/src/main/java/com/iciql/SQLDialectHSQL.java b/src/main/java/com/iciql/SQLDialectHSQL.java index 8b05ca4..74a3dcc 100644 --- a/src/main/java/com/iciql/SQLDialectHSQL.java +++ b/src/main/java/com/iciql/SQLDialectHSQL.java @@ -16,135 +16,135 @@ package com.iciql; -import java.text.MessageFormat; - import com.iciql.TableDefinition.FieldDefinition; import com.iciql.util.StatementBuilder; +import java.text.MessageFormat; + /** * HyperSQL database dialect. */ public class SQLDialectHSQL extends SQLDialectDefault { - /** - * CACHED tables are created by default. MEMORY tables are created upon - * request. - */ - @Override - protected String prepareCreateTable(TableDefinition def) { - if (def.memoryTable) { - return "CREATE MEMORY TABLE IF NOT EXISTS"; - } else { - return "CREATE CACHED TABLE IF NOT EXISTS"; - } - } + /** + * CACHED tables are created by default. MEMORY tables are created upon + * request. + */ + @Override + protected String prepareCreateTable(TableDefinition def) { + if (def.memoryTable) { + return "CREATE MEMORY TABLE IF NOT EXISTS"; + } else { + return "CREATE CACHED TABLE IF NOT EXISTS"; + } + } - @Override - public void prepareDropView(SQLStatement stat, TableDefinition def) { - StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS " - + prepareTableName(def.schemaName, def.tableName)); - stat.setSQL(buff.toString()); - return; - } + @Override + public void prepareDropView(SQLStatement stat, TableDefinition def) { + StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS " + + prepareTableName(def.schemaName, def.tableName)); + stat.setSQL(buff.toString()); + return; + } - @Override - protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, - boolean isAutoIncrement, boolean isPrimaryKey) { - boolean isIdentity = false; - String convertedType = convertSqlType(dataType); - buff.append(convertedType); - if (isIntegerType(dataType) && isAutoIncrement && isPrimaryKey) { - buff.append(" IDENTITY"); - isIdentity = true; - } - return isIdentity; - } + @Override + protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, + boolean isAutoIncrement, boolean isPrimaryKey) { + boolean isIdentity = false; + String convertedType = convertSqlType(dataType); + buff.append(convertedType); + if (isIntegerType(dataType) && isAutoIncrement && isPrimaryKey) { + buff.append(" IDENTITY"); + isIdentity = true; + } + return isIdentity; + } - @Override - public void prepareMerge(SQLStatement stat, String schemaName, String tableName, - TableDefinition def, Object obj) { - final String valuePrefix = "v"; - StatementBuilder buff = new StatementBuilder("MERGE INTO "); - buff.append(prepareTableName(schemaName, tableName)); - // a, b, c.... - buff.append(" USING (VALUES("); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append("CAST(? AS "); - String dataType = convertSqlType(field.dataType); - buff.append(dataType); - if ("VARCHAR".equals(dataType)) { - if (field.length > 0) { - // VARCHAR(x) - buff.append(MessageFormat.format("({0})", field.length)); - } - } else if ("DECIMAL".equals(dataType)) { - if (field.length > 0) { - if (field.scale > 0) { - // DECIMAL(x,y) - buff.append(MessageFormat.format("({0},{1})", field.length, field.scale)); - } else { - // DECIMAL(x) - buff.append(MessageFormat.format("({0})", field.length)); - } - } - } - buff.append(')'); - Object value = def.getValue(obj, field); - Object parameter = serialize(value, field.typeAdapter); - stat.addParameter(parameter); - } + @Override + public void prepareMerge(SQLStatement stat, String schemaName, String tableName, + TableDefinition def, Object obj) { + final String valuePrefix = "v"; + StatementBuilder buff = new StatementBuilder("MERGE INTO "); + buff.append(prepareTableName(schemaName, tableName)); + // a, b, c.... + buff.append(" USING (VALUES("); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append("CAST(? AS "); + String dataType = convertSqlType(field.dataType); + buff.append(dataType); + if ("VARCHAR".equals(dataType)) { + if (field.length > 0) { + // VARCHAR(x) + buff.append(MessageFormat.format("({0})", field.length)); + } + } else if ("DECIMAL".equals(dataType)) { + if (field.length > 0) { + if (field.scale > 0) { + // DECIMAL(x,y) + buff.append(MessageFormat.format("({0},{1})", field.length, field.scale)); + } else { + // DECIMAL(x) + buff.append(MessageFormat.format("({0})", field.length)); + } + } + } + buff.append(')'); + Object value = def.getValue(obj, field); + Object parameter = serialize(value, field.typeAdapter); + stat.addParameter(parameter); + } - // map to temporary table - buff.resetCount(); - buff.append(")) AS vals ("); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(valuePrefix + field.columnName)); - } + // map to temporary table + buff.resetCount(); + buff.append(")) AS vals ("); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(valuePrefix + field.columnName)); + } - buff.append(") ON "); + buff.append(") ON "); - // create the ON condition - // (va, vb) = (va,vb) - String[] prefixes = { "", valuePrefix }; - for (int i = 0; i < prefixes.length; i++) { - String prefix = prefixes[i]; - buff.resetCount(); - buff.append('('); - for (FieldDefinition field : def.fields) { - if (field.isPrimaryKey) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(prefix + field.columnName)); - } - } - buff.append(")"); - if (i == 0) { - buff.append('='); - } - } + // create the ON condition + // (va, vb) = (va,vb) + String[] prefixes = {"", valuePrefix}; + for (int i = 0; i < prefixes.length; i++) { + String prefix = prefixes[i]; + buff.resetCount(); + buff.append('('); + for (FieldDefinition field : def.fields) { + if (field.isPrimaryKey) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(prefix + field.columnName)); + } + } + buff.append(")"); + if (i == 0) { + buff.append('='); + } + } - // UPDATE - // set a=va - buff.append(" WHEN MATCHED THEN UPDATE SET "); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(field.columnName)); - buff.append('='); - buff.append(prepareColumnName(valuePrefix + field.columnName)); - } + // UPDATE + // set a=va + buff.append(" WHEN MATCHED THEN UPDATE SET "); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(field.columnName)); + buff.append('='); + buff.append(prepareColumnName(valuePrefix + field.columnName)); + } - // INSERT - // insert va, vb, vc.... - buff.append(" WHEN NOT MATCHED THEN INSERT "); - buff.resetCount(); - buff.append(" VALUES ("); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(valuePrefix + field.columnName)); - } - buff.append(')'); - stat.setSQL(buff.toString()); - } + // INSERT + // insert va, vb, vc.... + buff.append(" WHEN NOT MATCHED THEN INSERT "); + buff.resetCount(); + buff.append(" VALUES ("); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(valuePrefix + field.columnName)); + } + buff.append(')'); + stat.setSQL(buff.toString()); + } } \ No newline at end of file diff --git a/src/main/java/com/iciql/SQLDialectMSSQL.java b/src/main/java/com/iciql/SQLDialectMSSQL.java index e591d4a..7f765fa 100644 --- a/src/main/java/com/iciql/SQLDialectMSSQL.java +++ b/src/main/java/com/iciql/SQLDialectMSSQL.java @@ -22,37 +22,37 @@ package com.iciql; */ public class SQLDialectMSSQL extends SQLDialectDefault { - @Override - public String extractColumnName(String name) { - return super.extractColumnName(name).replace('[', ' ').replace(']', ' ').trim(); - } - - /** - * Append limit and offset rows - * - * @param stat Statement - * @param limit Limit rows - * @param offset Offset rows - */ - @Override - public void appendLimitOffset(SQLStatement stat, long limit, long offset) { - if (offset > 0) { - throw new IciqlException("iciql does not support offset for MSSQL dialect!"); - } - StringBuilder query = new StringBuilder(stat.getSQL()); - - // for databaseVersion >= 2012 need Offset - if (limit > 0) { - int indexSelect = query.indexOf("SELECT"); - - if (indexSelect >= 0) { - StringBuilder subPathQuery = new StringBuilder(" TOP "); - subPathQuery.append(Long.toString(limit)); - - query.insert(indexSelect + "SELECT".length(), subPathQuery); - - stat.setSQL(query.toString()); - } + @Override + public String extractColumnName(String name) { + return super.extractColumnName(name).replace('[', ' ').replace(']', ' ').trim(); + } + + /** + * Append limit and offset rows + * + * @param stat Statement + * @param limit Limit rows + * @param offset Offset rows + */ + @Override + public void appendLimitOffset(SQLStatement stat, long limit, long offset) { + if (offset > 0) { + throw new IciqlException("iciql does not support offset for MSSQL dialect!"); + } + StringBuilder query = new StringBuilder(stat.getSQL()); + + // for databaseVersion >= 2012 need Offset + if (limit > 0) { + int indexSelect = query.indexOf("SELECT"); + + if (indexSelect >= 0) { + StringBuilder subPathQuery = new StringBuilder(" TOP "); + subPathQuery.append(Long.toString(limit)); + + query.insert(indexSelect + "SELECT".length(), subPathQuery); + + stat.setSQL(query.toString()); + } + } } - } } diff --git a/src/main/java/com/iciql/SQLDialectMySQL.java b/src/main/java/com/iciql/SQLDialectMySQL.java index ec5923f..e78b8a0 100644 --- a/src/main/java/com/iciql/SQLDialectMySQL.java +++ b/src/main/java/com/iciql/SQLDialectMySQL.java @@ -24,71 +24,71 @@ import com.iciql.util.StatementBuilder; */ public class SQLDialectMySQL extends SQLDialectDefault { - @Override - public String convertSqlType(String sqlType) { - if (sqlType.equals("CLOB")) { - return "TEXT"; - } - return sqlType; - } + @Override + public String convertSqlType(String sqlType) { + if (sqlType.equals("CLOB")) { + return "TEXT"; + } + return sqlType; + } - @Override - protected String prepareCreateTable(TableDefinition def) { - return "CREATE TABLE IF NOT EXISTS"; - } + @Override + protected String prepareCreateTable(TableDefinition def) { + return "CREATE TABLE IF NOT EXISTS"; + } - @Override - public void prepareDropView(SQLStatement stat, TableDefinition def) { - StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS " - + prepareTableName(def.schemaName, def.tableName)); - stat.setSQL(buff.toString()); - return; - } + @Override + public void prepareDropView(SQLStatement stat, TableDefinition def) { + StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS " + + prepareTableName(def.schemaName, def.tableName)); + stat.setSQL(buff.toString()); + return; + } - @Override - public String prepareColumnName(String name) { - return "`" + name + "`"; - } + @Override + public String prepareColumnName(String name) { + return "`" + name + "`"; + } - @Override - protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, boolean isAutoIncrement, - boolean isPrimaryKey) { - String convertedType = convertSqlType(dataType); - buff.append(convertedType); - if (isIntegerType(dataType) && isAutoIncrement) { - buff.append(" AUTO_INCREMENT"); - } - return false; - } + @Override + protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, boolean isAutoIncrement, + boolean isPrimaryKey) { + String convertedType = convertSqlType(dataType); + buff.append(convertedType); + if (isIntegerType(dataType) && isAutoIncrement) { + buff.append(" AUTO_INCREMENT"); + } + return false; + } - @Override - public void prepareMerge(SQLStatement stat, String schemaName, String tableName, - TableDefinition def, Object obj) { - StatementBuilder buff = new StatementBuilder("INSERT INTO "); - buff.append(prepareTableName(schemaName, tableName)).append(" ("); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(field.columnName); - } - buff.resetCount(); - buff.append(") VALUES ("); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append('?'); - Object value = def.getValue(obj, field); - Object parameter = serialize(value, field.typeAdapter); - stat.addParameter(parameter); - } - buff.append(") ON DUPLICATE KEY UPDATE "); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(field.columnName); - buff.append("=VALUES("); - buff.append(field.columnName); - buff.append(')'); - } - stat.setSQL(buff.toString()); - } + @Override + public void prepareMerge(SQLStatement stat, String schemaName, String tableName, + TableDefinition def, Object obj) { + StatementBuilder buff = new StatementBuilder("INSERT INTO "); + buff.append(prepareTableName(schemaName, tableName)).append(" ("); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(field.columnName); + } + buff.resetCount(); + buff.append(") VALUES ("); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append('?'); + Object value = def.getValue(obj, field); + Object parameter = serialize(value, field.typeAdapter); + stat.addParameter(parameter); + } + buff.append(") ON DUPLICATE KEY UPDATE "); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(field.columnName); + buff.append("=VALUES("); + buff.append(field.columnName); + buff.append(')'); + } + stat.setSQL(buff.toString()); + } } \ No newline at end of file diff --git a/src/main/java/com/iciql/SQLDialectPostgreSQL.java b/src/main/java/com/iciql/SQLDialectPostgreSQL.java index 54e47b0..97b92f8 100644 --- a/src/main/java/com/iciql/SQLDialectPostgreSQL.java +++ b/src/main/java/com/iciql/SQLDialectPostgreSQL.java @@ -25,137 +25,137 @@ import com.iciql.util.StatementBuilder; */ public class SQLDialectPostgreSQL extends SQLDialectDefault { - @Override - public Class getDateTimeClass() { - return java.sql.Timestamp.class; - } - - @Override - public String convertSqlType(String sqlType) { - if ("DOUBLE".equals(sqlType)) { - return "DOUBLE PRECISION"; - } else if ("TINYINT".equals(sqlType)) { - // PostgreSQL does not have a byte type - return "SMALLINT"; - } else if ("CLOB".equals(sqlType)) { - return "TEXT"; - } else if ("BLOB".equals(sqlType)) { - return "BYTEA"; - } - return sqlType; - } - - @Override - protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, - boolean isAutoIncrement, boolean isPrimaryKey) { - String convertedType = convertSqlType(dataType); - if (isIntegerType(dataType)) { - if (isAutoIncrement) { - if ("BIGINT".equals(dataType)) { - buff.append("BIGSERIAL"); - } else { - buff.append("SERIAL"); - } - } else { - buff.append(convertedType); - } - } else { - buff.append(convertedType); - } - return false; - } - - @Override - public void prepareCreateIndex(SQLStatement stat, String schemaName, String tableName, - IndexDefinition index) { - StatementBuilder buff = new StatementBuilder(); - buff.append("CREATE "); - switch (index.type) { - case UNIQUE: - buff.append("UNIQUE "); - break; - case UNIQUE_HASH: - buff.append("UNIQUE "); - break; - } - buff.append("INDEX "); - buff.append(index.indexName); - buff.append(" ON "); - buff.append(tableName); - - switch (index.type) { - case HASH: - buff.append(" USING HASH"); - break; - case UNIQUE_HASH: - buff.append(" USING HASH"); - break; - } - - buff.append(" ("); - for (String col : index.columnNames) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(col)); - } - buff.append(") "); - - stat.setSQL(buff.toString().trim()); - } - - @Override - public void prepareMerge(SQLStatement stat, String schemaName, String tableName, - TableDefinition def, Object obj) { - - FieldDefinition primaryKey = null; - for (FieldDefinition field : def.fields) { - if (field.isPrimaryKey) { - primaryKey = field; - } - } - - if (primaryKey == null || databaseVersion < 9.5f) { - // simulated UPSERT for <= 9.4 release - super.prepareMerge(stat, schemaName, tableName, def, obj); - return; - } - - // official UPSERT added in 9.5 release - StatementBuilder buff = new StatementBuilder("INSERT INTO "); - buff.append(prepareTableName(schemaName, tableName)).append(" ("); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(field.columnName); - } - buff.resetCount(); - buff.append(") VALUES ("); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append('?'); - Object value = def.getValue(obj, field); - Object parameter = serialize(value, field.typeAdapter); - stat.addParameter(parameter); - } - - buff.append(") ON CONFLICT ("); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - if (field.isPrimaryKey) { - buff.appendExceptFirst(", "); - buff.append(field.columnName); - } - } - buff.append(") DO UPDATE SET "); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(field.columnName); - buff.append("=?"); - Object value = def.getValue(obj, field); - Object parameter = serialize(value, field.typeAdapter); - stat.addParameter(parameter); - } - stat.setSQL(buff.toString()); - } + @Override + public Class getDateTimeClass() { + return java.sql.Timestamp.class; + } + + @Override + public String convertSqlType(String sqlType) { + if ("DOUBLE".equals(sqlType)) { + return "DOUBLE PRECISION"; + } else if ("TINYINT".equals(sqlType)) { + // PostgreSQL does not have a byte type + return "SMALLINT"; + } else if ("CLOB".equals(sqlType)) { + return "TEXT"; + } else if ("BLOB".equals(sqlType)) { + return "BYTEA"; + } + return sqlType; + } + + @Override + protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, + boolean isAutoIncrement, boolean isPrimaryKey) { + String convertedType = convertSqlType(dataType); + if (isIntegerType(dataType)) { + if (isAutoIncrement) { + if ("BIGINT".equals(dataType)) { + buff.append("BIGSERIAL"); + } else { + buff.append("SERIAL"); + } + } else { + buff.append(convertedType); + } + } else { + buff.append(convertedType); + } + return false; + } + + @Override + public void prepareCreateIndex(SQLStatement stat, String schemaName, String tableName, + IndexDefinition index) { + StatementBuilder buff = new StatementBuilder(); + buff.append("CREATE "); + switch (index.type) { + case UNIQUE: + buff.append("UNIQUE "); + break; + case UNIQUE_HASH: + buff.append("UNIQUE "); + break; + } + buff.append("INDEX "); + buff.append(index.indexName); + buff.append(" ON "); + buff.append(tableName); + + switch (index.type) { + case HASH: + buff.append(" USING HASH"); + break; + case UNIQUE_HASH: + buff.append(" USING HASH"); + break; + } + + buff.append(" ("); + for (String col : index.columnNames) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(col)); + } + buff.append(") "); + + stat.setSQL(buff.toString().trim()); + } + + @Override + public void prepareMerge(SQLStatement stat, String schemaName, String tableName, + TableDefinition def, Object obj) { + + FieldDefinition primaryKey = null; + for (FieldDefinition field : def.fields) { + if (field.isPrimaryKey) { + primaryKey = field; + } + } + + if (primaryKey == null || databaseVersion < 9.5f) { + // simulated UPSERT for <= 9.4 release + super.prepareMerge(stat, schemaName, tableName, def, obj); + return; + } + + // official UPSERT added in 9.5 release + StatementBuilder buff = new StatementBuilder("INSERT INTO "); + buff.append(prepareTableName(schemaName, tableName)).append(" ("); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(field.columnName); + } + buff.resetCount(); + buff.append(") VALUES ("); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append('?'); + Object value = def.getValue(obj, field); + Object parameter = serialize(value, field.typeAdapter); + stat.addParameter(parameter); + } + + buff.append(") ON CONFLICT ("); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + if (field.isPrimaryKey) { + buff.appendExceptFirst(", "); + buff.append(field.columnName); + } + } + buff.append(") DO UPDATE SET "); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(field.columnName); + buff.append("=?"); + Object value = def.getValue(obj, field); + Object parameter = serialize(value, field.typeAdapter); + stat.addParameter(parameter); + } + stat.setSQL(buff.toString()); + } } \ No newline at end of file diff --git a/src/main/java/com/iciql/SQLDialectSQLite.java b/src/main/java/com/iciql/SQLDialectSQLite.java index 436af54..b99bf96 100644 --- a/src/main/java/com/iciql/SQLDialectSQLite.java +++ b/src/main/java/com/iciql/SQLDialectSQLite.java @@ -16,165 +16,165 @@ package com.iciql; -import java.sql.Date; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Time; -import java.sql.Timestamp; - import com.iciql.Iciql.DataTypeAdapter; import com.iciql.TableDefinition.FieldDefinition; import com.iciql.TableDefinition.IndexDefinition; import com.iciql.util.IciqlLogger; import com.iciql.util.StatementBuilder; +import java.sql.Date; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; + /** * SQLite database dialect. */ public class SQLDialectSQLite extends SQLDialectDefault { - @Override - public boolean supportsSavePoints() { - // SAVEPOINT support was added after the 3.8.7 release - String [] chunks = productVersion.split("\\."); - if (Integer.parseInt(chunks[0]) > 3) { - return true; - } - float f = Float.parseFloat(chunks[1] + "." + chunks[2]); - return (f > 8.7); - } - - @Override - protected String prepareCreateTable(TableDefinition def) { - return "CREATE TABLE IF NOT EXISTS"; - } - - @Override - protected String prepareCreateView(TableDefinition def) { - return "CREATE VIEW IF NOT EXISTS"; - } - - @Override - public String convertSqlType(String sqlType) { - if (isIntegerType(sqlType)) { - return "INTEGER"; - } - return sqlType; - } - - @Override - protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, - boolean isAutoIncrement, boolean isPrimaryKey) { - String convertedType = convertSqlType(dataType); - buff.append(convertedType); - if (isPrimaryKey) { - buff.append(" PRIMARY KEY"); - if (isAutoIncrement) { - buff.append(" AUTOINCREMENT"); - } - return true; - } - return false; - } - - @Override - public void prepareDropView(SQLStatement stat, TableDefinition def) { - StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS " - + prepareTableName(def.schemaName, def.tableName)); - stat.setSQL(buff.toString()); - return; - } - - @Override - public void prepareCreateIndex(SQLStatement stat, String schemaName, String tableName, - IndexDefinition index) { - StatementBuilder buff = new StatementBuilder(); - buff.append("CREATE "); - switch (index.type) { - case UNIQUE: - buff.append("UNIQUE "); - break; - case UNIQUE_HASH: - buff.append("UNIQUE "); - break; - default: - IciqlLogger.warn("{0} does not support hash indexes", getClass().getSimpleName()); - } - buff.append("INDEX IF NOT EXISTS "); - buff.append(index.indexName); - buff.append(" ON "); - buff.append(tableName); - buff.append("("); - for (String col : index.columnNames) { - buff.appendExceptFirst(", "); - buff.append(prepareColumnName(col)); - } - buff.append(") "); - - stat.setSQL(buff.toString().trim()); - } - - @Override - public void prepareMerge(SQLStatement stat, String schemaName, String tableName, - TableDefinition def, Object obj) { - StatementBuilder buff = new StatementBuilder("INSERT OR REPLACE INTO "); - buff.append(prepareTableName(schemaName, tableName)).append(" ("); - buff.resetCount(); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append(field.columnName); - } - buff.append(") "); - buff.resetCount(); - buff.append("VALUES ("); - for (FieldDefinition field : def.fields) { - buff.appendExceptFirst(", "); - buff.append('?'); - Object value = def.getValue(obj, field); - Object parameter = serialize(value, field.typeAdapter); - stat.addParameter(parameter); - } - buff.append(')'); - stat.setSQL(buff.toString()); - } - - @Override - public Object deserialize(ResultSet rs, int columnIndex, Class targetType, Class> typeAdapter) { - try { - return super.deserialize(rs, columnIndex, targetType, typeAdapter); - } catch (IciqlException e) { - if (typeAdapter == null && e.getMessage().startsWith("Can not convert")) { - try { - // give the SQLite JDBC driver an opportunity to deserialize DateTime objects - if (Timestamp.class.equals(targetType)) { - return rs.getTimestamp(columnIndex); - } else if (Time.class.equals(targetType)) { - return rs.getTime(columnIndex); - } else if (Date.class.equals(targetType)) { - return rs.getDate(columnIndex); - } else if (java.util.Date.class.equals(targetType)) { - Timestamp timestamp = rs.getTimestamp(columnIndex); - return new java.util.Date(timestamp.getTime()); - } - } catch (SQLException x) { - throw new IciqlException(x, "Can not convert the value at column {0} to {1}", - columnIndex, targetType.getName()); - } - } - - // rethrow e - throw e; - } - } - - @Override - public String prepareStringParameter(Object o) { - if (o instanceof Boolean) { - // SQLite does not have an explicit BOOLEAN type - Boolean bool = (Boolean) o; - return bool ? "1" : "0"; - } - return super.prepareStringParameter(o); - } -} + @Override + public boolean supportsSavePoints() { + // SAVEPOINT support was added after the 3.8.7 release + String[] chunks = productVersion.split("\\."); + if (Integer.parseInt(chunks[0]) > 3) { + return true; + } + float f = Float.parseFloat(chunks[1] + "." + chunks[2]); + return (f > 8.7); + } + + @Override + protected String prepareCreateTable(TableDefinition def) { + return "CREATE TABLE IF NOT EXISTS"; + } + + @Override + protected String prepareCreateView(TableDefinition def) { + return "CREATE VIEW IF NOT EXISTS"; + } + + @Override + public String convertSqlType(String sqlType) { + if (isIntegerType(sqlType)) { + return "INTEGER"; + } + return sqlType; + } + + @Override + protected boolean prepareColumnDefinition(StatementBuilder buff, String dataType, + boolean isAutoIncrement, boolean isPrimaryKey) { + String convertedType = convertSqlType(dataType); + buff.append(convertedType); + if (isPrimaryKey) { + buff.append(" PRIMARY KEY"); + if (isAutoIncrement) { + buff.append(" AUTOINCREMENT"); + } + return true; + } + return false; + } + + @Override + public void prepareDropView(SQLStatement stat, TableDefinition def) { + StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS " + + prepareTableName(def.schemaName, def.tableName)); + stat.setSQL(buff.toString()); + return; + } + + @Override + public void prepareCreateIndex(SQLStatement stat, String schemaName, String tableName, + IndexDefinition index) { + StatementBuilder buff = new StatementBuilder(); + buff.append("CREATE "); + switch (index.type) { + case UNIQUE: + buff.append("UNIQUE "); + break; + case UNIQUE_HASH: + buff.append("UNIQUE "); + break; + default: + IciqlLogger.warn("{0} does not support hash indexes", getClass().getSimpleName()); + } + buff.append("INDEX IF NOT EXISTS "); + buff.append(index.indexName); + buff.append(" ON "); + buff.append(tableName); + buff.append("("); + for (String col : index.columnNames) { + buff.appendExceptFirst(", "); + buff.append(prepareColumnName(col)); + } + buff.append(") "); + + stat.setSQL(buff.toString().trim()); + } + + @Override + public void prepareMerge(SQLStatement stat, String schemaName, String tableName, + TableDefinition def, Object obj) { + StatementBuilder buff = new StatementBuilder("INSERT OR REPLACE INTO "); + buff.append(prepareTableName(schemaName, tableName)).append(" ("); + buff.resetCount(); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append(field.columnName); + } + buff.append(") "); + buff.resetCount(); + buff.append("VALUES ("); + for (FieldDefinition field : def.fields) { + buff.appendExceptFirst(", "); + buff.append('?'); + Object value = def.getValue(obj, field); + Object parameter = serialize(value, field.typeAdapter); + stat.addParameter(parameter); + } + buff.append(')'); + stat.setSQL(buff.toString()); + } + + @Override + public Object deserialize(ResultSet rs, int columnIndex, Class targetType, Class> typeAdapter) { + try { + return super.deserialize(rs, columnIndex, targetType, typeAdapter); + } catch (IciqlException e) { + if (typeAdapter == null && e.getMessage().startsWith("Can not convert")) { + try { + // give the SQLite JDBC driver an opportunity to deserialize DateTime objects + if (Timestamp.class.equals(targetType)) { + return rs.getTimestamp(columnIndex); + } else if (Time.class.equals(targetType)) { + return rs.getTime(columnIndex); + } else if (Date.class.equals(targetType)) { + return rs.getDate(columnIndex); + } else if (java.util.Date.class.equals(targetType)) { + Timestamp timestamp = rs.getTimestamp(columnIndex); + return new java.util.Date(timestamp.getTime()); + } + } catch (SQLException x) { + throw new IciqlException(x, "Can not convert the value at column {0} to {1}", + columnIndex, targetType.getName()); + } + } + + // rethrow e + throw e; + } + } + + @Override + public String prepareStringParameter(Object o) { + if (o instanceof Boolean) { + // SQLite does not have an explicit BOOLEAN type + Boolean bool = (Boolean) o; + return bool ? "1" : "0"; + } + return super.prepareStringParameter(o); + } +} diff --git a/src/main/java/com/iciql/SQLStatement.java b/src/main/java/com/iciql/SQLStatement.java index 7eb0b04..d49e722 100644 --- a/src/main/java/com/iciql/SQLStatement.java +++ b/src/main/java/com/iciql/SQLStatement.java @@ -17,174 +17,174 @@ package com.iciql; +import com.iciql.util.JdbcUtils; + import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.StringTokenizer; -import com.iciql.util.JdbcUtils; - /** * This class represents a parameterized SQL statement. */ public class SQLStatement { - private Db db; - private StringBuilder buff = new StringBuilder(); - private String sql; - private ArrayList params = new ArrayList(); - - SQLStatement(Db db) { - this.db = db; - } - - public void setSQL(String sql) { - this.sql = sql; - buff = new StringBuilder(sql); - } - - public SQLStatement appendSQL(String s) { - buff.append(s); - sql = null; - return this; - } - - public SQLStatement appendTable(String schema, String table) { - return appendSQL(db.getDialect().prepareTableName(schema, table)); - } - - public SQLStatement appendColumn(String column) { - return appendSQL(db.getDialect().prepareColumnName(column)); - } - - /** - * getSQL returns a simple string representation of the parameterized - * statement which will be used later, internally, with prepareStatement. - * - * @return a simple sql statement - */ - String getSQL() { - if (sql == null) { - sql = buff.toString(); - } - return sql; - } - - /** - * toSQL creates a static sql statement with the referenced parameters - * encoded in the statement. - * - * @return a complete sql statement - */ - String toSQL() { - if (sql == null) { - sql = buff.toString(); - } - if (params.size() == 0) { - return sql; - } - StringBuilder sb = new StringBuilder(); - // TODO this needs to me more sophisticated - StringTokenizer st = new StringTokenizer(sql, "?", false); - int i = 0; - while (st.hasMoreTokens()) { - sb.append(st.nextToken()); - if (i < params.size()) { - Object o = params.get(i); - if (RuntimeParameter.PARAMETER == o) { - // dynamic parameter - sb.append('?'); - } else { - // static parameter - sb.append(db.getDialect().prepareStringParameter(o)); - } - i++; - } - } - return sb.toString(); - } - - public SQLStatement addParameter(Object o) { - // Automatically convert java.util.Date to java.sql.Timestamp - // if the dialect requires java.sql.Timestamp objects (e.g. Derby) - if (o != null && o.getClass().equals(java.util.Date.class) - && db.getDialect().getDateTimeClass().equals(java.sql.Timestamp.class)) { - o = new java.sql.Timestamp(((java.util.Date) o).getTime()); - } - params.add(o); - return this; - } - - void execute() { - PreparedStatement ps = null; - try { - ps = prepare(false); - ps.execute(); - } catch (SQLException e) { - throw IciqlException.fromSQL(getSQL(), e); - } finally { - JdbcUtils.closeSilently(ps); - } - } - - ResultSet executeQuery() { - try { - return prepare(false).executeQuery(); - } catch (SQLException e) { - throw IciqlException.fromSQL(getSQL(), e); - } - } - - int executeUpdate() { - PreparedStatement ps = null; - try { - ps = prepare(false); - return ps.executeUpdate(); - } catch (SQLException e) { - throw IciqlException.fromSQL(getSQL(), e); - } finally { - JdbcUtils.closeSilently(ps); - } - } - - long executeInsert() { - PreparedStatement ps = null; - try { - ps = prepare(true); - ps.executeUpdate(); - long identity = -1; - ResultSet rs = ps.getGeneratedKeys(); - if (rs != null && rs.next()) { - identity = rs.getLong(1); - } - JdbcUtils.closeSilently(rs); - return identity; - } catch (SQLException e) { - throw IciqlException.fromSQL(getSQL(), e); - } finally { - JdbcUtils.closeSilently(ps); - } - } - - private void setValue(PreparedStatement prep, int parameterIndex, Object x) { - try { - prep.setObject(parameterIndex, x); - } catch (SQLException e) { - IciqlException ix = new IciqlException(e, "error setting parameter {0} as {1}", parameterIndex, x - .getClass().getSimpleName()); - ix.setSQL(getSQL()); - throw ix; - } - } - - PreparedStatement prepare(boolean returnGeneratedKeys) { - PreparedStatement prep = db.prepare(getSQL(), returnGeneratedKeys); - for (int i = 0; i < params.size(); i++) { - Object o = params.get(i); - setValue(prep, i + 1, o); - } - return prep; - } + private Db db; + private StringBuilder buff = new StringBuilder(); + private String sql; + private ArrayList params = new ArrayList(); + + SQLStatement(Db db) { + this.db = db; + } + + public void setSQL(String sql) { + this.sql = sql; + buff = new StringBuilder(sql); + } + + public SQLStatement appendSQL(String s) { + buff.append(s); + sql = null; + return this; + } + + public SQLStatement appendTable(String schema, String table) { + return appendSQL(db.getDialect().prepareTableName(schema, table)); + } + + public SQLStatement appendColumn(String column) { + return appendSQL(db.getDialect().prepareColumnName(column)); + } + + /** + * getSQL returns a simple string representation of the parameterized + * statement which will be used later, internally, with prepareStatement. + * + * @return a simple sql statement + */ + String getSQL() { + if (sql == null) { + sql = buff.toString(); + } + return sql; + } + + /** + * toSQL creates a static sql statement with the referenced parameters + * encoded in the statement. + * + * @return a complete sql statement + */ + String toSQL() { + if (sql == null) { + sql = buff.toString(); + } + if (params.size() == 0) { + return sql; + } + StringBuilder sb = new StringBuilder(); + // TODO this needs to me more sophisticated + StringTokenizer st = new StringTokenizer(sql, "?", false); + int i = 0; + while (st.hasMoreTokens()) { + sb.append(st.nextToken()); + if (i < params.size()) { + Object o = params.get(i); + if (RuntimeParameter.PARAMETER == o) { + // dynamic parameter + sb.append('?'); + } else { + // static parameter + sb.append(db.getDialect().prepareStringParameter(o)); + } + i++; + } + } + return sb.toString(); + } + + public SQLStatement addParameter(Object o) { + // Automatically convert java.util.Date to java.sql.Timestamp + // if the dialect requires java.sql.Timestamp objects (e.g. Derby) + if (o != null && o.getClass().equals(java.util.Date.class) + && db.getDialect().getDateTimeClass().equals(java.sql.Timestamp.class)) { + o = new java.sql.Timestamp(((java.util.Date) o).getTime()); + } + params.add(o); + return this; + } + + void execute() { + PreparedStatement ps = null; + try { + ps = prepare(false); + ps.execute(); + } catch (SQLException e) { + throw IciqlException.fromSQL(getSQL(), e); + } finally { + JdbcUtils.closeSilently(ps); + } + } + + ResultSet executeQuery() { + try { + return prepare(false).executeQuery(); + } catch (SQLException e) { + throw IciqlException.fromSQL(getSQL(), e); + } + } + + int executeUpdate() { + PreparedStatement ps = null; + try { + ps = prepare(false); + return ps.executeUpdate(); + } catch (SQLException e) { + throw IciqlException.fromSQL(getSQL(), e); + } finally { + JdbcUtils.closeSilently(ps); + } + } + + long executeInsert() { + PreparedStatement ps = null; + try { + ps = prepare(true); + ps.executeUpdate(); + long identity = -1; + ResultSet rs = ps.getGeneratedKeys(); + if (rs != null && rs.next()) { + identity = rs.getLong(1); + } + JdbcUtils.closeSilently(rs); + return identity; + } catch (SQLException e) { + throw IciqlException.fromSQL(getSQL(), e); + } finally { + JdbcUtils.closeSilently(ps); + } + } + + private void setValue(PreparedStatement prep, int parameterIndex, Object x) { + try { + prep.setObject(parameterIndex, x); + } catch (SQLException e) { + IciqlException ix = new IciqlException(e, "error setting parameter {0} as {1}", parameterIndex, x + .getClass().getSimpleName()); + ix.setSQL(getSQL()); + throw ix; + } + } + + PreparedStatement prepare(boolean returnGeneratedKeys) { + PreparedStatement prep = db.prepare(getSQL(), returnGeneratedKeys); + for (int i = 0; i < params.size(); i++) { + Object o = params.get(i); + setValue(prep, i + 1, o); + } + return prep; + } } diff --git a/src/main/java/com/iciql/SelectColumn.java b/src/main/java/com/iciql/SelectColumn.java index 43a1a93..ef39703 100644 --- a/src/main/java/com/iciql/SelectColumn.java +++ b/src/main/java/com/iciql/SelectColumn.java @@ -21,37 +21,36 @@ import com.iciql.TableDefinition.FieldDefinition; /** * This class represents a column of a table in a query. - * - * @param - * the table data type + * + * @param the table data type */ class SelectColumn { - private SelectTable selectTable; - private FieldDefinition fieldDef; - - SelectColumn(SelectTable table, FieldDefinition fieldDef) { - this.selectTable = table; - this.fieldDef = fieldDef; - } - - void appendSQL(SQLStatement stat) { - if (selectTable.getQuery().isJoin()) { - stat.appendSQL(selectTable.getAs() + "." + fieldDef.columnName); - } else { - stat.appendColumn(fieldDef.columnName); - } - } - - FieldDefinition getFieldDefinition() { - return fieldDef; - } - - SelectTable getSelectTable() { - return selectTable; - } - - Object getCurrentValue() { - return fieldDef.getValue(selectTable.getCurrent()); - } + private SelectTable selectTable; + private FieldDefinition fieldDef; + + SelectColumn(SelectTable table, FieldDefinition fieldDef) { + this.selectTable = table; + this.fieldDef = fieldDef; + } + + void appendSQL(SQLStatement stat) { + if (selectTable.getQuery().isJoin()) { + stat.appendSQL(selectTable.getAs() + "." + fieldDef.columnName); + } else { + stat.appendColumn(fieldDef.columnName); + } + } + + FieldDefinition getFieldDefinition() { + return fieldDef; + } + + SelectTable getSelectTable() { + return selectTable; + } + + Object getCurrentValue() { + return fieldDef.getValue(selectTable.getCurrent()); + } } diff --git a/src/main/java/com/iciql/SelectTable.java b/src/main/java/com/iciql/SelectTable.java index 37b42c4..acb5e17 100644 --- a/src/main/java/com/iciql/SelectTable.java +++ b/src/main/java/com/iciql/SelectTable.java @@ -17,96 +17,95 @@ package com.iciql; -import java.util.ArrayList; - import com.iciql.util.Utils; +import java.util.ArrayList; + /** * This class represents a table in a query. - * - * @param - * the table class + * + * @param the table class */ class SelectTable { - private Query query; - private Class clazz; - private T current; - private String as; - private TableDefinition aliasDef; - private boolean outerJoin; - private ArrayList joinConditions = Utils.newArrayList(); - private T alias; - - @SuppressWarnings("unchecked") - SelectTable(Db db, Query query, T alias, boolean outerJoin) { - this.alias = alias; - this.query = query; - this.outerJoin = outerJoin; - aliasDef = (TableDefinition) db.getTableDefinition(alias.getClass()); - clazz = Utils.getClass(alias); - as = "T" + Utils.nextAsCount(); - } - - T getAlias() { - return alias; - } - - T newObject() { - return Utils.newObject(clazz); - } - - TableDefinition getAliasDefinition() { - return aliasDef; - } - - void appendSQL(SQLStatement stat) { - if (query.isJoin()) { - stat.appendTable(aliasDef.schemaName, aliasDef.tableName).appendSQL(" AS " + as); - } else { - stat.appendTable(aliasDef.schemaName, aliasDef.tableName); - } - } - - void appendSQLAsJoin(SQLStatement stat, Query q) { - if (outerJoin) { - stat.appendSQL(" LEFT OUTER JOIN "); - } else { - stat.appendSQL(" INNER JOIN "); - } - appendSQL(stat); - if (!joinConditions.isEmpty()) { - stat.appendSQL(" ON "); - for (Token token : joinConditions) { - token.appendSQL(stat, q); - stat.appendSQL(" "); - } - } - } - - boolean getOuterJoin() { - return outerJoin; - } - - Query getQuery() { - return query; - } - - String getAs() { - return as; - } - - void addConditionToken(Token condition) { - joinConditions.add(condition); - } - - T getCurrent() { - return current; - } - - void setCurrent(T current) { - this.current = current; - } + private Query query; + private Class clazz; + private T current; + private String as; + private TableDefinition aliasDef; + private boolean outerJoin; + private ArrayList joinConditions = Utils.newArrayList(); + private T alias; + + @SuppressWarnings("unchecked") + SelectTable(Db db, Query query, T alias, boolean outerJoin) { + this.alias = alias; + this.query = query; + this.outerJoin = outerJoin; + aliasDef = (TableDefinition) db.getTableDefinition(alias.getClass()); + clazz = Utils.getClass(alias); + as = "T" + Utils.nextAsCount(); + } + + T getAlias() { + return alias; + } + + T newObject() { + return Utils.newObject(clazz); + } + + TableDefinition getAliasDefinition() { + return aliasDef; + } + + void appendSQL(SQLStatement stat) { + if (query.isJoin()) { + stat.appendTable(aliasDef.schemaName, aliasDef.tableName).appendSQL(" AS " + as); + } else { + stat.appendTable(aliasDef.schemaName, aliasDef.tableName); + } + } + + void appendSQLAsJoin(SQLStatement stat, Query q) { + if (outerJoin) { + stat.appendSQL(" LEFT OUTER JOIN "); + } else { + stat.appendSQL(" INNER JOIN "); + } + appendSQL(stat); + if (!joinConditions.isEmpty()) { + stat.appendSQL(" ON "); + for (Token token : joinConditions) { + token.appendSQL(stat, q); + stat.appendSQL(" "); + } + } + } + + boolean getOuterJoin() { + return outerJoin; + } + + Query getQuery() { + return query; + } + + String getAs() { + return as; + } + + void addConditionToken(Token condition) { + joinConditions.add(condition); + } + + T getCurrent() { + return current; + } + + void setCurrent(T current) { + this.current = current; + } } diff --git a/src/main/java/com/iciql/SubQuery.java b/src/main/java/com/iciql/SubQuery.java index 398d214..410a42b 100644 --- a/src/main/java/com/iciql/SubQuery.java +++ b/src/main/java/com/iciql/SubQuery.java @@ -17,16 +17,16 @@ package com.iciql; public class SubQuery { - - final Query query; - final Z z; - - public SubQuery(Query query, Z x) { - this.query = query; - this.z = x; - } - public void appendSQL(SQLStatement stat) { - stat.appendSQL(query.toSubQuery(z)); - } + final Query query; + final Z z; + + public SubQuery(Query query, Z x) { + this.query = query; + this.z = x; + } + + public void appendSQL(SQLStatement stat) { + stat.appendSQL(query.toSubQuery(z)); + } } diff --git a/src/main/java/com/iciql/SubQueryCondition.java b/src/main/java/com/iciql/SubQueryCondition.java index effea3b..c6ca051 100644 --- a/src/main/java/com/iciql/SubQueryCondition.java +++ b/src/main/java/com/iciql/SubQueryCondition.java @@ -18,24 +18,23 @@ package com.iciql; /** * A condition that contains a subquery. - * - * @param - * the operand type + * + * @param the operand type */ class SubQueryCondition implements Token { - A x; - SubQuery subquery; + A x; + SubQuery subquery; - SubQueryCondition(A x, SubQuery subquery) { - this.x = x; - this.subquery = subquery; - } + SubQueryCondition(A x, SubQuery subquery) { + this.x = x; + this.subquery = subquery; + } - public void appendSQL(SQLStatement stat, Query query) { - query.appendSQL(stat, null, x); - stat.appendSQL(" in ("); - subquery.appendSQL(stat); - stat.appendSQL(")"); - } + public void appendSQL(SQLStatement stat, Query query) { + query.appendSQL(stat, null, x); + stat.appendSQL(" in ("); + subquery.appendSQL(stat); + stat.appendSQL(")"); + } } diff --git a/src/main/java/com/iciql/TableDefinition.java b/src/main/java/com/iciql/TableDefinition.java index 53ef895..fe273c0 100644 --- a/src/main/java/com/iciql/TableDefinition.java +++ b/src/main/java/com/iciql/TableDefinition.java @@ -18,6 +18,12 @@ package com.iciql; +import com.iciql.Iciql.*; +import com.iciql.util.IciqlLogger; +import com.iciql.util.StatementBuilder; +import com.iciql.util.StringUtils; +import com.iciql.util.Utils; + import java.lang.reflect.Field; import java.sql.PreparedStatement; import java.sql.ResultSet; @@ -30,1193 +36,1149 @@ 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 - * the table type + * @param the table type */ public class TableDefinition { - /** - * The meta data of an index. - */ - - public static class IndexDefinition { - public IndexType type; - public String indexName; - - public List columnNames; - } - - /** - * The meta data of a constraint on foreign key. - */ - - public static class ConstraintForeignKeyDefinition { - - public String constraintName; - public List foreignColumns; - public String referenceTable; - public List 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 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> 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 fields = Utils.newArrayList(); - String schemaName; - String tableName; - String viewTableName; - int tableVersion; - List primaryKeyColumnNames; - boolean memoryTable; - boolean multiplePrimitiveBools; - - private boolean createIfRequired = true; - private Class clazz; - private IdentityHashMap fieldMap = Utils.newIdentityHashMap(); - private ArrayList indexes = Utils.newArrayList(); - ArrayList constraintsForeignKey = Utils.newArrayList(); - ArrayList constraintsUnique = Utils.newArrayList(); - - TableDefinition(Class clazz) { - this.clazz = clazz; - schemaName = null; - tableName = clazz.getSimpleName(); - } - - Class getModelClass() { - return clazz; - } - - List 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 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 columnNames) { - primaryKeyColumnNames = Utils.newArrayList(columnNames); - List 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 String getColumnName(A fieldObject) { - FieldDefinition def = fieldMap.get(fieldObject); - return def == null ? null : def.columnName; - } - - private ArrayList mapColumnNames(Object[] columns) { - ArrayList 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 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 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 columnNames = mapColumnNames(modelFields); - addConstraintUnique(name, columnNames); - } - - /** - * Defines an unique constraint. - * - * @param name - * @param columnNames - */ - private void addConstraintUnique(String name, List 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 columnNames = mapColumnNames(modelFields); - List 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> 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 classFields = classFields(inheritColumns); - - Set uniqueFields = new LinkedHashSet(); - 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> 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 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!", tableName); - } - if (primaryKey.size() > 0) { - setPrimaryKey(primaryKey); - } - } - - private List classFields(boolean inheritColumns) { - List 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) { - if (!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) { - return !StringUtils.isNullOrEmpty(field.defaultValue); - } - } - 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 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(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 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(fieldAlias, value, CompareType.EQUAL)); - } - } - stat.setSQL(buff.toString()); - query.appendWhere(stat); - IciqlLogger.delete(stat.getSQL()); - return stat.executeUpdate(); - } - - TableDefinition 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 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 foreignColumns = Arrays.asList(constraint.foreignColumns()); - List referenceColumns = Arrays.asList(constraint.referenceColumns()); - addConstraintForeignKey(constraint.name(), foreignColumns, constraint.referenceName(), referenceColumns, constraint.deleteType(), constraint.updateType(), constraint.deferrabilityType()); - } - - private void addConstraintUnique(IQContraintUnique constraint) { - List 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 foreignColumns, String referenceName, - List 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 columns = Arrays.asList(index.value()); - addIndex(index.name(), index.type(), columns); - } - - List getIndexes() { - return indexes; - } - - List getContraintsUnique() { - return constraintsUnique; - } - - List getContraintsForeignKey() { - return constraintsForeignKey; - } - - private void initObject(Object obj, Map map) { - for (FieldDefinition def : fields) { - Object newValue = def.initWithNewObject(obj); - map.put(newValue, def); - } - } - - void initSelectObject(SelectTable table, Object obj, Map> map, boolean reuse) { - for (FieldDefinition def : fields) { - Object value; - if (!reuse) { - value = def.initWithNewObject(obj); - } else { - value = def.getValue(obj); - } - SelectColumn column = new SelectColumn(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(SQLDialect dialect, 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(dialect.extractColumnName(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); - } - } - - void appendSelectList(SQLStatement stat, Query 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); - } - } - } + /** + * The meta data of an index. + */ + + public static class IndexDefinition { + public IndexType type; + public String indexName; + + public List columnNames; + } + + /** + * The meta data of a constraint on foreign key. + */ + + public static class ConstraintForeignKeyDefinition { + + public String constraintName; + public List foreignColumns; + public String referenceTable; + public List 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 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> 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 fields = Utils.newArrayList(); + String schemaName; + String tableName; + String viewTableName; + int tableVersion; + List primaryKeyColumnNames; + boolean memoryTable; + boolean multiplePrimitiveBools; + + private boolean createIfRequired = true; + private Class clazz; + private IdentityHashMap fieldMap = Utils.newIdentityHashMap(); + private ArrayList indexes = Utils.newArrayList(); + ArrayList constraintsForeignKey = Utils.newArrayList(); + ArrayList constraintsUnique = Utils.newArrayList(); + + TableDefinition(Class clazz) { + this.clazz = clazz; + schemaName = null; + tableName = clazz.getSimpleName(); + } + + Class getModelClass() { + return clazz; + } + + List 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 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 columnNames) { + primaryKeyColumnNames = Utils.newArrayList(columnNames); + List 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 String getColumnName(A fieldObject) { + FieldDefinition def = fieldMap.get(fieldObject); + return def == null ? null : def.columnName; + } + + private ArrayList mapColumnNames(Object[] columns) { + ArrayList 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 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 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 columnNames = mapColumnNames(modelFields); + addConstraintUnique(name, columnNames); + } + + /** + * Defines an unique constraint. + * + * @param name + * @param columnNames + */ + private void addConstraintUnique(String name, List 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 columnNames = mapColumnNames(modelFields); + List 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> 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 classFields = classFields(inheritColumns); + + Set uniqueFields = new LinkedHashSet(); + 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> 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 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!", tableName); + } + if (primaryKey.size() > 0) { + setPrimaryKey(primaryKey); + } + } + + private List classFields(boolean inheritColumns) { + List 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) { + if (!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) { + return !StringUtils.isNullOrEmpty(field.defaultValue); + } + } + 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 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(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 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(fieldAlias, value, CompareType.EQUAL)); + } + } + stat.setSQL(buff.toString()); + query.appendWhere(stat); + IciqlLogger.delete(stat.getSQL()); + return stat.executeUpdate(); + } + + TableDefinition 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 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 foreignColumns = Arrays.asList(constraint.foreignColumns()); + List referenceColumns = Arrays.asList(constraint.referenceColumns()); + addConstraintForeignKey(constraint.name(), foreignColumns, constraint.referenceName(), referenceColumns, constraint.deleteType(), constraint.updateType(), constraint.deferrabilityType()); + } + + private void addConstraintUnique(IQContraintUnique constraint) { + List 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 foreignColumns, String referenceName, + List 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 columns = Arrays.asList(index.value()); + addIndex(index.name(), index.type(), columns); + } + + List getIndexes() { + return indexes; + } + + List getContraintsUnique() { + return constraintsUnique; + } + + List getContraintsForeignKey() { + return constraintsForeignKey; + } + + private void initObject(Object obj, Map map) { + for (FieldDefinition def : fields) { + Object newValue = def.initWithNewObject(obj); + map.put(newValue, def); + } + } + + void initSelectObject(SelectTable table, Object obj, Map> map, boolean reuse) { + for (FieldDefinition def : fields) { + Object value; + if (!reuse) { + value = def.initWithNewObject(obj); + } else { + value = def.getValue(obj); + } + SelectColumn column = new SelectColumn(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(SQLDialect dialect, 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(dialect.extractColumnName(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); + } + } + + void appendSelectList(SQLStatement stat, Query 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); + } + } + } } diff --git a/src/main/java/com/iciql/TableInspector.java b/src/main/java/com/iciql/TableInspector.java index b717203..d775f0f 100644 --- a/src/main/java/com/iciql/TableInspector.java +++ b/src/main/java/com/iciql/TableInspector.java @@ -18,25 +18,6 @@ package com.iciql; -import static com.iciql.ValidationRemark.consider; -import static com.iciql.ValidationRemark.error; -import static com.iciql.ValidationRemark.warn; -import static com.iciql.util.JdbcUtils.closeSilently; -import static com.iciql.util.StringUtils.isNullOrEmpty; -import static java.text.MessageFormat.format; - -import java.io.Serializable; -import java.lang.reflect.Modifier; -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; - import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQIndex; import com.iciql.Iciql.IQIndexes; @@ -51,6 +32,23 @@ import com.iciql.util.StatementBuilder; import com.iciql.util.StringUtils; import com.iciql.util.Utils; +import java.io.Serializable; +import java.lang.reflect.Modifier; +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.iciql.ValidationRemark.*; +import static com.iciql.util.JdbcUtils.closeSilently; +import static com.iciql.util.StringUtils.isNullOrEmpty; +import static java.text.MessageFormat.format; + /** * Class to inspect the contents of a particular table including its indexes. * This class does the bulk of the work in terms of model generation and model @@ -58,666 +56,661 @@ import com.iciql.util.Utils; */ public class TableInspector { - private String schema; - private String table; - private Class dateTimeClass; - private List primaryKeys = Utils.newArrayList(); - private Map indexes; - private Map columns; - private final String eol = "\n"; - - TableInspector(String schema, String table, Class dateTimeClass) { - this.schema = schema; - this.table = table; - this.dateTimeClass = dateTimeClass; - } - - /** - * Tests to see if this TableInspector represents schema.table. - *

- * - * @param schema - * the schema name - * @param table - * the table name - * @return true if the table matches - */ - boolean matches(String schema, String table) { - if (isNullOrEmpty(schema)) { - // table name matching - return this.table.equalsIgnoreCase(table); - } else if (isNullOrEmpty(table)) { - // schema name matching - return this.schema.equalsIgnoreCase(schema); - } else { - // exact table matching - return this.schema.equalsIgnoreCase(schema) && this.table.equalsIgnoreCase(table); - } - } - - /** - * Reads the DatabaseMetaData for the details of this table including - * primary keys and indexes. - * - * @param metaData - * the database meta data - */ - void read(DatabaseMetaData metaData) throws SQLException { - ResultSet rs = null; - - // primary keys - try { - rs = metaData.getPrimaryKeys(null, schema, table); - while (rs.next()) { - String c = rs.getString("COLUMN_NAME"); - primaryKeys.add(c); - } - closeSilently(rs); - - // indexes - rs = metaData.getIndexInfo(null, schema, table, false, true); - indexes = Utils.newHashMap(); - while (rs.next()) { - IndexInspector info = new IndexInspector(rs); - if (info.type.equals(IndexType.UNIQUE)) { - String name = info.name.toLowerCase(); - if (name.startsWith("primary") || name.startsWith("sys_idx_sys_pk") - || name.startsWith("sql") || name.endsWith("_pkey")) { - // skip primary key indexes - continue; - } - } - if (indexes.containsKey(info.name)) { - indexes.get(info.name).addColumn(rs); - } else { - indexes.put(info.name, info); - } - } - closeSilently(rs); - - // columns - rs = metaData.getColumns(null, schema, table, null); - columns = Utils.newHashMap(); - while (rs.next()) { - ColumnInspector col = new ColumnInspector(); - col.name = rs.getString("COLUMN_NAME"); - col.type = rs.getString("TYPE_NAME"); - col.clazz = ModelUtils.getClassForSqlType(col.type, dateTimeClass); - col.size = rs.getInt("COLUMN_SIZE"); - col.nullable = rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable; - try { - Object autoIncrement = rs.getObject("IS_AUTOINCREMENT"); - if (autoIncrement instanceof Boolean) { - col.isAutoIncrement = (Boolean) autoIncrement; - } else if (autoIncrement instanceof String) { - String val = autoIncrement.toString().toLowerCase(); - col.isAutoIncrement = val.equals("true") | val.equals("yes"); - } else if (autoIncrement instanceof Number) { - Number n = (Number) autoIncrement; - col.isAutoIncrement = n.intValue() > 0; - } - } catch (SQLException s) { + private String schema; + private String table; + private Class dateTimeClass; + private List primaryKeys = Utils.newArrayList(); + private Map indexes; + private Map columns; + private final String eol = "\n"; + + TableInspector(String schema, String table, Class dateTimeClass) { + this.schema = schema; + this.table = table; + this.dateTimeClass = dateTimeClass; + } + + /** + * Tests to see if this TableInspector represents schema.table. + *

+ * + * @param schema the schema name + * @param table the table name + * @return true if the table matches + */ + boolean matches(String schema, String table) { + if (isNullOrEmpty(schema)) { + // table name matching + return this.table.equalsIgnoreCase(table); + } else if (isNullOrEmpty(table)) { + // schema name matching + return this.schema.equalsIgnoreCase(schema); + } else { + // exact table matching + return this.schema.equalsIgnoreCase(schema) && this.table.equalsIgnoreCase(table); + } + } + + /** + * Reads the DatabaseMetaData for the details of this table including + * primary keys and indexes. + * + * @param metaData the database meta data + */ + void read(DatabaseMetaData metaData) throws SQLException { + ResultSet rs = null; + + // primary keys + try { + rs = metaData.getPrimaryKeys(null, schema, table); + while (rs.next()) { + String c = rs.getString("COLUMN_NAME"); + primaryKeys.add(c); + } + closeSilently(rs); + + // indexes + rs = metaData.getIndexInfo(null, schema, table, false, true); + indexes = Utils.newHashMap(); + while (rs.next()) { + IndexInspector info = new IndexInspector(rs); + if (info.type.equals(IndexType.UNIQUE)) { + String name = info.name.toLowerCase(); + if (name.startsWith("primary") || name.startsWith("sys_idx_sys_pk") + || name.startsWith("sql") || name.endsWith("_pkey")) { + // skip primary key indexes + continue; + } + } + if (indexes.containsKey(info.name)) { + indexes.get(info.name).addColumn(rs); + } else { + indexes.put(info.name, info); + } + } + closeSilently(rs); + + // columns + rs = metaData.getColumns(null, schema, table, null); + columns = Utils.newHashMap(); + while (rs.next()) { + ColumnInspector col = new ColumnInspector(); + col.name = rs.getString("COLUMN_NAME"); + col.type = rs.getString("TYPE_NAME"); + col.clazz = ModelUtils.getClassForSqlType(col.type, dateTimeClass); + col.size = rs.getInt("COLUMN_SIZE"); + col.nullable = rs.getInt("NULLABLE") == DatabaseMetaData.columnNullable; + try { + Object autoIncrement = rs.getObject("IS_AUTOINCREMENT"); + if (autoIncrement instanceof Boolean) { + col.isAutoIncrement = (Boolean) autoIncrement; + } else if (autoIncrement instanceof String) { + String val = autoIncrement.toString().toLowerCase(); + col.isAutoIncrement = val.equals("true") | val.equals("yes"); + } else if (autoIncrement instanceof Number) { + Number n = (Number) autoIncrement; + col.isAutoIncrement = n.intValue() > 0; + } + } catch (SQLException s) { // throw s; - } - if (primaryKeys.size() == 1) { - if (col.name.equalsIgnoreCase(primaryKeys.get(0))) { - col.isPrimaryKey = true; - } - } - if (!col.isAutoIncrement) { - col.defaultValue = rs.getString("COLUMN_DEF"); - } - columns.put(col.name.toLowerCase(), col); - } - } finally { - closeSilently(rs); - } - } - - /** - * Generates a model (class definition) from this table. The model includes - * indexes, primary keys, default values, lengths, and nullables. - * information. - *

- * The caller may optionally set a destination package name, whether or not - * to include the schema name (setting schema can be a problem when using - * the model between databases), and if to automatically trim strings for - * those that have a maximum length. - *

- * - * @param packageName - * @param annotateSchema - * @param trimStrings - * @return a complete model (class definition) for this table as a string - */ - String generateModel(String packageName, boolean annotateSchema, boolean trimStrings) { - - // import statements - Set imports = Utils.newHashSet(); - imports.add(Serializable.class.getCanonicalName()); - imports.add(IQSchema.class.getCanonicalName()); - imports.add(IQTable.class.getCanonicalName()); - imports.add(IQIndexes.class.getCanonicalName()); - imports.add(IQIndex.class.getCanonicalName()); - imports.add(IQColumn.class.getCanonicalName()); - imports.add(IndexType.class.getCanonicalName()); - - // fields - StringBuilder fields = new StringBuilder(); - List sortedColumns = Utils.newArrayList(columns.values()); - Collections.sort(sortedColumns); - for (ColumnInspector col : sortedColumns) { - fields.append(generateColumn(imports, col, trimStrings)); - } - - // build complete class definition - StringBuilder model = new StringBuilder(); - if (!isNullOrEmpty(packageName)) { - // package - model.append("package " + packageName + ";"); - model.append(eol).append(eol); - } - - // imports - List sortedImports = new ArrayList(imports); - Collections.sort(sortedImports); - for (String imp : sortedImports) { - model.append("import ").append(imp).append(';').append(eol); - } - model.append(eol); - - // @IQSchema - if (annotateSchema && !isNullOrEmpty(schema)) { - model.append('@').append(IQSchema.class.getSimpleName()); - model.append('('); - AnnotationBuilder ap = new AnnotationBuilder(); - ap.addParameter(null, schema); - model.append(ap); - model.append(')').append(eol); - } - - // @IQTable - model.append('@').append(IQTable.class.getSimpleName()); - model.append('('); - - // IQTable annotation parameters - AnnotationBuilder ap = new AnnotationBuilder(); - ap.addParameter("name", table); - - if (primaryKeys.size() > 1) { - ap.addParameter("primaryKey", primaryKeys); - } - - // finish @IQTable annotation - model.append(ap); - model.append(')').append(eol); - - // @IQIndexes - // @IQIndex - String indexAnnotations = generateIndexAnnotations(); - if (!StringUtils.isNullOrEmpty(indexAnnotations)) { - model.append(indexAnnotations); - } - - // class declaration - String clazzName = ModelUtils.convertTableToClassName(table); - model.append(format("public class {0} implements Serializable '{'", clazzName)).append(eol); - model.append(eol); - model.append("\tprivate static final long serialVersionUID = 1L;").append(eol); - model.append(eol); - - // field declarations - model.append(fields); - - // default constructor - model.append("\t" + "public ").append(clazzName).append("() {").append(eol); - model.append("\t}").append(eol); - - // end of class body - model.append('}'); - model.trimToSize(); - return model.toString(); - } - - /** - * Generates the specified index annotation. - * - * @param ap - */ - String generateIndexAnnotations() { - if (indexes == null || indexes.size() == 0) { - // no matching indexes - return null; - } - AnnotationBuilder ap = new AnnotationBuilder(); - if (indexes.size() == 1) { - // single index - IndexInspector index = indexes.values().toArray(new IndexInspector[1])[0]; - ap.append(generateIndexAnnotation(index)); - ap.append(eol); - } else { - // multiple indexes - ap.append('@').append(IQIndexes.class.getSimpleName()); - ap.append("({"); - ap.resetCount(); - for (IndexInspector index : indexes.values()) { - ap.appendExceptFirst(", "); - ap.append(generateIndexAnnotation(index)); - } - ap.append("})").append(eol); - } - return ap.toString(); - } - - private String generateIndexAnnotation(IndexInspector index) { - AnnotationBuilder ap = new AnnotationBuilder(); - ap.append('@').append(IQIndex.class.getSimpleName()); - ap.append('('); - ap.resetCount(); - if (!StringUtils.isNullOrEmpty(index.name)) { - ap.addParameter("name", index.name); - } - if (!index.type.equals(IndexType.STANDARD)) { - ap.addEnum("type", index.type); - } - if (ap.getCount() > 0) { - // multiple fields specified - ap.addParameter("value", index.columns); - } else { - // default value - ap.addParameter(null, index.columns); - } - ap.append(')'); - return ap.toString(); - } - - private StatementBuilder generateColumn(Set imports, ColumnInspector col, boolean trimStrings) { - StatementBuilder sb = new StatementBuilder(); - Class clazz = col.clazz; - String column = ModelUtils.convertColumnToFieldName(col.name.toLowerCase()); - sb.append('\t'); - if (clazz == null) { - // unsupported type - clazz = Object.class; - sb.append("// unsupported type " + col.type); - } else { - // Imports - // don't import primitives, java.lang classes, or byte [] - if (clazz.getPackage() == null) { - } else if (clazz.getPackage().getName().equals("java.lang")) { - } else if (clazz.equals(byte[].class)) { - } else { - imports.add(clazz.getCanonicalName()); - } - // @IQColumn - sb.append('@').append(IQColumn.class.getSimpleName()); - - // IQColumn annotation parameters - AnnotationBuilder ap = new AnnotationBuilder(); - - // IQColumn.name - if (!col.name.equalsIgnoreCase(column)) { - ap.addParameter("name", col.name); - } - - // IQColumn.primaryKey - // composite primary keys are annotated on the table - if (col.isPrimaryKey && primaryKeys.size() == 1) { - ap.addParameter("primaryKey=true"); - } - - // IQColumn.length - if ((clazz == String.class) && (col.size > 0) && (col.size < Integer.MAX_VALUE)) { - ap.addParameter("length", col.size); - - // IQColumn.trim - if (trimStrings) { - ap.addParameter("trim=true"); - } - } else { - // IQColumn.AutoIncrement - if (col.isAutoIncrement) { - ap.addParameter("autoIncrement=true"); - } - } - - // IQColumn.nullable - if (!col.nullable) { - ap.addParameter("nullable=false"); - } - - // IQColumn.defaultValue - if (!isNullOrEmpty(col.defaultValue)) { - ap.addParameter("defaultValue=\"" + col.defaultValue + "\""); - } - - // add leading and trailing () - if (ap.length() > 0) { - ap.insert(0, '('); - ap.append(')'); - } - sb.append(ap); - } - sb.append(eol); - - // variable declaration - sb.append("\t" + "public "); - sb.append(clazz.getSimpleName()); - sb.append(' '); - sb.append(column); - sb.append(';'); - sb.append(eol).append(eol); - return sb; - } - - /** - * Validates that a table definition (annotated, interface, or both) matches - * the current state of the table and indexes in the database. Results are - * returned as a list of validation remarks which includes recommendations, - * warnings, and errors about the model. The caller may choose to have - * validate throw an exception on any validation ERROR. - * - * @param def - * the table definition - * @param throwError - * whether or not to throw an exception if an error was found - * @return a list if validation remarks - */ - List validate(TableDefinition def, boolean throwError) { - List remarks = Utils.newArrayList(); - - // model class definition validation - if (!Modifier.isPublic(def.getModelClass().getModifiers())) { - remarks.add(error(table, "SCHEMA", - format("Class {0} MUST BE PUBLIC!", def.getModelClass().getCanonicalName())).throwError( - throwError)); - } - - // Schema Validation - if (!isNullOrEmpty(schema)) { - if (isNullOrEmpty(def.schemaName)) { - remarks.add(consider(table, "SCHEMA", - format("@{0}(\"{1}\")", IQSchema.class.getSimpleName(), schema))); - } else if (!schema.equalsIgnoreCase(def.schemaName)) { - remarks.add(error( - table, - "SCHEMA", - format("@{0}(\"{1}\") != {2}", IQSchema.class.getSimpleName(), def.schemaName, schema)) - .throwError(throwError)); - } - } - - // index validation - for (IndexInspector index : indexes.values()) { - validate(remarks, def, index, throwError); - } - - // field column validation - for (FieldDefinition fieldDef : def.getFields()) { - validate(remarks, fieldDef, throwError); - } - return remarks; - } - - /** - * Validates an inspected index from the database against the - * IndexDefinition within the TableDefinition. - */ - private void validate(List remarks, TableDefinition def, IndexInspector index, - boolean throwError) { - List defIndexes = def.getIndexes(); - if (defIndexes.size() > indexes.size()) { - remarks.add(warn(table, IndexType.STANDARD.name(), "More model indexes than database indexes")); - } else if (defIndexes.size() < indexes.size()) { - remarks.add(warn(table, IndexType.STANDARD.name(), "Model class is missing indexes")); - } - // TODO complete index validation. - // need to actually compare index types and columns within each index. - - // TODO add constraints validation - List defContraintsU = def.getContraintsUnique(); - List defContraintsFK = def.getContraintsForeignKey(); - } - - /** - * Validates a column against the model's field definition. Checks for - * existence, supported type, type mapping, default value, defined lengths, - * primary key, autoincrement. - */ - private void validate(List remarks, FieldDefinition fieldDef, boolean throwError) { - // unknown field - if (!columns.containsKey(fieldDef.columnName.toLowerCase())) { - // unknown column mapping - remarks.add(error(table, fieldDef, "Does not exist in database!").throwError(throwError)); - return; - } - ColumnInspector col = columns.get(fieldDef.columnName.toLowerCase()); - Class fieldClass = fieldDef.field.getType(); - Class jdbcClass = ModelUtils.getClassForSqlType(col.type, dateTimeClass); - - // supported type check - // iciql maps to VARCHAR for unsupported types. - if (fieldDef.dataType.equals("VARCHAR") && (fieldClass != String.class)) { - remarks.add(error(table, fieldDef, - "iciql does not currently implement support for " + fieldClass.getName()).throwError( - throwError)); - } - // number types - if (!fieldClass.equals(jdbcClass)) { - if (Number.class.isAssignableFrom(fieldClass)) { - remarks.add(warn( - table, - col, - format("Precision mismatch: ModelObject={0}, ColumnObject={1}", - fieldClass.getSimpleName(), jdbcClass.getSimpleName()))); - } else { - if (!Date.class.isAssignableFrom(jdbcClass)) { - remarks.add(warn( - table, - col, - format("Object Mismatch: ModelObject={0}, ColumnObject={1}", - fieldClass.getSimpleName(), jdbcClass.getSimpleName()))); - } - } - } - - // string types - if (fieldClass == String.class) { - if ((fieldDef.length != col.size) && (col.size < Integer.MAX_VALUE)) { - remarks.add(warn( - table, - col, - format("{0}.length={1}, ColumnMaxLength={2}", IQColumn.class.getSimpleName(), - fieldDef.length, col.size))); - } - if (fieldDef.length > 0 && !fieldDef.trim) { - remarks.add(consider(table, col, format("{0}.trim=true will prevent IciqlExceptions on" - + " INSERT or UPDATE, but will clip data!", IQColumn.class.getSimpleName()))); - } - } - - // numeric autoIncrement - if (fieldDef.isAutoIncrement != col.isAutoIncrement) { - remarks.add(warn( - table, - col, - format("{0}.autoIncrement={1}" + " while Column autoIncrement={2}", - IQColumn.class.getSimpleName(), fieldDef.isAutoIncrement, col.isAutoIncrement))); - } - // default value - if (!col.isAutoIncrement && !col.isPrimaryKey) { - String defaultValue = null; - if (fieldDef.defaultValue != null && fieldDef.defaultValue instanceof String) { - defaultValue = fieldDef.defaultValue.toString(); - } - // check Model.defaultValue format - if (!ModelUtils.isProperlyFormattedDefaultValue(defaultValue)) { - remarks.add(error( - table, - col, - format("{0}.defaultValue=\"{1}\"" + " is improperly formatted!", - IQColumn.class.getSimpleName(), defaultValue)).throwError(throwError)); - // next field - return; - } - // compare Model.defaultValue to Column.defaultValue - if (isNullOrEmpty(defaultValue) && !isNullOrEmpty(col.defaultValue)) { - // Model.defaultValue is NULL, Column.defaultValue is NOT NULL - remarks.add(warn( - table, - col, - format("{0}.defaultValue=\"\"" + " while column default=\"{1}\"", - IQColumn.class.getSimpleName(), col.defaultValue))); - } else if (!isNullOrEmpty(defaultValue) && isNullOrEmpty(col.defaultValue)) { - // Column.defaultValue is NULL, Model.defaultValue is NOT NULL - remarks.add(warn( - table, - col, - format("{0}.defaultValue=\"{1}\"" + " while column default=\"\"", - IQColumn.class.getSimpleName(), defaultValue))); - } else if (!isNullOrEmpty(defaultValue) && !isNullOrEmpty(col.defaultValue)) { - if (!defaultValue.equals(col.defaultValue)) { - // Model.defaultValue != Column.defaultValue - remarks.add(warn( - table, - col, - format("{0}.defaultValue=\"{1}\"" + " while column default=\"{2}\"", - IQColumn.class.getSimpleName(), defaultValue, col.defaultValue))); - } - } - - // sanity check Model.defaultValue literal value - if (!ModelUtils.isValidDefaultValue(fieldDef.field.getType(), defaultValue)) { - remarks.add(error( - table, - col, - format("{0}.defaultValue=\"{1}\" is invalid!", IQColumn.class.getSimpleName(), - defaultValue))); - } - } - } - - /** - * Represents an index as it exists in the database. - */ - private static class IndexInspector { - - String name; - IndexType type; - private List columns = new ArrayList(); - - public IndexInspector(ResultSet rs) throws SQLException { - name = rs.getString("INDEX_NAME"); - - // determine index type - boolean hash = rs.getInt("TYPE") == DatabaseMetaData.tableIndexHashed; - boolean unique = !rs.getBoolean("NON_UNIQUE"); - - if (!hash && !unique) { - type = IndexType.STANDARD; - } else if (hash && unique) { - type = IndexType.UNIQUE_HASH; - } else if (unique) { - type = IndexType.UNIQUE; - } else if (hash) { - type = IndexType.HASH; - } - columns.add(rs.getString("COLUMN_NAME")); - } - - public void addColumn(ResultSet rs) throws SQLException { - columns.add(rs.getString("COLUMN_NAME")); - } - } - - /** - * Represents a column as it exists in the database. - */ - static class ColumnInspector implements Comparable { - String name; - String type; - int size; - boolean nullable; - Class clazz; - boolean isPrimaryKey; - boolean isAutoIncrement; - String defaultValue; - - public int compareTo(ColumnInspector o) { - if (isPrimaryKey && o.isPrimaryKey) { - // both primary sort by name - return name.compareTo(o.name); - } else if (isPrimaryKey && !o.isPrimaryKey) { - // primary first - return -1; - } else if (!isPrimaryKey && o.isPrimaryKey) { - // primary first - return 1; - } else { - // neither primary, sort by name - return name.compareTo(o.name); - } - } - } - - /** - * Convenience class based on StatementBuilder for creating the annotation - * parameter list. - */ - private static class AnnotationBuilder extends StatementBuilder { - - AnnotationBuilder() { - super(); - } - - void addParameter(String parameter) { - - appendExceptFirst(", "); - append(parameter); - } - - void addParameter(String parameter, T value) { - appendExceptFirst(", "); - if (!StringUtils.isNullOrEmpty(parameter)) { - append(parameter); - append('='); - } - if (value instanceof List) { - append("{ "); - List list = (List) value; - StatementBuilder flat = new StatementBuilder(); - for (Object o : list) { - flat.appendExceptFirst(", "); - if (o instanceof String) { - flat.append('\"'); - } - // TODO escape string - flat.append(o.toString().trim()); - if (o instanceof String) { - flat.append('\"'); - } - } - append(flat); - append(" }"); - } else { - if (value instanceof String) { - append('\"'); - } - // TODO escape - append(value.toString().trim()); - if (value instanceof String) { - append('\"'); - } - } - } - - void addEnum(String parameter, Enum value) { - appendExceptFirst(", "); - if (!StringUtils.isNullOrEmpty(parameter)) { - append(parameter); - append('='); - } - append(value.getClass().getSimpleName() + "." + value.name()); - } - } + } + if (primaryKeys.size() == 1) { + if (col.name.equalsIgnoreCase(primaryKeys.get(0))) { + col.isPrimaryKey = true; + } + } + if (!col.isAutoIncrement) { + col.defaultValue = rs.getString("COLUMN_DEF"); + } + columns.put(col.name.toLowerCase(), col); + } + } finally { + closeSilently(rs); + } + } + + /** + * Generates a model (class definition) from this table. The model includes + * indexes, primary keys, default values, lengths, and nullables. + * information. + *

+ * The caller may optionally set a destination package name, whether or not + * to include the schema name (setting schema can be a problem when using + * the model between databases), and if to automatically trim strings for + * those that have a maximum length. + *

+ * + * @param packageName + * @param annotateSchema + * @param trimStrings + * @return a complete model (class definition) for this table as a string + */ + String generateModel(String packageName, boolean annotateSchema, boolean trimStrings) { + + // import statements + Set imports = Utils.newHashSet(); + imports.add(Serializable.class.getCanonicalName()); + imports.add(IQSchema.class.getCanonicalName()); + imports.add(IQTable.class.getCanonicalName()); + imports.add(IQIndexes.class.getCanonicalName()); + imports.add(IQIndex.class.getCanonicalName()); + imports.add(IQColumn.class.getCanonicalName()); + imports.add(IndexType.class.getCanonicalName()); + + // fields + StringBuilder fields = new StringBuilder(); + List sortedColumns = Utils.newArrayList(columns.values()); + Collections.sort(sortedColumns); + for (ColumnInspector col : sortedColumns) { + fields.append(generateColumn(imports, col, trimStrings)); + } + + // build complete class definition + StringBuilder model = new StringBuilder(); + if (!isNullOrEmpty(packageName)) { + // package + model.append("package " + packageName + ";"); + model.append(eol).append(eol); + } + + // imports + List sortedImports = new ArrayList(imports); + Collections.sort(sortedImports); + for (String imp : sortedImports) { + model.append("import ").append(imp).append(';').append(eol); + } + model.append(eol); + + // @IQSchema + if (annotateSchema && !isNullOrEmpty(schema)) { + model.append('@').append(IQSchema.class.getSimpleName()); + model.append('('); + AnnotationBuilder ap = new AnnotationBuilder(); + ap.addParameter(null, schema); + model.append(ap); + model.append(')').append(eol); + } + + // @IQTable + model.append('@').append(IQTable.class.getSimpleName()); + model.append('('); + + // IQTable annotation parameters + AnnotationBuilder ap = new AnnotationBuilder(); + ap.addParameter("name", table); + + if (primaryKeys.size() > 1) { + ap.addParameter("primaryKey", primaryKeys); + } + + // finish @IQTable annotation + model.append(ap); + model.append(')').append(eol); + + // @IQIndexes + // @IQIndex + String indexAnnotations = generateIndexAnnotations(); + if (!StringUtils.isNullOrEmpty(indexAnnotations)) { + model.append(indexAnnotations); + } + + // class declaration + String clazzName = ModelUtils.convertTableToClassName(table); + model.append(format("public class {0} implements Serializable '{'", clazzName)).append(eol); + model.append(eol); + model.append("\tprivate static final long serialVersionUID = 1L;").append(eol); + model.append(eol); + + // field declarations + model.append(fields); + + // default constructor + model.append("\t" + "public ").append(clazzName).append("() {").append(eol); + model.append("\t}").append(eol); + + // end of class body + model.append('}'); + model.trimToSize(); + return model.toString(); + } + + /** + * Generates the specified index annotation. + * + * @param ap + */ + String generateIndexAnnotations() { + if (indexes == null || indexes.size() == 0) { + // no matching indexes + return null; + } + AnnotationBuilder ap = new AnnotationBuilder(); + if (indexes.size() == 1) { + // single index + IndexInspector index = indexes.values().toArray(new IndexInspector[1])[0]; + ap.append(generateIndexAnnotation(index)); + ap.append(eol); + } else { + // multiple indexes + ap.append('@').append(IQIndexes.class.getSimpleName()); + ap.append("({"); + ap.resetCount(); + for (IndexInspector index : indexes.values()) { + ap.appendExceptFirst(", "); + ap.append(generateIndexAnnotation(index)); + } + ap.append("})").append(eol); + } + return ap.toString(); + } + + private String generateIndexAnnotation(IndexInspector index) { + AnnotationBuilder ap = new AnnotationBuilder(); + ap.append('@').append(IQIndex.class.getSimpleName()); + ap.append('('); + ap.resetCount(); + if (!StringUtils.isNullOrEmpty(index.name)) { + ap.addParameter("name", index.name); + } + if (!index.type.equals(IndexType.STANDARD)) { + ap.addEnum("type", index.type); + } + if (ap.getCount() > 0) { + // multiple fields specified + ap.addParameter("value", index.columns); + } else { + // default value + ap.addParameter(null, index.columns); + } + ap.append(')'); + return ap.toString(); + } + + private StatementBuilder generateColumn(Set imports, ColumnInspector col, boolean trimStrings) { + StatementBuilder sb = new StatementBuilder(); + Class clazz = col.clazz; + String column = ModelUtils.convertColumnToFieldName(col.name.toLowerCase()); + sb.append('\t'); + if (clazz == null) { + // unsupported type + clazz = Object.class; + sb.append("// unsupported type " + col.type); + } else { + // Imports + // don't import primitives, java.lang classes, or byte [] + if (clazz.getPackage() == null) { + } else if (clazz.getPackage().getName().equals("java.lang")) { + } else if (clazz.equals(byte[].class)) { + } else { + imports.add(clazz.getCanonicalName()); + } + // @IQColumn + sb.append('@').append(IQColumn.class.getSimpleName()); + + // IQColumn annotation parameters + AnnotationBuilder ap = new AnnotationBuilder(); + + // IQColumn.name + if (!col.name.equalsIgnoreCase(column)) { + ap.addParameter("name", col.name); + } + + // IQColumn.primaryKey + // composite primary keys are annotated on the table + if (col.isPrimaryKey && primaryKeys.size() == 1) { + ap.addParameter("primaryKey=true"); + } + + // IQColumn.length + if ((clazz == String.class) && (col.size > 0) && (col.size < Integer.MAX_VALUE)) { + ap.addParameter("length", col.size); + + // IQColumn.trim + if (trimStrings) { + ap.addParameter("trim=true"); + } + } else { + // IQColumn.AutoIncrement + if (col.isAutoIncrement) { + ap.addParameter("autoIncrement=true"); + } + } + + // IQColumn.nullable + if (!col.nullable) { + ap.addParameter("nullable=false"); + } + + // IQColumn.defaultValue + if (!isNullOrEmpty(col.defaultValue)) { + ap.addParameter("defaultValue=\"" + col.defaultValue + "\""); + } + + // add leading and trailing () + if (ap.length() > 0) { + ap.insert(0, '('); + ap.append(')'); + } + sb.append(ap); + } + sb.append(eol); + + // variable declaration + sb.append("\t" + "public "); + sb.append(clazz.getSimpleName()); + sb.append(' '); + sb.append(column); + sb.append(';'); + sb.append(eol).append(eol); + return sb; + } + + /** + * Validates that a table definition (annotated, interface, or both) matches + * the current state of the table and indexes in the database. Results are + * returned as a list of validation remarks which includes recommendations, + * warnings, and errors about the model. The caller may choose to have + * validate throw an exception on any validation ERROR. + * + * @param def the table definition + * @param throwError whether or not to throw an exception if an error was found + * @return a list if validation remarks + */ + List validate(TableDefinition def, boolean throwError) { + List remarks = Utils.newArrayList(); + + // model class definition validation + if (!Modifier.isPublic(def.getModelClass().getModifiers())) { + remarks.add(error(table, "SCHEMA", + format("Class {0} MUST BE PUBLIC!", def.getModelClass().getCanonicalName())).throwError( + throwError)); + } + + // Schema Validation + if (!isNullOrEmpty(schema)) { + if (isNullOrEmpty(def.schemaName)) { + remarks.add(consider(table, "SCHEMA", + format("@{0}(\"{1}\")", IQSchema.class.getSimpleName(), schema))); + } else if (!schema.equalsIgnoreCase(def.schemaName)) { + remarks.add(error( + table, + "SCHEMA", + format("@{0}(\"{1}\") != {2}", IQSchema.class.getSimpleName(), def.schemaName, schema)) + .throwError(throwError)); + } + } + + // index validation + for (IndexInspector index : indexes.values()) { + validate(remarks, def, index, throwError); + } + + // field column validation + for (FieldDefinition fieldDef : def.getFields()) { + validate(remarks, fieldDef, throwError); + } + return remarks; + } + + /** + * Validates an inspected index from the database against the + * IndexDefinition within the TableDefinition. + */ + private void validate(List remarks, TableDefinition def, IndexInspector index, + boolean throwError) { + List defIndexes = def.getIndexes(); + if (defIndexes.size() > indexes.size()) { + remarks.add(warn(table, IndexType.STANDARD.name(), "More model indexes than database indexes")); + } else if (defIndexes.size() < indexes.size()) { + remarks.add(warn(table, IndexType.STANDARD.name(), "Model class is missing indexes")); + } + // TODO complete index validation. + // need to actually compare index types and columns within each index. + + // TODO add constraints validation + List defContraintsU = def.getContraintsUnique(); + List defContraintsFK = def.getContraintsForeignKey(); + } + + /** + * Validates a column against the model's field definition. Checks for + * existence, supported type, type mapping, default value, defined lengths, + * primary key, autoincrement. + */ + private void validate(List remarks, FieldDefinition fieldDef, boolean throwError) { + // unknown field + if (!columns.containsKey(fieldDef.columnName.toLowerCase())) { + // unknown column mapping + remarks.add(error(table, fieldDef, "Does not exist in database!").throwError(throwError)); + return; + } + ColumnInspector col = columns.get(fieldDef.columnName.toLowerCase()); + Class fieldClass = fieldDef.field.getType(); + Class jdbcClass = ModelUtils.getClassForSqlType(col.type, dateTimeClass); + + // supported type check + // iciql maps to VARCHAR for unsupported types. + if (fieldDef.dataType.equals("VARCHAR") && (fieldClass != String.class)) { + remarks.add(error(table, fieldDef, + "iciql does not currently implement support for " + fieldClass.getName()).throwError( + throwError)); + } + // number types + if (!fieldClass.equals(jdbcClass)) { + if (Number.class.isAssignableFrom(fieldClass)) { + remarks.add(warn( + table, + col, + format("Precision mismatch: ModelObject={0}, ColumnObject={1}", + fieldClass.getSimpleName(), jdbcClass.getSimpleName()))); + } else { + if (!Date.class.isAssignableFrom(jdbcClass)) { + remarks.add(warn( + table, + col, + format("Object Mismatch: ModelObject={0}, ColumnObject={1}", + fieldClass.getSimpleName(), jdbcClass.getSimpleName()))); + } + } + } + + // string types + if (fieldClass == String.class) { + if ((fieldDef.length != col.size) && (col.size < Integer.MAX_VALUE)) { + remarks.add(warn( + table, + col, + format("{0}.length={1}, ColumnMaxLength={2}", IQColumn.class.getSimpleName(), + fieldDef.length, col.size))); + } + if (fieldDef.length > 0 && !fieldDef.trim) { + remarks.add(consider(table, col, format("{0}.trim=true will prevent IciqlExceptions on" + + " INSERT or UPDATE, but will clip data!", IQColumn.class.getSimpleName()))); + } + } + + // numeric autoIncrement + if (fieldDef.isAutoIncrement != col.isAutoIncrement) { + remarks.add(warn( + table, + col, + format("{0}.autoIncrement={1}" + " while Column autoIncrement={2}", + IQColumn.class.getSimpleName(), fieldDef.isAutoIncrement, col.isAutoIncrement))); + } + // default value + if (!col.isAutoIncrement && !col.isPrimaryKey) { + String defaultValue = null; + if (fieldDef.defaultValue != null && fieldDef.defaultValue instanceof String) { + defaultValue = fieldDef.defaultValue.toString(); + } + // check Model.defaultValue format + if (!ModelUtils.isProperlyFormattedDefaultValue(defaultValue)) { + remarks.add(error( + table, + col, + format("{0}.defaultValue=\"{1}\"" + " is improperly formatted!", + IQColumn.class.getSimpleName(), defaultValue)).throwError(throwError)); + // next field + return; + } + // compare Model.defaultValue to Column.defaultValue + if (isNullOrEmpty(defaultValue) && !isNullOrEmpty(col.defaultValue)) { + // Model.defaultValue is NULL, Column.defaultValue is NOT NULL + remarks.add(warn( + table, + col, + format("{0}.defaultValue=\"\"" + " while column default=\"{1}\"", + IQColumn.class.getSimpleName(), col.defaultValue))); + } else if (!isNullOrEmpty(defaultValue) && isNullOrEmpty(col.defaultValue)) { + // Column.defaultValue is NULL, Model.defaultValue is NOT NULL + remarks.add(warn( + table, + col, + format("{0}.defaultValue=\"{1}\"" + " while column default=\"\"", + IQColumn.class.getSimpleName(), defaultValue))); + } else if (!isNullOrEmpty(defaultValue) && !isNullOrEmpty(col.defaultValue)) { + if (!defaultValue.equals(col.defaultValue)) { + // Model.defaultValue != Column.defaultValue + remarks.add(warn( + table, + col, + format("{0}.defaultValue=\"{1}\"" + " while column default=\"{2}\"", + IQColumn.class.getSimpleName(), defaultValue, col.defaultValue))); + } + } + + // sanity check Model.defaultValue literal value + if (!ModelUtils.isValidDefaultValue(fieldDef.field.getType(), defaultValue)) { + remarks.add(error( + table, + col, + format("{0}.defaultValue=\"{1}\" is invalid!", IQColumn.class.getSimpleName(), + defaultValue))); + } + } + } + + /** + * Represents an index as it exists in the database. + */ + private static class IndexInspector { + + String name; + IndexType type; + private List columns = new ArrayList(); + + public IndexInspector(ResultSet rs) throws SQLException { + name = rs.getString("INDEX_NAME"); + + // determine index type + boolean hash = rs.getInt("TYPE") == DatabaseMetaData.tableIndexHashed; + boolean unique = !rs.getBoolean("NON_UNIQUE"); + + if (!hash && !unique) { + type = IndexType.STANDARD; + } else if (hash && unique) { + type = IndexType.UNIQUE_HASH; + } else if (unique) { + type = IndexType.UNIQUE; + } else if (hash) { + type = IndexType.HASH; + } + columns.add(rs.getString("COLUMN_NAME")); + } + + public void addColumn(ResultSet rs) throws SQLException { + columns.add(rs.getString("COLUMN_NAME")); + } + } + + /** + * Represents a column as it exists in the database. + */ + static class ColumnInspector implements Comparable { + String name; + String type; + int size; + boolean nullable; + Class clazz; + boolean isPrimaryKey; + boolean isAutoIncrement; + String defaultValue; + + public int compareTo(ColumnInspector o) { + if (isPrimaryKey && o.isPrimaryKey) { + // both primary sort by name + return name.compareTo(o.name); + } else if (isPrimaryKey && !o.isPrimaryKey) { + // primary first + return -1; + } else if (!isPrimaryKey && o.isPrimaryKey) { + // primary first + return 1; + } else { + // neither primary, sort by name + return name.compareTo(o.name); + } + } + } + + /** + * Convenience class based on StatementBuilder for creating the annotation + * parameter list. + */ + private static class AnnotationBuilder extends StatementBuilder { + + AnnotationBuilder() { + super(); + } + + void addParameter(String parameter) { + + appendExceptFirst(", "); + append(parameter); + } + + void addParameter(String parameter, T value) { + appendExceptFirst(", "); + if (!StringUtils.isNullOrEmpty(parameter)) { + append(parameter); + append('='); + } + if (value instanceof List) { + append("{ "); + List list = (List) value; + StatementBuilder flat = new StatementBuilder(); + for (Object o : list) { + flat.appendExceptFirst(", "); + if (o instanceof String) { + flat.append('\"'); + } + // TODO escape string + flat.append(o.toString().trim()); + if (o instanceof String) { + flat.append('\"'); + } + } + append(flat); + append(" }"); + } else { + if (value instanceof String) { + append('\"'); + } + // TODO escape + append(value.toString().trim()); + if (value instanceof String) { + append('\"'); + } + } + } + + void addEnum(String parameter, Enum value) { + appendExceptFirst(", "); + if (!StringUtils.isNullOrEmpty(parameter)) { + append(parameter); + append('='); + } + append(value.getClass().getSimpleName() + "." + value.name()); + } + } } \ No newline at end of file diff --git a/src/main/java/com/iciql/TestCondition.java b/src/main/java/com/iciql/TestCondition.java index 010f5a1..3c08eec 100644 --- a/src/main/java/com/iciql/TestCondition.java +++ b/src/main/java/com/iciql/TestCondition.java @@ -21,95 +21,94 @@ import com.iciql.util.Utils; /** * This class represents an incomplete condition. - * - * @param - * the incomplete condition data type + * + * @param the incomplete condition data type */ public class TestCondition { - private A x; + private A x; - public TestCondition(A x) { - this.x = x; - } + public TestCondition(A x) { + this.x = x; + } - public Boolean is(A y) { - Boolean o = Utils.newObject(Boolean.class); - return Db.registerToken(o, new Function("=", x, y) { - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL("("); - query.appendSQL(stat, null, x[0]); - stat.appendSQL(" = "); - query.appendSQL(stat, x[0], x[1]); - stat.appendSQL(")"); - } - }); - } + public Boolean is(A y) { + Boolean o = Utils.newObject(Boolean.class); + return Db.registerToken(o, new Function("=", x, y) { + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL("("); + query.appendSQL(stat, null, x[0]); + stat.appendSQL(" = "); + query.appendSQL(stat, x[0], x[1]); + stat.appendSQL(")"); + } + }); + } - public Boolean exceeds(A y) { - Boolean o = Utils.newObject(Boolean.class); - return Db.registerToken(o, new Function(">", x, y) { - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL("("); - query.appendSQL(stat, null, x[0]); - stat.appendSQL(" > "); - query.appendSQL(stat, x[0], x[1]); - stat.appendSQL(")"); - } - }); - } + public Boolean exceeds(A y) { + Boolean o = Utils.newObject(Boolean.class); + return Db.registerToken(o, new Function(">", x, y) { + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL("("); + query.appendSQL(stat, null, x[0]); + stat.appendSQL(" > "); + query.appendSQL(stat, x[0], x[1]); + stat.appendSQL(")"); + } + }); + } - public Boolean atLeast(A y) { - Boolean o = Utils.newObject(Boolean.class); - return Db.registerToken(o, new Function(">=", x, y) { - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL("("); - query.appendSQL(stat, null, x[0]); - stat.appendSQL(" >= "); - query.appendSQL(stat, x[0], x[1]); - stat.appendSQL(")"); - } - }); - } + public Boolean atLeast(A y) { + Boolean o = Utils.newObject(Boolean.class); + return Db.registerToken(o, new Function(">=", x, y) { + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL("("); + query.appendSQL(stat, null, x[0]); + stat.appendSQL(" >= "); + query.appendSQL(stat, x[0], x[1]); + stat.appendSQL(")"); + } + }); + } - public Boolean lessThan(A y) { - Boolean o = Utils.newObject(Boolean.class); - return Db.registerToken(o, new Function("<", x, y) { - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL("("); - query.appendSQL(stat, null, x[0]); - stat.appendSQL(" < "); - query.appendSQL(stat, x[0], x[1]); - stat.appendSQL(")"); - } - }); - } + public Boolean lessThan(A y) { + Boolean o = Utils.newObject(Boolean.class); + return Db.registerToken(o, new Function("<", x, y) { + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL("("); + query.appendSQL(stat, null, x[0]); + stat.appendSQL(" < "); + query.appendSQL(stat, x[0], x[1]); + stat.appendSQL(")"); + } + }); + } - public Boolean atMost(A y) { - Boolean o = Utils.newObject(Boolean.class); - return Db.registerToken(o, new Function("<=", x, y) { - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL("("); - query.appendSQL(stat, null, x[0]); - stat.appendSQL(" <= "); - query.appendSQL(stat, x[0], x[1]); - stat.appendSQL(")"); - } - }); - } + public Boolean atMost(A y) { + Boolean o = Utils.newObject(Boolean.class); + return Db.registerToken(o, new Function("<=", x, y) { + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL("("); + query.appendSQL(stat, null, x[0]); + stat.appendSQL(" <= "); + query.appendSQL(stat, x[0], x[1]); + stat.appendSQL(")"); + } + }); + } - public Boolean like(A pattern) { - Boolean o = Utils.newObject(Boolean.class); - return Db.registerToken(o, new Function("LIKE", x, pattern) { - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL("("); - query.appendSQL(stat, null, x[0]); - stat.appendSQL(" LIKE "); - query.appendSQL(stat, x[0], x[1]); - stat.appendSQL(")"); - } - }); - } + public Boolean like(A pattern) { + Boolean o = Utils.newObject(Boolean.class); + return Db.registerToken(o, new Function("LIKE", x, pattern) { + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL("("); + query.appendSQL(stat, null, x[0]); + stat.appendSQL(" LIKE "); + query.appendSQL(stat, x[0], x[1]); + stat.appendSQL(")"); + } + }); + } } diff --git a/src/main/java/com/iciql/Token.java b/src/main/java/com/iciql/Token.java index cc2203c..acc25c4 100644 --- a/src/main/java/com/iciql/Token.java +++ b/src/main/java/com/iciql/Token.java @@ -21,15 +21,13 @@ package com.iciql; * Classes implementing this interface can be used as a token in a statement. */ public interface Token { - /** - * Append the SQL to the given statement using the given query. - * - * @param stat - * the statement to append the SQL to - * @param query - * the query to use - */ + /** + * Append the SQL to the given statement using the given query. + * + * @param stat the statement to append the SQL to + * @param query the query to use + */ - void appendSQL(SQLStatement stat, Query query); + void appendSQL(SQLStatement stat, Query query); } diff --git a/src/main/java/com/iciql/UpdateColumn.java b/src/main/java/com/iciql/UpdateColumn.java index 1eaf14c..970ec61 100644 --- a/src/main/java/com/iciql/UpdateColumn.java +++ b/src/main/java/com/iciql/UpdateColumn.java @@ -23,13 +23,12 @@ package com.iciql; */ public interface UpdateColumn { - /** - * Append the SQL to the given statement using the given query. - * - * @param stat - * the statement to append the SQL to - */ + /** + * Append the SQL to the given statement using the given query. + * + * @param stat the statement to append the SQL to + */ - void appendSQL(SQLStatement stat); + void appendSQL(SQLStatement stat); } diff --git a/src/main/java/com/iciql/UpdateColumnIncrement.java b/src/main/java/com/iciql/UpdateColumnIncrement.java index 143ce48..e8f96e3 100644 --- a/src/main/java/com/iciql/UpdateColumnIncrement.java +++ b/src/main/java/com/iciql/UpdateColumnIncrement.java @@ -19,37 +19,35 @@ package com.iciql; /** * This class represents "SET column = (column + x)" in an UPDATE statement. - * - * @param - * the query type - * @param - * the new value data type + * + * @param the query type + * @param the new value data type */ public class UpdateColumnIncrement implements UpdateColumn { - private Query query; - private A x; - private A y; - - UpdateColumnIncrement(Query query, A x) { - this.query = query; - this.x = x; - } - - public Query by(A y) { - query.addUpdateColumnDeclaration(this); - this.y = y; - return query; - } - - public void appendSQL(SQLStatement stat) { - query.appendSQL(stat, null, x); - stat.appendSQL("=("); - query.appendSQL(stat, null, x); - stat.appendSQL("+"); - query.appendSQL(stat, x, y); - stat.appendSQL(")"); - } + private Query query; + private A x; + private A y; + + UpdateColumnIncrement(Query query, A x) { + this.query = query; + this.x = x; + } + + public Query by(A y) { + query.addUpdateColumnDeclaration(this); + this.y = y; + return query; + } + + public void appendSQL(SQLStatement stat) { + query.appendSQL(stat, null, x); + stat.appendSQL("=("); + query.appendSQL(stat, null, x); + stat.appendSQL("+"); + query.appendSQL(stat, x, y); + stat.appendSQL(")"); + } } diff --git a/src/main/java/com/iciql/UpdateColumnSet.java b/src/main/java/com/iciql/UpdateColumnSet.java index a961480..7ae74a0 100644 --- a/src/main/java/com/iciql/UpdateColumnSet.java +++ b/src/main/java/com/iciql/UpdateColumnSet.java @@ -19,45 +19,43 @@ package com.iciql; /** * This class represents "SET column = value" in an UPDATE statement. - * - * @param - * the query type - * @param - * the new value data type + * + * @param the query type + * @param the new value data type */ public class UpdateColumnSet implements UpdateColumn { - private Query query; - private A x; - private A y; - private boolean isParameter; - - UpdateColumnSet(Query query, A x) { - this.query = query; - this.x = x; - } - - public Query to(A y) { - query.addUpdateColumnDeclaration(this); - this.y = y; - return query; - } - - public Query toParameter() { - query.addUpdateColumnDeclaration(this); - isParameter = true; - return query; - } - - public void appendSQL(SQLStatement stat) { - query.appendSQL(stat, null, x); - stat.appendSQL(" = "); - if (isParameter) { - query.appendSQL(stat, x, RuntimeParameter.PARAMETER); - } else { - query.appendSQL(stat, x, y); - } - } + private Query query; + private A x; + private A y; + private boolean isParameter; + + UpdateColumnSet(Query query, A x) { + this.query = query; + this.x = x; + } + + public Query to(A y) { + query.addUpdateColumnDeclaration(this); + this.y = y; + return query; + } + + public Query toParameter() { + query.addUpdateColumnDeclaration(this); + isParameter = true; + return query; + } + + public void appendSQL(SQLStatement stat) { + query.appendSQL(stat, null, x); + stat.appendSQL(" = "); + if (isParameter) { + query.appendSQL(stat, x, RuntimeParameter.PARAMETER); + } else { + query.appendSQL(stat, x, y); + } + } } diff --git a/src/main/java/com/iciql/ValidationRemark.java b/src/main/java/com/iciql/ValidationRemark.java index 33320ab..74b334e 100644 --- a/src/main/java/com/iciql/ValidationRemark.java +++ b/src/main/java/com/iciql/ValidationRemark.java @@ -28,100 +28,100 @@ import com.iciql.util.StringUtils; */ public class ValidationRemark { - /** - * The validation message level. - */ - public static enum Level { - CONSIDER, WARN, ERROR; - } - - public final Level level; - public final String table; - public final String fieldType; - public final String fieldName; - public final String message; - - private ValidationRemark(Level level, String table, String type, String message) { - this.level = level; - this.table = table; - this.fieldType = type; - this.fieldName = ""; - this.message = message; - } - - private ValidationRemark(Level level, String table, FieldDefinition field, String message) { - this.level = level; - this.table = table; - this.fieldType = field.dataType; - this.fieldName = field.columnName; - this.message = message; - } - - private ValidationRemark(Level level, String table, ColumnInspector col, String message) { - this.level = level; - this.table = table; - this.fieldType = col.type; - this.fieldName = col.name; - this.message = message; - } - - public static ValidationRemark consider(String table, String type, String message) { - return new ValidationRemark(Level.CONSIDER, table, type, message); - } - - public static ValidationRemark consider(String table, ColumnInspector col, String message) { - return new ValidationRemark(Level.CONSIDER, table, col, message); - } - - public static ValidationRemark warn(String table, ColumnInspector col, String message) { - return new ValidationRemark(Level.WARN, table, col, message); - } - - public static ValidationRemark warn(String table, String type, String message) { - return new ValidationRemark(Level.WARN, table, type, message); - } - - public static ValidationRemark error(String table, ColumnInspector col, String message) { - return new ValidationRemark(Level.ERROR, table, col, message); - } - - public static ValidationRemark error(String table, String type, String message) { - return new ValidationRemark(Level.ERROR, table, type, message); - } - - public static ValidationRemark error(String table, FieldDefinition field, String message) { - return new ValidationRemark(Level.ERROR, table, field, message); - } - - public ValidationRemark throwError(boolean throwOnError) { - if (throwOnError && isError()) { - throw new IciqlException(toString()); - } - return this; - } - - public boolean isError() { - return level.equals(Level.ERROR); - } - - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append(StringUtils.pad(level.name(), 9, " ", true)); - sb.append(StringUtils.pad(table, 25, " ", true)); - sb.append(StringUtils.pad(fieldName, 20, " ", true)); - sb.append(' '); - sb.append(message); - return sb.toString(); - } - - public String toCSVString() { - StringBuilder sb = new StringBuilder(); - sb.append(level.name()).append(','); - sb.append(table).append(','); - sb.append(fieldType).append(','); - sb.append(fieldName).append(','); - sb.append(message); - return sb.toString(); - } + /** + * The validation message level. + */ + public static enum Level { + CONSIDER, WARN, ERROR; + } + + public final Level level; + public final String table; + public final String fieldType; + public final String fieldName; + public final String message; + + private ValidationRemark(Level level, String table, String type, String message) { + this.level = level; + this.table = table; + this.fieldType = type; + this.fieldName = ""; + this.message = message; + } + + private ValidationRemark(Level level, String table, FieldDefinition field, String message) { + this.level = level; + this.table = table; + this.fieldType = field.dataType; + this.fieldName = field.columnName; + this.message = message; + } + + private ValidationRemark(Level level, String table, ColumnInspector col, String message) { + this.level = level; + this.table = table; + this.fieldType = col.type; + this.fieldName = col.name; + this.message = message; + } + + public static ValidationRemark consider(String table, String type, String message) { + return new ValidationRemark(Level.CONSIDER, table, type, message); + } + + public static ValidationRemark consider(String table, ColumnInspector col, String message) { + return new ValidationRemark(Level.CONSIDER, table, col, message); + } + + public static ValidationRemark warn(String table, ColumnInspector col, String message) { + return new ValidationRemark(Level.WARN, table, col, message); + } + + public static ValidationRemark warn(String table, String type, String message) { + return new ValidationRemark(Level.WARN, table, type, message); + } + + public static ValidationRemark error(String table, ColumnInspector col, String message) { + return new ValidationRemark(Level.ERROR, table, col, message); + } + + public static ValidationRemark error(String table, String type, String message) { + return new ValidationRemark(Level.ERROR, table, type, message); + } + + public static ValidationRemark error(String table, FieldDefinition field, String message) { + return new ValidationRemark(Level.ERROR, table, field, message); + } + + public ValidationRemark throwError(boolean throwOnError) { + if (throwOnError && isError()) { + throw new IciqlException(toString()); + } + return this; + } + + public boolean isError() { + return level.equals(Level.ERROR); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(StringUtils.pad(level.name(), 9, " ", true)); + sb.append(StringUtils.pad(table, 25, " ", true)); + sb.append(StringUtils.pad(fieldName, 20, " ", true)); + sb.append(' '); + sb.append(message); + return sb.toString(); + } + + public String toCSVString() { + StringBuilder sb = new StringBuilder(); + sb.append(level.name()).append(','); + sb.append(table).append(','); + sb.append(fieldType).append(','); + sb.append(fieldName).append(','); + sb.append(message); + return sb.toString(); + } } diff --git a/src/main/java/com/iciql/adapter/GsonTypeAdapter.java b/src/main/java/com/iciql/adapter/GsonTypeAdapter.java index 8d23cfd..83cbe51 100644 --- a/src/main/java/com/iciql/adapter/GsonTypeAdapter.java +++ b/src/main/java/com/iciql/adapter/GsonTypeAdapter.java @@ -26,13 +26,13 @@ import com.iciql.Iciql.Mode; *

* You use this by creating a subclass which defines your object class. *

- * + *

*

  * public class CustomObjectAdapter extends GsonTypeAdapter<CustomObject> {
  *
  * 	public Class<CustomObject> getJavaType() {
  * 		return CustomObject.class;
- * 	}
+ *    }
  * }
  * 
* @@ -40,32 +40,32 @@ import com.iciql.Iciql.Mode; */ public abstract class GsonTypeAdapter implements DataTypeAdapter { - protected Mode mode; + protected Mode mode; - protected Gson gson() { - return new GsonBuilder().create(); - } + protected Gson gson() { + return new GsonBuilder().create(); + } - @Override - public void setMode(Mode mode) { - this.mode = mode; - } + @Override + public void setMode(Mode mode) { + this.mode = mode; + } - @Override - public String getDataType() { - return "TEXT"; - } + @Override + public String getDataType() { + return "TEXT"; + } - @Override - public Object serialize(T value) { - return gson().toJson(value); - } + @Override + public Object serialize(T value) { + return gson().toJson(value); + } - @Override - public T deserialize(Object value) { - String json = value.toString(); - Gson gson = gson(); - T t = gson.fromJson(json, getJavaType()); - return t; - } + @Override + public T deserialize(Object value) { + String json = value.toString(); + Gson gson = gson(); + T t = gson.fromJson(json, getJavaType()); + return t; + } } diff --git a/src/main/java/com/iciql/adapter/JavaSerializationTypeAdapter.java b/src/main/java/com/iciql/adapter/JavaSerializationTypeAdapter.java index df24495..bebb8fa 100644 --- a/src/main/java/com/iciql/adapter/JavaSerializationTypeAdapter.java +++ b/src/main/java/com/iciql/adapter/JavaSerializationTypeAdapter.java @@ -16,6 +16,10 @@ package com.iciql.adapter; +import com.iciql.Iciql.DataTypeAdapter; +import com.iciql.Iciql.Mode; +import com.iciql.IciqlException; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -25,10 +29,6 @@ import java.io.ObjectOutputStream; import java.sql.Blob; import java.sql.SQLException; -import com.iciql.Iciql.DataTypeAdapter; -import com.iciql.Iciql.Mode; -import com.iciql.IciqlException; - /** * Base class for inserting/retrieving a Java Object as a BLOB field using Java Serialization. *

You use this by creating a subclass which defines your object class.

@@ -40,68 +40,69 @@ import com.iciql.IciqlException; * } * } * + * * @param */ public abstract class JavaSerializationTypeAdapter implements DataTypeAdapter { - protected Mode mode; + protected Mode mode; - @Override - public void setMode(Mode mode) { - this.mode = mode; - } + @Override + public void setMode(Mode mode) { + this.mode = mode; + } - @Override - public final String getDataType() { - return "BLOB"; - } + @Override + public final String getDataType() { + return "BLOB"; + } - @Override - public final Object serialize(T value) { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - try { - new ObjectOutputStream(os).writeObject(value); - return os.toByteArray(); - } catch (IOException e) { - throw new IciqlException(e); - } finally { - try { - os.close(); - } catch (IOException e) { - throw new IciqlException (e); - } - } - } + @Override + public final Object serialize(T value) { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + try { + new ObjectOutputStream(os).writeObject(value); + return os.toByteArray(); + } catch (IOException e) { + throw new IciqlException(e); + } finally { + try { + os.close(); + } catch (IOException e) { + throw new IciqlException(e); + } + } + } - @SuppressWarnings("unchecked") - @Override - public final T deserialize(Object value) { - InputStream is = null; - if (value instanceof Blob) { - Blob blob = (Blob) value; - try { - is = blob.getBinaryStream(); - } catch (SQLException e) { - throw new IciqlException(e); - } - } else if (value instanceof byte[]) { - byte [] bytes = (byte []) value; - is = new ByteArrayInputStream(bytes); - } + @SuppressWarnings("unchecked") + @Override + public final T deserialize(Object value) { + InputStream is = null; + if (value instanceof Blob) { + Blob blob = (Blob) value; + try { + is = blob.getBinaryStream(); + } catch (SQLException e) { + throw new IciqlException(e); + } + } else if (value instanceof byte[]) { + byte[] bytes = (byte[]) value; + is = new ByteArrayInputStream(bytes); + } - try { - T object = (T) new ObjectInputStream(is).readObject(); - return object; - } catch (Exception e) { - throw new IciqlException(e); - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - throw new IciqlException (e); - } - } - } - } + try { + T object = (T) new ObjectInputStream(is).readObject(); + return object; + } catch (Exception e) { + throw new IciqlException(e); + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException e) { + throw new IciqlException(e); + } + } + } + } } diff --git a/src/main/java/com/iciql/adapter/SnakeYamlTypeAdapter.java b/src/main/java/com/iciql/adapter/SnakeYamlTypeAdapter.java index da3b20b..f96afcb 100644 --- a/src/main/java/com/iciql/adapter/SnakeYamlTypeAdapter.java +++ b/src/main/java/com/iciql/adapter/SnakeYamlTypeAdapter.java @@ -16,48 +16,47 @@ package com.iciql.adapter; +import com.iciql.Iciql.DataTypeAdapter; +import com.iciql.Iciql.Mode; import org.yaml.snakeyaml.DumperOptions.FlowStyle; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.nodes.Tag; -import com.iciql.Iciql.DataTypeAdapter; -import com.iciql.Iciql.Mode; - /** * Base class for inserting/retrieving a Java Object (de)serialized as YAML using SnakeYaml. */ public abstract class SnakeYamlTypeAdapter implements DataTypeAdapter { - protected Mode mode; - - protected Yaml yaml() { - return new Yaml(); - } - - @Override - public void setMode(Mode mode) { - this.mode = mode; - } - - @Override - public String getDataType() { - return "TEXT"; - } - - @Override - public abstract Class getJavaType(); - - @Override - public Object serialize(Object value) { - return yaml().dumpAs(value, Tag.MAP, FlowStyle.BLOCK); - } - - @Override - public T deserialize(Object value) { - String yaml = value.toString(); - Yaml processor = yaml(); - T t = processor.loadAs(yaml, getJavaType()); - return t; - } + protected Mode mode; + + protected Yaml yaml() { + return new Yaml(); + } + + @Override + public void setMode(Mode mode) { + this.mode = mode; + } + + @Override + public String getDataType() { + return "TEXT"; + } + + @Override + public abstract Class getJavaType(); + + @Override + public Object serialize(Object value) { + return yaml().dumpAs(value, Tag.MAP, FlowStyle.BLOCK); + } + + @Override + public T deserialize(Object value) { + String yaml = value.toString(); + Yaml processor = yaml(); + T t = processor.loadAs(yaml, getJavaType()); + return t; + } } diff --git a/src/main/java/com/iciql/adapter/XStreamTypeAdapter.java b/src/main/java/com/iciql/adapter/XStreamTypeAdapter.java index a554724..224e7bb 100644 --- a/src/main/java/com/iciql/adapter/XStreamTypeAdapter.java +++ b/src/main/java/com/iciql/adapter/XStreamTypeAdapter.java @@ -25,38 +25,38 @@ import com.thoughtworks.xstream.XStream; */ public class XStreamTypeAdapter implements DataTypeAdapter { - protected Mode mode; - - protected XStream xstream() { - return new XStream(); - } - - @Override - public void setMode(Mode mode) { - this.mode = mode; - } - - @Override - public String getDataType() { - return "TEXT"; - } - - @Override - public Class getJavaType() { - return Object.class; - } - - @Override - public Object serialize(Object value) { - return xstream().toXML(value); - } - - @Override - public Object deserialize(Object value) { - String xml = value.toString(); - XStream xstream = xstream(); - Object t = xstream.fromXML(xml); - return t; - } + protected Mode mode; + + protected XStream xstream() { + return new XStream(); + } + + @Override + public void setMode(Mode mode) { + this.mode = mode; + } + + @Override + public String getDataType() { + return "TEXT"; + } + + @Override + public Class getJavaType() { + return Object.class; + } + + @Override + public Object serialize(Object value) { + return xstream().toXML(value); + } + + @Override + public Object deserialize(Object value) { + String xml = value.toString(); + XStream xstream = xstream(); + Object t = xstream.fromXML(xml); + return t; + } } diff --git a/src/main/java/com/iciql/adapter/postgresql/JsonObjectAdapter.java b/src/main/java/com/iciql/adapter/postgresql/JsonObjectAdapter.java index 118cfc1..91f989a 100644 --- a/src/main/java/com/iciql/adapter/postgresql/JsonObjectAdapter.java +++ b/src/main/java/com/iciql/adapter/postgresql/JsonObjectAdapter.java @@ -15,38 +15,36 @@ */ package com.iciql.adapter.postgresql; -import java.sql.SQLException; - +import com.iciql.adapter.GsonTypeAdapter; import org.postgresql.util.PGobject; -import com.iciql.adapter.GsonTypeAdapter; +import java.sql.SQLException; /** * Postgres JSON data type adapter maps a JSON column to a domain object using * Google GSON. * - * @author James Moger - * * @param + * @author James Moger */ public abstract class JsonObjectAdapter extends GsonTypeAdapter { - @Override - public String getDataType() { - return "json"; - } + @Override + public String getDataType() { + return "json"; + } - @Override - public Object serialize(T value) { + @Override + public Object serialize(T value) { - String json = gson().toJson(value); - PGobject pg = new PGobject(); - pg.setType(getDataType()); - try { - pg.setValue(json); - } catch (SQLException e) { - // not thrown on base PGobject - } - return pg; - } + String json = gson().toJson(value); + PGobject pg = new PGobject(); + pg.setType(getDataType()); + try { + pg.setValue(json); + } catch (SQLException e) { + // not thrown on base PGobject + } + return pg; + } } diff --git a/src/main/java/com/iciql/adapter/postgresql/JsonStringAdapter.java b/src/main/java/com/iciql/adapter/postgresql/JsonStringAdapter.java index 01d2834..5295d52 100644 --- a/src/main/java/com/iciql/adapter/postgresql/JsonStringAdapter.java +++ b/src/main/java/com/iciql/adapter/postgresql/JsonStringAdapter.java @@ -15,49 +15,48 @@ */ package com.iciql.adapter.postgresql; -import java.sql.SQLException; - -import org.postgresql.util.PGobject; - import com.iciql.Iciql.DataTypeAdapter; import com.iciql.Iciql.Mode; +import org.postgresql.util.PGobject; + +import java.sql.SQLException; /** * Handles transforming raw strings to/from the Postgres JSON data type. */ public class JsonStringAdapter implements DataTypeAdapter { - protected Mode mode; - - @Override - public void setMode(Mode mode) { - this.mode = mode; - } - - @Override - public String getDataType() { - return "json"; - } - - @Override - public Class getJavaType() { - return String.class; - } - - @Override - public Object serialize(String value) { - PGobject pg = new PGobject(); - pg.setType(getDataType()); - try { - pg.setValue(value); - } catch (SQLException e) { - // not thrown on base PGobject - } - return pg; - } - - @Override - public String deserialize(Object value) { - return value.toString(); - } + protected Mode mode; + + @Override + public void setMode(Mode mode) { + this.mode = mode; + } + + @Override + public String getDataType() { + return "json"; + } + + @Override + public Class getJavaType() { + return String.class; + } + + @Override + public Object serialize(String value) { + PGobject pg = new PGobject(); + pg.setType(getDataType()); + try { + pg.setValue(value); + } catch (SQLException e) { + // not thrown on base PGobject + } + return pg; + } + + @Override + public String deserialize(Object value) { + return value.toString(); + } } \ No newline at end of file diff --git a/src/main/java/com/iciql/adapter/postgresql/JsonbObjectAdapter.java b/src/main/java/com/iciql/adapter/postgresql/JsonbObjectAdapter.java index 6d8ad2c..947778b 100644 --- a/src/main/java/com/iciql/adapter/postgresql/JsonbObjectAdapter.java +++ b/src/main/java/com/iciql/adapter/postgresql/JsonbObjectAdapter.java @@ -15,38 +15,36 @@ */ package com.iciql.adapter.postgresql; -import java.sql.SQLException; - +import com.iciql.adapter.GsonTypeAdapter; import org.postgresql.util.PGobject; -import com.iciql.adapter.GsonTypeAdapter; +import java.sql.SQLException; /** * Postgres JSONB data type adapter maps a JSONB column to a domain object using * Google GSON. * - * @author James Moger - * * @param + * @author James Moger */ public abstract class JsonbObjectAdapter extends GsonTypeAdapter { - @Override - public String getDataType() { - return "jsonb"; - } + @Override + public String getDataType() { + return "jsonb"; + } - @Override - public Object serialize(T value) { + @Override + public Object serialize(T value) { - String json = gson().toJson(value); - PGobject pg = new PGobject(); - pg.setType(getDataType()); - try { - pg.setValue(json); - } catch (SQLException e) { - // not thrown on base PGobject - } - return pg; - } + String json = gson().toJson(value); + PGobject pg = new PGobject(); + pg.setType(getDataType()); + try { + pg.setValue(json); + } catch (SQLException e) { + // not thrown on base PGobject + } + return pg; + } } diff --git a/src/main/java/com/iciql/adapter/postgresql/JsonbStringAdapter.java b/src/main/java/com/iciql/adapter/postgresql/JsonbStringAdapter.java index 9d7388b..88d69c6 100644 --- a/src/main/java/com/iciql/adapter/postgresql/JsonbStringAdapter.java +++ b/src/main/java/com/iciql/adapter/postgresql/JsonbStringAdapter.java @@ -15,49 +15,48 @@ */ package com.iciql.adapter.postgresql; -import java.sql.SQLException; - -import org.postgresql.util.PGobject; - import com.iciql.Iciql.DataTypeAdapter; import com.iciql.Iciql.Mode; +import org.postgresql.util.PGobject; + +import java.sql.SQLException; /** * Handles transforming raw strings to/from the Postgres JSONB data type. */ public class JsonbStringAdapter implements DataTypeAdapter { - protected Mode mode; - - @Override - public void setMode(Mode mode) { - this.mode = mode; - } - - @Override - public String getDataType() { - return "jsonb"; - } - - @Override - public Class getJavaType() { - return String.class; - } - - @Override - public Object serialize(String value) { - PGobject pg = new PGobject(); - pg.setType(getDataType()); - try { - pg.setValue(value); - } catch (SQLException e) { - // not thrown on base PGobject - } - return pg; - } - - @Override - public String deserialize(Object value) { - return value.toString(); - } + protected Mode mode; + + @Override + public void setMode(Mode mode) { + this.mode = mode; + } + + @Override + public String getDataType() { + return "jsonb"; + } + + @Override + public Class getJavaType() { + return String.class; + } + + @Override + public Object serialize(String value) { + PGobject pg = new PGobject(); + pg.setType(getDataType()); + try { + pg.setValue(value); + } catch (SQLException e) { + // not thrown on base PGobject + } + return pg; + } + + @Override + public String deserialize(Object value) { + return value.toString(); + } } \ No newline at end of file diff --git a/src/main/java/com/iciql/adapter/postgresql/XmlObjectAdapter.java b/src/main/java/com/iciql/adapter/postgresql/XmlObjectAdapter.java index eba7c06..f5160d3 100644 --- a/src/main/java/com/iciql/adapter/postgresql/XmlObjectAdapter.java +++ b/src/main/java/com/iciql/adapter/postgresql/XmlObjectAdapter.java @@ -23,13 +23,12 @@ import com.iciql.adapter.XStreamTypeAdapter; * XStream. * * @author James Moger - * */ public abstract class XmlObjectAdapter extends XStreamTypeAdapter { - @Override - public String getDataType() { - return "xml"; - } + @Override + public String getDataType() { + return "xml"; + } } diff --git a/src/main/java/com/iciql/adapter/postgresql/XmlStringAdapter.java b/src/main/java/com/iciql/adapter/postgresql/XmlStringAdapter.java index 0aea77e..a795e4f 100644 --- a/src/main/java/com/iciql/adapter/postgresql/XmlStringAdapter.java +++ b/src/main/java/com/iciql/adapter/postgresql/XmlStringAdapter.java @@ -15,49 +15,48 @@ */ package com.iciql.adapter.postgresql; -import java.sql.SQLException; - -import org.postgresql.util.PGobject; - import com.iciql.Iciql.DataTypeAdapter; import com.iciql.Iciql.Mode; +import org.postgresql.util.PGobject; + +import java.sql.SQLException; /** * Handles transforming raw strings to/from the Postgres XML data type. */ public class XmlStringAdapter implements DataTypeAdapter { - protected Mode mode; - - @Override - public void setMode(Mode mode) { - this.mode = mode; - } - - @Override - public String getDataType() { - return "xml"; - } - - @Override - public Class getJavaType() { - return String.class; - } - - @Override - public Object serialize(String value) { - PGobject pg = new PGobject(); - pg.setType(getDataType()); - try { - pg.setValue(value); - } catch (SQLException e) { - // not thrown on base PGobject - } - return pg; - } - - @Override - public String deserialize(Object value) { - return value.toString(); - } + protected Mode mode; + + @Override + public void setMode(Mode mode) { + this.mode = mode; + } + + @Override + public String getDataType() { + return "xml"; + } + + @Override + public Class getJavaType() { + return String.class; + } + + @Override + public Object serialize(String value) { + PGobject pg = new PGobject(); + pg.setType(getDataType()); + try { + pg.setValue(value); + } catch (SQLException e) { + // not thrown on base PGobject + } + return pg; + } + + @Override + public String deserialize(Object value) { + return value.toString(); + } } \ No newline at end of file diff --git a/src/main/java/com/iciql/bytecode/And.java b/src/main/java/com/iciql/bytecode/And.java index 808a9c1..0b04525 100644 --- a/src/main/java/com/iciql/bytecode/And.java +++ b/src/main/java/com/iciql/bytecode/And.java @@ -26,21 +26,21 @@ import com.iciql.Token; */ public class And implements Token { - private final Token left, right; - - private And(Token left, Token right) { - this.left = left; - this.right = right; - } - - static And get(Token left, Token right) { - return new And(left, right); - } - - public void appendSQL(SQLStatement stat, Query query) { - left.appendSQL(stat, query); - stat.appendSQL(" AND "); - right.appendSQL(stat, query); - } + private final Token left, right; + + private And(Token left, Token right) { + this.left = left; + this.right = right; + } + + static And get(Token left, Token right) { + return new And(left, right); + } + + public void appendSQL(SQLStatement stat, Query query) { + left.appendSQL(stat, query); + stat.appendSQL(" AND "); + right.appendSQL(stat, query); + } } diff --git a/src/main/java/com/iciql/bytecode/ArrayGet.java b/src/main/java/com/iciql/bytecode/ArrayGet.java index 29516c2..5ea2b74 100644 --- a/src/main/java/com/iciql/bytecode/ArrayGet.java +++ b/src/main/java/com/iciql/bytecode/ArrayGet.java @@ -26,24 +26,24 @@ import com.iciql.Token; */ public class ArrayGet implements Token { - private final Token variable; - private final Token index; - - private ArrayGet(Token variable, Token index) { - this.variable = variable; - this.index = index; - } - - static ArrayGet get(Token variable, Token index) { - return new ArrayGet(variable, index); - } - - public void appendSQL(SQLStatement stat, Query query) { - // untested - variable.appendSQL(stat, query); - stat.appendSQL("["); - index.appendSQL(stat, query); - stat.appendSQL("]"); - } + private final Token variable; + private final Token index; + + private ArrayGet(Token variable, Token index) { + this.variable = variable; + this.index = index; + } + + static ArrayGet get(Token variable, Token index) { + return new ArrayGet(variable, index); + } + + public void appendSQL(SQLStatement stat, Query query) { + // untested + variable.appendSQL(stat, query); + stat.appendSQL("["); + index.appendSQL(stat, query); + stat.appendSQL("]"); + } } diff --git a/src/main/java/com/iciql/bytecode/CaseWhen.java b/src/main/java/com/iciql/bytecode/CaseWhen.java index 2a1d69e..bdf7fc5 100644 --- a/src/main/java/com/iciql/bytecode/CaseWhen.java +++ b/src/main/java/com/iciql/bytecode/CaseWhen.java @@ -26,37 +26,37 @@ import com.iciql.Token; */ public class CaseWhen implements Token { - private final Token condition, ifTrue, ifFalse; - - private CaseWhen(Token condition, Token ifTrue, Token ifFalse) { - this.condition = condition; - this.ifTrue = ifTrue; - this.ifFalse = ifFalse; - } - - static Token get(Token condition, Token ifTrue, Token ifFalse) { - if ("0".equals(ifTrue.toString()) && "1".equals(ifFalse.toString())) { - return Not.get(condition); - } else if ("1".equals(ifTrue.toString()) && "0".equals(ifFalse.toString())) { - return condition; - } else if ("0".equals(ifTrue.toString())) { - return And.get(Not.get(condition), ifFalse); - } - return new CaseWhen(condition, ifTrue, ifFalse); - } - - public String toString() { - return "CASEWHEN(" + condition + ", " + ifTrue + ", " + ifFalse + ")"; - } - - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL("CASEWHEN "); - condition.appendSQL(stat, query); - stat.appendSQL(" THEN "); - ifTrue.appendSQL(stat, query); - stat.appendSQL(" ELSE "); - ifFalse.appendSQL(stat, query); - stat.appendSQL(" END"); - } + private final Token condition, ifTrue, ifFalse; + + private CaseWhen(Token condition, Token ifTrue, Token ifFalse) { + this.condition = condition; + this.ifTrue = ifTrue; + this.ifFalse = ifFalse; + } + + static Token get(Token condition, Token ifTrue, Token ifFalse) { + if ("0".equals(ifTrue.toString()) && "1".equals(ifFalse.toString())) { + return Not.get(condition); + } else if ("1".equals(ifTrue.toString()) && "0".equals(ifFalse.toString())) { + return condition; + } else if ("0".equals(ifTrue.toString())) { + return And.get(Not.get(condition), ifFalse); + } + return new CaseWhen(condition, ifTrue, ifFalse); + } + + public String toString() { + return "CASEWHEN(" + condition + ", " + ifTrue + ", " + ifFalse + ")"; + } + + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL("CASEWHEN "); + condition.appendSQL(stat, query); + stat.appendSQL(" THEN "); + ifTrue.appendSQL(stat, query); + stat.appendSQL(" ELSE "); + ifFalse.appendSQL(stat, query); + stat.appendSQL(" END"); + } } diff --git a/src/main/java/com/iciql/bytecode/ClassReader.java b/src/main/java/com/iciql/bytecode/ClassReader.java index 38fd2f5..52c71b4 100644 --- a/src/main/java/com/iciql/bytecode/ClassReader.java +++ b/src/main/java/com/iciql/bytecode/ClassReader.java @@ -17,6 +17,9 @@ package com.iciql.bytecode; +import com.iciql.IciqlException; +import com.iciql.Token; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -25,1433 +28,1430 @@ import java.util.HashMap; import java.util.Map; import java.util.Stack; -import com.iciql.IciqlException; -import com.iciql.Token; - /** * This class converts a method to a SQL Token by interpreting (decompiling) the * bytecode of the class. */ public class ClassReader { - private static final boolean DEBUG = false; + private static final boolean DEBUG = false; - private byte[] data; - private int pos; - private Constant[] constantPool; - private int startByteCode; - private String methodName; + private byte[] data; + private int pos; + private Constant[] constantPool; + private int startByteCode; + private String methodName; - private String convertMethodName; - private Token result; - private Stack stack = new Stack(); - private ArrayList variables = new ArrayList(); - private boolean endOfMethod; - private boolean condition; - private int nextPc; - private Map fieldMap = new HashMap(); + private String convertMethodName; + private Token result; + private Stack stack = new Stack(); + private ArrayList variables = new ArrayList(); + private boolean endOfMethod; + private boolean condition; + private int nextPc; + private Map fieldMap = new HashMap(); - private static void debug(String s) { - if (DEBUG) { - System.out.println(s); - } - } + private static void debug(String s) { + if (DEBUG) { + System.out.println(s); + } + } - public Token decompile(Object instance, Map fields, String method) { - this.fieldMap = fields; - this.convertMethodName = method; - Class clazz = instance.getClass(); - String className = clazz.getName(); - debug("class name " + className); - ByteArrayOutputStream buff = new ByteArrayOutputStream(); - try { - InputStream in = clazz.getClassLoader().getResource(className.replace('.', '/') + ".class") - .openStream(); - while (true) { - int x = in.read(); - if (x < 0) { - break; - } - buff.write(x); - } - } catch (IOException e) { - throw new IciqlException("Could not read class bytecode", e); - } - data = buff.toByteArray(); - int header = readInt(); - debug("header: " + Integer.toHexString(header)); - int minorVersion = readShort(); - int majorVersion = readShort(); - debug("version: " + majorVersion + "." + minorVersion); - int constantPoolCount = readShort(); - constantPool = new Constant[constantPoolCount]; - for (int i = 1; i < constantPoolCount; i++) { - int type = readByte(); - switch (type) { - case 1: - constantPool[i] = ConstantString.get(readString()); - break; - case 3: { - int x = readInt(); - constantPool[i] = ConstantNumber.get(x); - break; - } - case 4: { - int x = readInt(); - constantPool[i] = ConstantNumber.get("" + Float.intBitsToFloat(x), x, Constant.Type.FLOAT); - break; - } - case 5: { - long x = readLong(); - constantPool[i] = ConstantNumber.get(x); - i++; - break; - } - case 6: { - long x = readLong(); - constantPool[i] = ConstantNumber - .get("" + Double.longBitsToDouble(x), x, Constant.Type.DOUBLE); - i++; - break; - } - case 7: { - int x = readShort(); - constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.CLASS_REF); - break; - } - case 8: { - int x = readShort(); - constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.STRING_REF); - break; - } - case 9: { - int x = readInt(); - constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.FIELD_REF); - break; - } - case 10: { - int x = readInt(); - constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.METHOD_REF); - break; - } - case 11: { - int x = readInt(); - constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.INTERFACE_METHOD_REF); - break; - } - case 12: { - int x = readInt(); - constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.NAME_AND_TYPE); - break; - } - default: - throw new IciqlException("Unsupported constant pool tag: " + type); - } - } - int accessFlags = readShort(); - debug("access flags: " + accessFlags); - int classRef = readShort(); - debug("class: " + constantPool[constantPool[classRef].intValue()]); - int superClassRef = readShort(); - debug(" extends " + constantPool[constantPool[superClassRef].intValue()]); - int interfaceCount = readShort(); - for (int i = 0; i < interfaceCount; i++) { - int interfaceRef = readShort(); - debug(" implements " + constantPool[constantPool[interfaceRef].intValue()]); - } - int fieldCount = readShort(); - for (int i = 0; i < fieldCount; i++) { - readField(); - } - int methodCount = readShort(); - for (int i = 0; i < methodCount; i++) { - readMethod(); - } - readAttributes(); - return result; - } + public Token decompile(Object instance, Map fields, String method) { + this.fieldMap = fields; + this.convertMethodName = method; + Class clazz = instance.getClass(); + String className = clazz.getName(); + debug("class name " + className); + ByteArrayOutputStream buff = new ByteArrayOutputStream(); + try { + InputStream in = clazz.getClassLoader().getResource(className.replace('.', '/') + ".class") + .openStream(); + while (true) { + int x = in.read(); + if (x < 0) { + break; + } + buff.write(x); + } + } catch (IOException e) { + throw new IciqlException("Could not read class bytecode", e); + } + data = buff.toByteArray(); + int header = readInt(); + debug("header: " + Integer.toHexString(header)); + int minorVersion = readShort(); + int majorVersion = readShort(); + debug("version: " + majorVersion + "." + minorVersion); + int constantPoolCount = readShort(); + constantPool = new Constant[constantPoolCount]; + for (int i = 1; i < constantPoolCount; i++) { + int type = readByte(); + switch (type) { + case 1: + constantPool[i] = ConstantString.get(readString()); + break; + case 3: { + int x = readInt(); + constantPool[i] = ConstantNumber.get(x); + break; + } + case 4: { + int x = readInt(); + constantPool[i] = ConstantNumber.get("" + Float.intBitsToFloat(x), x, Constant.Type.FLOAT); + break; + } + case 5: { + long x = readLong(); + constantPool[i] = ConstantNumber.get(x); + i++; + break; + } + case 6: { + long x = readLong(); + constantPool[i] = ConstantNumber + .get("" + Double.longBitsToDouble(x), x, Constant.Type.DOUBLE); + i++; + break; + } + case 7: { + int x = readShort(); + constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.CLASS_REF); + break; + } + case 8: { + int x = readShort(); + constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.STRING_REF); + break; + } + case 9: { + int x = readInt(); + constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.FIELD_REF); + break; + } + case 10: { + int x = readInt(); + constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.METHOD_REF); + break; + } + case 11: { + int x = readInt(); + constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.INTERFACE_METHOD_REF); + break; + } + case 12: { + int x = readInt(); + constantPool[i] = ConstantNumber.get(null, x, ConstantNumber.Type.NAME_AND_TYPE); + break; + } + default: + throw new IciqlException("Unsupported constant pool tag: " + type); + } + } + int accessFlags = readShort(); + debug("access flags: " + accessFlags); + int classRef = readShort(); + debug("class: " + constantPool[constantPool[classRef].intValue()]); + int superClassRef = readShort(); + debug(" extends " + constantPool[constantPool[superClassRef].intValue()]); + int interfaceCount = readShort(); + for (int i = 0; i < interfaceCount; i++) { + int interfaceRef = readShort(); + debug(" implements " + constantPool[constantPool[interfaceRef].intValue()]); + } + int fieldCount = readShort(); + for (int i = 0; i < fieldCount; i++) { + readField(); + } + int methodCount = readShort(); + for (int i = 0; i < methodCount; i++) { + readMethod(); + } + readAttributes(); + return result; + } - private void readField() { - int accessFlags = readShort(); - int nameIndex = readShort(); - int descIndex = readShort(); - debug(" " + constantPool[descIndex] + " " + constantPool[nameIndex] + " " + accessFlags); - readAttributes(); - } + private void readField() { + int accessFlags = readShort(); + int nameIndex = readShort(); + int descIndex = readShort(); + debug(" " + constantPool[descIndex] + " " + constantPool[nameIndex] + " " + accessFlags); + readAttributes(); + } - private void readMethod() { - int accessFlags = readShort(); - int nameIndex = readShort(); - int descIndex = readShort(); - String desc = constantPool[descIndex].toString(); - methodName = constantPool[nameIndex].toString(); - debug(" " + desc + " " + methodName + " " + accessFlags); - readAttributes(); - } + private void readMethod() { + int accessFlags = readShort(); + int nameIndex = readShort(); + int descIndex = readShort(); + String desc = constantPool[descIndex].toString(); + methodName = constantPool[nameIndex].toString(); + debug(" " + desc + " " + methodName + " " + accessFlags); + readAttributes(); + } - private void readAttributes() { - int attributeCount = readShort(); - for (int i = 0; i < attributeCount; i++) { - int attributeNameIndex = readShort(); - String attributeName = constantPool[attributeNameIndex].toString(); - debug(" attribute " + attributeName); - int attributeLength = readInt(); - int end = pos + attributeLength; - if ("Code".equals(attributeName)) { - readCode(); - } - pos = end; - } - } + private void readAttributes() { + int attributeCount = readShort(); + for (int i = 0; i < attributeCount; i++) { + int attributeNameIndex = readShort(); + String attributeName = constantPool[attributeNameIndex].toString(); + debug(" attribute " + attributeName); + int attributeLength = readInt(); + int end = pos + attributeLength; + if ("Code".equals(attributeName)) { + readCode(); + } + pos = end; + } + } - void decompile() { - int maxStack = readShort(); - int maxLocals = readShort(); - debug("stack: " + maxStack + " locals: " + maxLocals); - int codeLength = readInt(); - startByteCode = pos; - int end = pos + codeLength; - while (pos < end) { - readByteCode(); - } - debug(""); - pos = startByteCode + codeLength; - int exceptionTableLength = readShort(); - pos += 2 * exceptionTableLength; - readAttributes(); - } + void decompile() { + int maxStack = readShort(); + int maxLocals = readShort(); + debug("stack: " + maxStack + " locals: " + maxLocals); + int codeLength = readInt(); + startByteCode = pos; + int end = pos + codeLength; + while (pos < end) { + readByteCode(); + } + debug(""); + pos = startByteCode + codeLength; + int exceptionTableLength = readShort(); + pos += 2 * exceptionTableLength; + readAttributes(); + } - private void readCode() { - variables.clear(); - stack.clear(); - int maxStack = readShort(); - int maxLocals = readShort(); - debug("stack: " + maxStack + " locals: " + maxLocals); - int codeLength = readInt(); - startByteCode = pos; - if (methodName.startsWith(convertMethodName)) { - result = getResult(); - } - pos = startByteCode + codeLength; - int exceptionTableLength = readShort(); - pos += 2 * exceptionTableLength; - readAttributes(); - } + private void readCode() { + variables.clear(); + stack.clear(); + int maxStack = readShort(); + int maxLocals = readShort(); + debug("stack: " + maxStack + " locals: " + maxLocals); + int codeLength = readInt(); + startByteCode = pos; + if (methodName.startsWith(convertMethodName)) { + result = getResult(); + } + pos = startByteCode + codeLength; + int exceptionTableLength = readShort(); + pos += 2 * exceptionTableLength; + readAttributes(); + } - private Token getResult() { - while (true) { - readByteCode(); - if (endOfMethod) { - return stack.pop(); - } - if (condition) { - Token c = stack.pop(); - Stack currentStack = new Stack(); - currentStack.addAll(stack); - ArrayList currentVariables = new ArrayList(); - currentVariables.addAll(variables); - int branch = nextPc; - Token a = getResult(); - stack = currentStack; - variables = currentVariables; - pos = branch + startByteCode; - Token b = getResult(); - if (a.equals("0") && b.equals("1")) { - return c; - } else if (a.equals("1") && b.equals("0")) { - return Not.get(c); - } else if (b.equals("0")) { - return And.get(Not.get(c), a); - } else if (a.equals("0")) { - return And.get(c, b); - } else if (b.equals("1")) { - return Or.get(c, a); - } else if (a.equals("1")) { - return And.get(Not.get(c), b); - } - return CaseWhen.get(c, b, a); - } - if (nextPc != 0) { - pos = nextPc + startByteCode; - } - } - } + private Token getResult() { + while (true) { + readByteCode(); + if (endOfMethod) { + return stack.pop(); + } + if (condition) { + Token c = stack.pop(); + Stack currentStack = new Stack(); + currentStack.addAll(stack); + ArrayList currentVariables = new ArrayList(); + currentVariables.addAll(variables); + int branch = nextPc; + Token a = getResult(); + stack = currentStack; + variables = currentVariables; + pos = branch + startByteCode; + Token b = getResult(); + if (a.equals("0") && b.equals("1")) { + return c; + } else if (a.equals("1") && b.equals("0")) { + return Not.get(c); + } else if (b.equals("0")) { + return And.get(Not.get(c), a); + } else if (a.equals("0")) { + return And.get(c, b); + } else if (b.equals("1")) { + return Or.get(c, a); + } else if (a.equals("1")) { + return And.get(Not.get(c), b); + } + return CaseWhen.get(c, b, a); + } + if (nextPc != 0) { + pos = nextPc + startByteCode; + } + } + } - private void readByteCode() { - int startPos = pos - startByteCode; - int opCode = readByte(); - String op; - endOfMethod = false; - condition = false; - nextPc = 0; - switch (opCode) { - case 0: - op = "nop"; - break; - case 1: - op = "aconst_null"; - stack.push(Null.INSTANCE); - break; - case 2: - op = "iconst_m1"; - stack.push(ConstantNumber.get("-1")); - break; - case 3: - op = "iconst_0"; - stack.push(ConstantNumber.get("0")); - break; - case 4: - op = "iconst_1"; - stack.push(ConstantNumber.get("1")); - break; - case 5: - op = "iconst_2"; - stack.push(ConstantNumber.get("2")); - break; - case 6: - op = "iconst_3"; - stack.push(ConstantNumber.get("3")); - break; - case 7: - op = "iconst_4"; - stack.push(ConstantNumber.get("4")); - break; - case 8: - op = "iconst_5"; - stack.push(ConstantNumber.get("5")); - break; - case 9: - op = "lconst_0"; - stack.push(ConstantNumber.get("0")); - break; - case 10: - op = "lconst_1"; - stack.push(ConstantNumber.get("1")); - break; - case 11: - op = "fconst_0"; - stack.push(ConstantNumber.get("0.0")); - break; - case 12: - op = "fconst_1"; - stack.push(ConstantNumber.get("1.0")); - break; - case 13: - op = "fconst_2"; - stack.push(ConstantNumber.get("2.0")); - break; - case 14: - op = "dconst_0"; - stack.push(ConstantNumber.get("0.0")); - break; - case 15: - op = "dconst_1"; - stack.push(ConstantNumber.get("1.0")); - break; - case 16: { - int x = (byte) readByte(); - op = "bipush " + x; - stack.push(ConstantNumber.get(x)); - break; - } - case 17: { - int x = (short) readShort(); - op = "sipush " + x; - stack.push(ConstantNumber.get(x)); - break; - } - case 18: { - Token s = getConstant(readByte()); - op = "ldc " + s; - stack.push(s); - break; - } - case 19: { - Token s = getConstant(readShort()); - op = "ldc_w " + s; - stack.push(s); - break; - } - case 20: { - Token s = getConstant(readShort()); - op = "ldc2_w " + s; - stack.push(s); - break; - } - case 21: { - int x = readByte(); - op = "iload " + x; - stack.push(getVariable(x)); - break; - } - case 22: { - int x = readByte(); - op = "lload " + x; - stack.push(getVariable(x)); - break; - } - case 23: { - int x = readByte(); - op = "fload " + x; - stack.push(getVariable(x)); - break; - } - case 24: { - int x = readByte(); - op = "dload " + x; - stack.push(getVariable(x)); - break; - } - case 25: { - int x = readByte(); - op = "aload " + x; - stack.push(getVariable(x)); - break; - } - case 26: - op = "iload_0"; - stack.push(getVariable(0)); - break; - case 27: - op = "iload_1"; - stack.push(getVariable(1)); - break; - case 28: - op = "iload_2"; - stack.push(getVariable(2)); - break; - case 29: - op = "iload_3"; - stack.push(getVariable(3)); - break; - case 30: - op = "lload_0"; - stack.push(getVariable(0)); - break; - case 31: - op = "lload_1"; - stack.push(getVariable(1)); - break; - case 32: - op = "lload_2"; - stack.push(getVariable(2)); - break; - case 33: - op = "lload_3"; - stack.push(getVariable(3)); - break; - case 34: - op = "fload_0"; - stack.push(getVariable(0)); - break; - case 35: - op = "fload_1"; - stack.push(getVariable(1)); - break; - case 36: - op = "fload_2"; - stack.push(getVariable(2)); - break; - case 37: - op = "fload_3"; - stack.push(getVariable(3)); - break; - case 38: - op = "dload_0"; - stack.push(getVariable(0)); - break; - case 39: - op = "dload_1"; - stack.push(getVariable(1)); - break; - case 40: - op = "dload_2"; - stack.push(getVariable(2)); - break; - case 41: - op = "dload_3"; - stack.push(getVariable(3)); - break; - case 42: - op = "aload_0"; - stack.push(getVariable(0)); - break; - case 43: - op = "aload_1"; - stack.push(getVariable(1)); - break; - case 44: - op = "aload_2"; - stack.push(getVariable(2)); - break; - case 45: - op = "aload_3"; - stack.push(getVariable(3)); - break; - case 46: { - Token index = stack.pop(); - Token ref = stack.pop(); - op = "iaload"; - stack.push(ArrayGet.get(ref, index)); - break; - } - case 47: { - Token index = stack.pop(); - Token ref = stack.pop(); - op = "laload"; - stack.push(ArrayGet.get(ref, index)); - break; - } - case 48: { - Token index = stack.pop(); - Token ref = stack.pop(); - op = "faload"; - stack.push(ArrayGet.get(ref, index)); - break; - } - case 49: { - Token index = stack.pop(); - Token ref = stack.pop(); - op = "daload"; - stack.push(ArrayGet.get(ref, index)); - break; - } - case 50: { - Token index = stack.pop(); - Token ref = stack.pop(); - op = "aaload"; - stack.push(ArrayGet.get(ref, index)); - break; - } - case 51: { - Token index = stack.pop(); - Token ref = stack.pop(); - op = "baload"; - stack.push(ArrayGet.get(ref, index)); - break; - } - case 52: { - Token index = stack.pop(); - Token ref = stack.pop(); - op = "caload"; - stack.push(ArrayGet.get(ref, index)); - break; - } - case 53: { - Token index = stack.pop(); - Token ref = stack.pop(); - op = "saload"; - stack.push(ArrayGet.get(ref, index)); - break; - } - case 54: { - int var = readByte(); - op = "istore " + var; - setVariable(var, stack.pop()); - break; - } - case 55: { - int var = readByte(); - op = "lstore " + var; - setVariable(var, stack.pop()); - break; - } - case 56: { - int var = readByte(); - op = "fstore " + var; - setVariable(var, stack.pop()); - break; - } - case 57: { - int var = readByte(); - op = "dstore " + var; - setVariable(var, stack.pop()); - break; - } - case 58: { - int var = readByte(); - op = "astore " + var; - setVariable(var, stack.pop()); - break; - } - case 59: - op = "istore_0"; - setVariable(0, stack.pop()); - break; - case 60: - op = "istore_1"; - setVariable(1, stack.pop()); - break; - case 61: - op = "istore_2"; - setVariable(2, stack.pop()); - break; - case 62: - op = "istore_3"; - setVariable(3, stack.pop()); - break; - case 63: - op = "lstore_0"; - setVariable(0, stack.pop()); - break; - case 64: - op = "lstore_1"; - setVariable(1, stack.pop()); - break; - case 65: - op = "lstore_2"; - setVariable(2, stack.pop()); - break; - case 66: - op = "lstore_3"; - setVariable(3, stack.pop()); - break; - case 67: - op = "fstore_0"; - setVariable(0, stack.pop()); - break; - case 68: - op = "fstore_1"; - setVariable(1, stack.pop()); - break; - case 69: - op = "fstore_2"; - setVariable(2, stack.pop()); - break; - case 70: - op = "fstore_3"; - setVariable(3, stack.pop()); - break; - case 71: - op = "dstore_0"; - setVariable(0, stack.pop()); - break; - case 72: - op = "dstore_1"; - setVariable(1, stack.pop()); - break; - case 73: - op = "dstore_2"; - setVariable(2, stack.pop()); - break; - case 74: - op = "dstore_3"; - setVariable(3, stack.pop()); - break; - case 75: - op = "astore_0"; - setVariable(0, stack.pop()); - break; - case 76: - op = "astore_1"; - setVariable(1, stack.pop()); - break; - case 77: - op = "astore_2"; - setVariable(2, stack.pop()); - break; - case 78: - op = "astore_3"; - setVariable(3, stack.pop()); - break; - case 79: { - // String value = stack.pop(); - // String index = stack.pop(); - // String ref = stack.pop(); - op = "iastore"; - // TODO side effect - not supported - break; - } - case 80: - op = "lastore"; - // TODO side effect - not supported - break; - case 81: - op = "fastore"; - // TODO side effect - not supported - break; - case 82: - op = "dastore"; - // TODO side effect - not supported - break; - case 83: - op = "aastore"; - // TODO side effect - not supported - break; - case 84: - op = "bastore"; - // TODO side effect - not supported - break; - case 85: - op = "castore"; - // TODO side effect - not supported - break; - case 86: - op = "sastore"; - // TODO side effect - not supported - break; - case 87: - op = "pop"; - stack.pop(); - break; - case 88: - op = "pop2"; - // TODO currently we don't know the stack types - stack.pop(); - stack.pop(); - break; - case 89: { - op = "dup"; - Token x = stack.pop(); - stack.push(x); - stack.push(x); - break; - } - case 90: { - op = "dup_x1"; - Token a = stack.pop(); - Token b = stack.pop(); - stack.push(a); - stack.push(b); - stack.push(a); - break; - } - case 91: { - // TODO currently we don't know the stack types - op = "dup_x2"; - Token a = stack.pop(); - Token b = stack.pop(); - Token c = stack.pop(); - stack.push(a); - stack.push(c); - stack.push(b); - stack.push(a); - break; - } - case 92: { - // TODO currently we don't know the stack types - op = "dup2"; - Token a = stack.pop(); - Token b = stack.pop(); - stack.push(b); - stack.push(a); - stack.push(b); - stack.push(a); - break; - } - case 93: { - // TODO currently we don't know the stack types - op = "dup2_x1"; - Token a = stack.pop(); - Token b = stack.pop(); - Token c = stack.pop(); - stack.push(b); - stack.push(a); - stack.push(c); - stack.push(b); - stack.push(a); - break; - } - case 94: { - // TODO currently we don't know the stack types - op = "dup2_x2"; - Token a = stack.pop(); - Token b = stack.pop(); - Token c = stack.pop(); - Token d = stack.pop(); - stack.push(b); - stack.push(a); - stack.push(d); - stack.push(c); - stack.push(b); - stack.push(a); - break; - } - case 95: { - op = "swap"; - Token a = stack.pop(); - Token b = stack.pop(); - stack.push(a); - stack.push(b); - break; - } - case 96: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "iadd"; - stack.push(Operation.get(a, Operation.Type.ADD, b)); - break; - } - case 97: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "ladd"; - stack.push(Operation.get(a, Operation.Type.ADD, b)); - break; - } - case 98: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "fadd"; - stack.push(Operation.get(a, Operation.Type.ADD, b)); - break; - } - case 99: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "dadd"; - stack.push(Operation.get(a, Operation.Type.ADD, b)); - break; - } - case 100: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "isub"; - stack.push(Operation.get(a, Operation.Type.SUBTRACT, b)); - break; - } - case 101: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "lsub"; - stack.push(Operation.get(a, Operation.Type.SUBTRACT, b)); - break; - } - case 102: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "fsub"; - stack.push(Operation.get(a, Operation.Type.SUBTRACT, b)); - break; - } - case 103: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "dsub"; - stack.push(Operation.get(a, Operation.Type.SUBTRACT, b)); - break; - } - case 104: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "imul"; - stack.push(Operation.get(a, Operation.Type.MULTIPLY, b)); - break; - } - case 105: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "lmul"; - stack.push(Operation.get(a, Operation.Type.MULTIPLY, b)); - break; - } - case 106: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "fmul"; - stack.push(Operation.get(a, Operation.Type.MULTIPLY, b)); - break; - } - case 107: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "dmul"; - stack.push(Operation.get(a, Operation.Type.MULTIPLY, b)); - break; - } - case 108: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "idiv"; - stack.push(Operation.get(a, Operation.Type.DIVIDE, b)); - break; - } - case 109: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "ldiv"; - stack.push(Operation.get(a, Operation.Type.DIVIDE, b)); - break; - } - case 110: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "fdiv"; - stack.push(Operation.get(a, Operation.Type.DIVIDE, b)); - break; - } - case 111: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "ddiv"; - stack.push(Operation.get(a, Operation.Type.DIVIDE, b)); - break; - } - case 112: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "irem"; - stack.push(Operation.get(a, Operation.Type.MOD, b)); - break; - } - case 113: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "lrem"; - stack.push(Operation.get(a, Operation.Type.MOD, b)); - break; - } - case 114: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "frem"; - stack.push(Operation.get(a, Operation.Type.MOD, b)); - break; - } - case 115: { - Token b = stack.pop(); - Token a = stack.pop(); - op = "drem"; - stack.push(Operation.get(a, Operation.Type.MOD, b)); - break; - } - // case 116: - // op = "ineg"; - // break; - // case 117: - // op = "lneg"; - // break; - // case 118: - // op = "fneg"; - // break; - // case 119: - // op = "dneg"; - // break; - // case 120: - // op = "ishl"; - // break; - // case 121: - // op = "lshl"; - // break; - // case 122: - // op = "ishr"; - // break; - // case 123: - // op = "lshr"; - // break; - // case 124: - // op = "iushr"; - // break; - // case 125: - // op = "lushr"; - // break; - // case 126: - // op = "iand"; - // break; - // case 127: - // op = "land"; - // break; - // case 128: - // op = "ior"; - // break; - // case 129: - // op = "lor"; - // break; - // case 130: - // op = "ixor"; - // break; - // case 131: - // op = "lxor"; - // break; - // case 132: { - // int var = readByte(); - // int off = (byte) readByte(); - // op = "iinc " + var + " " + off; - // break; - // } - // case 133: - // op = "i2l"; - // break; - // case 134: - // op = "i2f"; - // break; - // case 135: - // op = "i2d"; - // break; - // case 136: - // op = "l2i"; - // break; - // case 137: - // op = "l2f"; - // break; - // case 138: - // op = "l2d"; - // break; - // case 139: - // op = "f2i"; - // break; - // case 140: - // op = "f2l"; - // break; - // case 141: - // op = "f2d"; - // break; - // case 142: - // op = "d2i"; - // break; - // case 143: - // op = "d2l"; - // break; - // case 144: - // op = "d2f"; - // break; - // case 145: - // op = "i2b"; - // break; - // case 146: - // op = "i2c"; - // break; - // case 147: - // op = "i2s"; - // break; - case 148: { - Token b = stack.pop(), a = stack.pop(); - stack.push(new Function("SIGN", Operation.get(a, Operation.Type.SUBTRACT, b))); - op = "lcmp"; - break; - } - // case 149: - // op = "fcmpl"; - // break; - // case 150: - // op = "fcmpg"; - // break; - // case 151: - // op = "dcmpl"; - // break; - // case 152: - // op = "dcmpg"; - // break; - case 153: - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - stack.push(Operation.get(stack.pop(), Operation.Type.EQUALS, ConstantNumber.get(0))); - op = "ifeq " + nextPc; - break; - case 154: - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - stack.push(Operation.get(stack.pop(), Operation.Type.NOT_EQUALS, ConstantNumber.get(0))); - op = "ifne " + nextPc; - break; - case 155: - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - stack.push(Operation.get(stack.pop(), Operation.Type.SMALLER, ConstantNumber.get(0))); - op = "iflt " + nextPc; - break; - case 156: - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - stack.push(Operation.get(stack.pop(), Operation.Type.BIGGER_EQUALS, ConstantNumber.get(0))); - op = "ifge " + nextPc; - break; - case 157: - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - stack.push(Operation.get(stack.pop(), Operation.Type.BIGGER, ConstantNumber.get(0))); - op = "ifgt " + nextPc; - break; - case 158: - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - stack.push(Operation.get(stack.pop(), Operation.Type.SMALLER_EQUALS, ConstantNumber.get(0))); - op = "ifle " + nextPc; - break; - case 159: { - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - Token b = stack.pop(), a = stack.pop(); - stack.push(Operation.get(a, Operation.Type.EQUALS, b)); - op = "if_icmpeq " + nextPc; - break; - } - case 160: { - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - Token b = stack.pop(), a = stack.pop(); - stack.push(Operation.get(a, Operation.Type.NOT_EQUALS, b)); - op = "if_icmpne " + nextPc; - break; - } - case 161: { - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - Token b = stack.pop(), a = stack.pop(); - stack.push(Operation.get(a, Operation.Type.SMALLER, b)); - op = "if_icmplt " + nextPc; - break; - } - case 162: { - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - Token b = stack.pop(), a = stack.pop(); - stack.push(Operation.get(a, Operation.Type.BIGGER_EQUALS, b)); - op = "if_icmpge " + nextPc; - break; - } - case 163: { - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - Token b = stack.pop(), a = stack.pop(); - stack.push(Operation.get(a, Operation.Type.BIGGER, b)); - op = "if_icmpgt " + nextPc; - break; - } - case 164: { - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - Token b = stack.pop(), a = stack.pop(); - stack.push(Operation.get(a, Operation.Type.SMALLER_EQUALS, b)); - op = "if_icmple " + nextPc; - break; - } - case 165: { - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - Token b = stack.pop(), a = stack.pop(); - stack.push(Operation.get(a, Operation.Type.EQUALS, b)); - op = "if_acmpeq " + nextPc; - break; - } - case 166: { - condition = true; - nextPc = getAbsolutePos(pos, readShort()); - Token b = stack.pop(), a = stack.pop(); - stack.push(Operation.get(a, Operation.Type.NOT_EQUALS, b)); - op = "if_acmpne " + nextPc; - break; - } - case 167: - nextPc = getAbsolutePos(pos, readShort()); - op = "goto " + nextPc; - break; - // case 168: - // // TODO not supported yet - // op = "jsr " + getAbsolutePos(pos, readShort()); - // break; - // case 169: - // // TODO not supported yet - // op = "ret " + readByte(); - // break; - // case 170: { - // int start = pos; - // pos += 4 - ((pos - startByteCode) & 3); - // int def = readInt(); - // int low = readInt(), high = readInt(); - // int n = high - low + 1; - // op = "tableswitch default:" + getAbsolutePos(start, def); - // StringBuilder buff = new StringBuilder(); - // for (int i = 0; i < n; i++) { - // buff.append(' ').append(low++). - // append(":"). - // append(getAbsolutePos(start, readInt())); - // } - // op += buff.toString(); - // // pos += n * 4; - // break; - // } - // case 171: { - // int start = pos; - // pos += 4 - ((pos - startByteCode) & 3); - // int def = readInt(); - // int n = readInt(); - // op = "lookupswitch default:" + getAbsolutePos(start, def); - // StringBuilder buff = new StringBuilder(); - // for (int i = 0; i < n; i++) { - // buff.append(' '). - // append(readInt()). - // append(":"). - // append(getAbsolutePos(start, readInt())); - // } - // op += buff.toString(); - // // pos += n * 8; - // break; - // } - case 172: - op = "ireturn"; - endOfMethod = true; - break; - case 173: - op = "lreturn"; - endOfMethod = true; - break; - case 174: - op = "freturn"; - endOfMethod = true; - break; - case 175: - op = "dreturn"; - endOfMethod = true; - break; - case 176: - op = "areturn"; - endOfMethod = true; - break; - case 177: - op = "return"; - // no value returned - stack.push(null); - endOfMethod = true; - break; - // case 178: - // op = "getstatic " + getField(readShort()); - // break; - // case 179: - // op = "putstatic " + getField(readShort()); - // break; - case 180: { - String field = getField(readShort()); - Token p = stack.pop(); - String s = p + "." + field.substring(field.lastIndexOf('.') + 1, field.indexOf(' ')); - if (s.startsWith("this.")) { - s = s.substring(5); - } - stack.push(Variable.get(s, fieldMap.get(s))); - op = "getfield " + field; - break; - } - // case 181: - // op = "putfield " + getField(readShort()); - // break; - case 182: { - String method = getMethod(readShort()); - op = "invokevirtual " + method; - if (method.equals("java/lang/String.equals (Ljava/lang/Object;)Z")) { - Token a = stack.pop(); - Token b = stack.pop(); - stack.push(Operation.get(a, Operation.Type.EQUALS, b)); - } else if (method.equals("java/lang/Integer.intValue ()I")) { - // ignore - } else if (method.equals("java/lang/Long.longValue ()J")) { - // ignore - } - break; - } - case 183: { - String method = getMethod(readShort()); - op = "invokespecial " + method; - break; - } - case 184: - op = "invokestatic " + getMethod(readShort()); - break; - // case 185: { - // int methodRef = readShort(); - // readByte(); - // readByte(); - // op = "invokeinterface " + getMethod(methodRef); - // break; - // } - case 187: { - String className = constantPool[constantPool[readShort()].intValue()].toString(); - op = "new " + className; - break; - } - // case 188: - // op = "newarray " + readByte(); - // break; - // case 189: - // op = "anewarray " + cpString[readShort()]; - // break; - // case 190: - // op = "arraylength"; - // break; - // case 191: - // op = "athrow"; - // break; - // case 192: - // op = "checkcast " + cpString[readShort()]; - // break; - // case 193: - // op = "instanceof " + cpString[readShort()]; - // break; - // case 194: - // op = "monitorenter"; - // break; - // case 195: - // op = "monitorexit"; - // break; - // case 196: { - // opCode = readByte(); - // switch (opCode) { - // case 21: - // op = "wide iload " + readShort(); - // break; - // case 22: - // op = "wide lload " + readShort(); - // break; - // case 23: - // op = "wide fload " + readShort(); - // break; - // case 24: - // op = "wide dload " + readShort(); - // break; - // case 25: - // op = "wide aload " + readShort(); - // break; - // case 54: - // op = "wide istore " + readShort(); - // break; - // case 55: - // op = "wide lstore " + readShort(); - // break; - // case 56: - // op = "wide fstore " + readShort(); - // break; - // case 57: - // op = "wide dstore " + readShort(); - // break; - // case 58: - // op = "wide astore " + readShort(); - // break; - // case 132: { - // int var = readShort(); - // int off = (short) readShort(); - // op = "wide iinc " + var + " " + off; - // break; - // } - // case 169: - // op = "wide ret " + readShort(); - // break; - // default: - // throw new IciqlException( - // "Unsupported wide opCode " + opCode); - // } - // break; - // } - // case 197: - // op = "multianewarray " + cpString[readShort()] + " " + readByte(); - // break; - // case 198: { - // condition = true; - // nextPc = getAbsolutePos(pos, readShort()); - // Token a = stack.pop(); - // stack.push("(" + a + " IS NULL)"); - // op = "ifnull " + nextPc; - // break; - // } - // case 199: { - // condition = true; - // nextPc = getAbsolutePos(pos, readShort()); - // Token a = stack.pop(); - // stack.push("(" + a + " IS NOT NULL)"); - // op = "ifnonnull " + nextPc; - // break; - // } - case 200: - op = "goto_w " + getAbsolutePos(pos, readInt()); - break; - case 201: - op = "jsr_w " + getAbsolutePos(pos, readInt()); - break; - default: - throw new IciqlException("Unsupported opCode " + opCode); - } - debug(" " + startPos + ": " + op); - } + private void readByteCode() { + int startPos = pos - startByteCode; + int opCode = readByte(); + String op; + endOfMethod = false; + condition = false; + nextPc = 0; + switch (opCode) { + case 0: + op = "nop"; + break; + case 1: + op = "aconst_null"; + stack.push(Null.INSTANCE); + break; + case 2: + op = "iconst_m1"; + stack.push(ConstantNumber.get("-1")); + break; + case 3: + op = "iconst_0"; + stack.push(ConstantNumber.get("0")); + break; + case 4: + op = "iconst_1"; + stack.push(ConstantNumber.get("1")); + break; + case 5: + op = "iconst_2"; + stack.push(ConstantNumber.get("2")); + break; + case 6: + op = "iconst_3"; + stack.push(ConstantNumber.get("3")); + break; + case 7: + op = "iconst_4"; + stack.push(ConstantNumber.get("4")); + break; + case 8: + op = "iconst_5"; + stack.push(ConstantNumber.get("5")); + break; + case 9: + op = "lconst_0"; + stack.push(ConstantNumber.get("0")); + break; + case 10: + op = "lconst_1"; + stack.push(ConstantNumber.get("1")); + break; + case 11: + op = "fconst_0"; + stack.push(ConstantNumber.get("0.0")); + break; + case 12: + op = "fconst_1"; + stack.push(ConstantNumber.get("1.0")); + break; + case 13: + op = "fconst_2"; + stack.push(ConstantNumber.get("2.0")); + break; + case 14: + op = "dconst_0"; + stack.push(ConstantNumber.get("0.0")); + break; + case 15: + op = "dconst_1"; + stack.push(ConstantNumber.get("1.0")); + break; + case 16: { + int x = (byte) readByte(); + op = "bipush " + x; + stack.push(ConstantNumber.get(x)); + break; + } + case 17: { + int x = (short) readShort(); + op = "sipush " + x; + stack.push(ConstantNumber.get(x)); + break; + } + case 18: { + Token s = getConstant(readByte()); + op = "ldc " + s; + stack.push(s); + break; + } + case 19: { + Token s = getConstant(readShort()); + op = "ldc_w " + s; + stack.push(s); + break; + } + case 20: { + Token s = getConstant(readShort()); + op = "ldc2_w " + s; + stack.push(s); + break; + } + case 21: { + int x = readByte(); + op = "iload " + x; + stack.push(getVariable(x)); + break; + } + case 22: { + int x = readByte(); + op = "lload " + x; + stack.push(getVariable(x)); + break; + } + case 23: { + int x = readByte(); + op = "fload " + x; + stack.push(getVariable(x)); + break; + } + case 24: { + int x = readByte(); + op = "dload " + x; + stack.push(getVariable(x)); + break; + } + case 25: { + int x = readByte(); + op = "aload " + x; + stack.push(getVariable(x)); + break; + } + case 26: + op = "iload_0"; + stack.push(getVariable(0)); + break; + case 27: + op = "iload_1"; + stack.push(getVariable(1)); + break; + case 28: + op = "iload_2"; + stack.push(getVariable(2)); + break; + case 29: + op = "iload_3"; + stack.push(getVariable(3)); + break; + case 30: + op = "lload_0"; + stack.push(getVariable(0)); + break; + case 31: + op = "lload_1"; + stack.push(getVariable(1)); + break; + case 32: + op = "lload_2"; + stack.push(getVariable(2)); + break; + case 33: + op = "lload_3"; + stack.push(getVariable(3)); + break; + case 34: + op = "fload_0"; + stack.push(getVariable(0)); + break; + case 35: + op = "fload_1"; + stack.push(getVariable(1)); + break; + case 36: + op = "fload_2"; + stack.push(getVariable(2)); + break; + case 37: + op = "fload_3"; + stack.push(getVariable(3)); + break; + case 38: + op = "dload_0"; + stack.push(getVariable(0)); + break; + case 39: + op = "dload_1"; + stack.push(getVariable(1)); + break; + case 40: + op = "dload_2"; + stack.push(getVariable(2)); + break; + case 41: + op = "dload_3"; + stack.push(getVariable(3)); + break; + case 42: + op = "aload_0"; + stack.push(getVariable(0)); + break; + case 43: + op = "aload_1"; + stack.push(getVariable(1)); + break; + case 44: + op = "aload_2"; + stack.push(getVariable(2)); + break; + case 45: + op = "aload_3"; + stack.push(getVariable(3)); + break; + case 46: { + Token index = stack.pop(); + Token ref = stack.pop(); + op = "iaload"; + stack.push(ArrayGet.get(ref, index)); + break; + } + case 47: { + Token index = stack.pop(); + Token ref = stack.pop(); + op = "laload"; + stack.push(ArrayGet.get(ref, index)); + break; + } + case 48: { + Token index = stack.pop(); + Token ref = stack.pop(); + op = "faload"; + stack.push(ArrayGet.get(ref, index)); + break; + } + case 49: { + Token index = stack.pop(); + Token ref = stack.pop(); + op = "daload"; + stack.push(ArrayGet.get(ref, index)); + break; + } + case 50: { + Token index = stack.pop(); + Token ref = stack.pop(); + op = "aaload"; + stack.push(ArrayGet.get(ref, index)); + break; + } + case 51: { + Token index = stack.pop(); + Token ref = stack.pop(); + op = "baload"; + stack.push(ArrayGet.get(ref, index)); + break; + } + case 52: { + Token index = stack.pop(); + Token ref = stack.pop(); + op = "caload"; + stack.push(ArrayGet.get(ref, index)); + break; + } + case 53: { + Token index = stack.pop(); + Token ref = stack.pop(); + op = "saload"; + stack.push(ArrayGet.get(ref, index)); + break; + } + case 54: { + int var = readByte(); + op = "istore " + var; + setVariable(var, stack.pop()); + break; + } + case 55: { + int var = readByte(); + op = "lstore " + var; + setVariable(var, stack.pop()); + break; + } + case 56: { + int var = readByte(); + op = "fstore " + var; + setVariable(var, stack.pop()); + break; + } + case 57: { + int var = readByte(); + op = "dstore " + var; + setVariable(var, stack.pop()); + break; + } + case 58: { + int var = readByte(); + op = "astore " + var; + setVariable(var, stack.pop()); + break; + } + case 59: + op = "istore_0"; + setVariable(0, stack.pop()); + break; + case 60: + op = "istore_1"; + setVariable(1, stack.pop()); + break; + case 61: + op = "istore_2"; + setVariable(2, stack.pop()); + break; + case 62: + op = "istore_3"; + setVariable(3, stack.pop()); + break; + case 63: + op = "lstore_0"; + setVariable(0, stack.pop()); + break; + case 64: + op = "lstore_1"; + setVariable(1, stack.pop()); + break; + case 65: + op = "lstore_2"; + setVariable(2, stack.pop()); + break; + case 66: + op = "lstore_3"; + setVariable(3, stack.pop()); + break; + case 67: + op = "fstore_0"; + setVariable(0, stack.pop()); + break; + case 68: + op = "fstore_1"; + setVariable(1, stack.pop()); + break; + case 69: + op = "fstore_2"; + setVariable(2, stack.pop()); + break; + case 70: + op = "fstore_3"; + setVariable(3, stack.pop()); + break; + case 71: + op = "dstore_0"; + setVariable(0, stack.pop()); + break; + case 72: + op = "dstore_1"; + setVariable(1, stack.pop()); + break; + case 73: + op = "dstore_2"; + setVariable(2, stack.pop()); + break; + case 74: + op = "dstore_3"; + setVariable(3, stack.pop()); + break; + case 75: + op = "astore_0"; + setVariable(0, stack.pop()); + break; + case 76: + op = "astore_1"; + setVariable(1, stack.pop()); + break; + case 77: + op = "astore_2"; + setVariable(2, stack.pop()); + break; + case 78: + op = "astore_3"; + setVariable(3, stack.pop()); + break; + case 79: { + // String value = stack.pop(); + // String index = stack.pop(); + // String ref = stack.pop(); + op = "iastore"; + // TODO side effect - not supported + break; + } + case 80: + op = "lastore"; + // TODO side effect - not supported + break; + case 81: + op = "fastore"; + // TODO side effect - not supported + break; + case 82: + op = "dastore"; + // TODO side effect - not supported + break; + case 83: + op = "aastore"; + // TODO side effect - not supported + break; + case 84: + op = "bastore"; + // TODO side effect - not supported + break; + case 85: + op = "castore"; + // TODO side effect - not supported + break; + case 86: + op = "sastore"; + // TODO side effect - not supported + break; + case 87: + op = "pop"; + stack.pop(); + break; + case 88: + op = "pop2"; + // TODO currently we don't know the stack types + stack.pop(); + stack.pop(); + break; + case 89: { + op = "dup"; + Token x = stack.pop(); + stack.push(x); + stack.push(x); + break; + } + case 90: { + op = "dup_x1"; + Token a = stack.pop(); + Token b = stack.pop(); + stack.push(a); + stack.push(b); + stack.push(a); + break; + } + case 91: { + // TODO currently we don't know the stack types + op = "dup_x2"; + Token a = stack.pop(); + Token b = stack.pop(); + Token c = stack.pop(); + stack.push(a); + stack.push(c); + stack.push(b); + stack.push(a); + break; + } + case 92: { + // TODO currently we don't know the stack types + op = "dup2"; + Token a = stack.pop(); + Token b = stack.pop(); + stack.push(b); + stack.push(a); + stack.push(b); + stack.push(a); + break; + } + case 93: { + // TODO currently we don't know the stack types + op = "dup2_x1"; + Token a = stack.pop(); + Token b = stack.pop(); + Token c = stack.pop(); + stack.push(b); + stack.push(a); + stack.push(c); + stack.push(b); + stack.push(a); + break; + } + case 94: { + // TODO currently we don't know the stack types + op = "dup2_x2"; + Token a = stack.pop(); + Token b = stack.pop(); + Token c = stack.pop(); + Token d = stack.pop(); + stack.push(b); + stack.push(a); + stack.push(d); + stack.push(c); + stack.push(b); + stack.push(a); + break; + } + case 95: { + op = "swap"; + Token a = stack.pop(); + Token b = stack.pop(); + stack.push(a); + stack.push(b); + break; + } + case 96: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "iadd"; + stack.push(Operation.get(a, Operation.Type.ADD, b)); + break; + } + case 97: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "ladd"; + stack.push(Operation.get(a, Operation.Type.ADD, b)); + break; + } + case 98: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "fadd"; + stack.push(Operation.get(a, Operation.Type.ADD, b)); + break; + } + case 99: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "dadd"; + stack.push(Operation.get(a, Operation.Type.ADD, b)); + break; + } + case 100: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "isub"; + stack.push(Operation.get(a, Operation.Type.SUBTRACT, b)); + break; + } + case 101: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "lsub"; + stack.push(Operation.get(a, Operation.Type.SUBTRACT, b)); + break; + } + case 102: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "fsub"; + stack.push(Operation.get(a, Operation.Type.SUBTRACT, b)); + break; + } + case 103: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "dsub"; + stack.push(Operation.get(a, Operation.Type.SUBTRACT, b)); + break; + } + case 104: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "imul"; + stack.push(Operation.get(a, Operation.Type.MULTIPLY, b)); + break; + } + case 105: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "lmul"; + stack.push(Operation.get(a, Operation.Type.MULTIPLY, b)); + break; + } + case 106: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "fmul"; + stack.push(Operation.get(a, Operation.Type.MULTIPLY, b)); + break; + } + case 107: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "dmul"; + stack.push(Operation.get(a, Operation.Type.MULTIPLY, b)); + break; + } + case 108: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "idiv"; + stack.push(Operation.get(a, Operation.Type.DIVIDE, b)); + break; + } + case 109: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "ldiv"; + stack.push(Operation.get(a, Operation.Type.DIVIDE, b)); + break; + } + case 110: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "fdiv"; + stack.push(Operation.get(a, Operation.Type.DIVIDE, b)); + break; + } + case 111: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "ddiv"; + stack.push(Operation.get(a, Operation.Type.DIVIDE, b)); + break; + } + case 112: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "irem"; + stack.push(Operation.get(a, Operation.Type.MOD, b)); + break; + } + case 113: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "lrem"; + stack.push(Operation.get(a, Operation.Type.MOD, b)); + break; + } + case 114: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "frem"; + stack.push(Operation.get(a, Operation.Type.MOD, b)); + break; + } + case 115: { + Token b = stack.pop(); + Token a = stack.pop(); + op = "drem"; + stack.push(Operation.get(a, Operation.Type.MOD, b)); + break; + } + // case 116: + // op = "ineg"; + // break; + // case 117: + // op = "lneg"; + // break; + // case 118: + // op = "fneg"; + // break; + // case 119: + // op = "dneg"; + // break; + // case 120: + // op = "ishl"; + // break; + // case 121: + // op = "lshl"; + // break; + // case 122: + // op = "ishr"; + // break; + // case 123: + // op = "lshr"; + // break; + // case 124: + // op = "iushr"; + // break; + // case 125: + // op = "lushr"; + // break; + // case 126: + // op = "iand"; + // break; + // case 127: + // op = "land"; + // break; + // case 128: + // op = "ior"; + // break; + // case 129: + // op = "lor"; + // break; + // case 130: + // op = "ixor"; + // break; + // case 131: + // op = "lxor"; + // break; + // case 132: { + // int var = readByte(); + // int off = (byte) readByte(); + // op = "iinc " + var + " " + off; + // break; + // } + // case 133: + // op = "i2l"; + // break; + // case 134: + // op = "i2f"; + // break; + // case 135: + // op = "i2d"; + // break; + // case 136: + // op = "l2i"; + // break; + // case 137: + // op = "l2f"; + // break; + // case 138: + // op = "l2d"; + // break; + // case 139: + // op = "f2i"; + // break; + // case 140: + // op = "f2l"; + // break; + // case 141: + // op = "f2d"; + // break; + // case 142: + // op = "d2i"; + // break; + // case 143: + // op = "d2l"; + // break; + // case 144: + // op = "d2f"; + // break; + // case 145: + // op = "i2b"; + // break; + // case 146: + // op = "i2c"; + // break; + // case 147: + // op = "i2s"; + // break; + case 148: { + Token b = stack.pop(), a = stack.pop(); + stack.push(new Function("SIGN", Operation.get(a, Operation.Type.SUBTRACT, b))); + op = "lcmp"; + break; + } + // case 149: + // op = "fcmpl"; + // break; + // case 150: + // op = "fcmpg"; + // break; + // case 151: + // op = "dcmpl"; + // break; + // case 152: + // op = "dcmpg"; + // break; + case 153: + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + stack.push(Operation.get(stack.pop(), Operation.Type.EQUALS, ConstantNumber.get(0))); + op = "ifeq " + nextPc; + break; + case 154: + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + stack.push(Operation.get(stack.pop(), Operation.Type.NOT_EQUALS, ConstantNumber.get(0))); + op = "ifne " + nextPc; + break; + case 155: + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + stack.push(Operation.get(stack.pop(), Operation.Type.SMALLER, ConstantNumber.get(0))); + op = "iflt " + nextPc; + break; + case 156: + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + stack.push(Operation.get(stack.pop(), Operation.Type.BIGGER_EQUALS, ConstantNumber.get(0))); + op = "ifge " + nextPc; + break; + case 157: + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + stack.push(Operation.get(stack.pop(), Operation.Type.BIGGER, ConstantNumber.get(0))); + op = "ifgt " + nextPc; + break; + case 158: + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + stack.push(Operation.get(stack.pop(), Operation.Type.SMALLER_EQUALS, ConstantNumber.get(0))); + op = "ifle " + nextPc; + break; + case 159: { + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + Token b = stack.pop(), a = stack.pop(); + stack.push(Operation.get(a, Operation.Type.EQUALS, b)); + op = "if_icmpeq " + nextPc; + break; + } + case 160: { + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + Token b = stack.pop(), a = stack.pop(); + stack.push(Operation.get(a, Operation.Type.NOT_EQUALS, b)); + op = "if_icmpne " + nextPc; + break; + } + case 161: { + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + Token b = stack.pop(), a = stack.pop(); + stack.push(Operation.get(a, Operation.Type.SMALLER, b)); + op = "if_icmplt " + nextPc; + break; + } + case 162: { + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + Token b = stack.pop(), a = stack.pop(); + stack.push(Operation.get(a, Operation.Type.BIGGER_EQUALS, b)); + op = "if_icmpge " + nextPc; + break; + } + case 163: { + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + Token b = stack.pop(), a = stack.pop(); + stack.push(Operation.get(a, Operation.Type.BIGGER, b)); + op = "if_icmpgt " + nextPc; + break; + } + case 164: { + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + Token b = stack.pop(), a = stack.pop(); + stack.push(Operation.get(a, Operation.Type.SMALLER_EQUALS, b)); + op = "if_icmple " + nextPc; + break; + } + case 165: { + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + Token b = stack.pop(), a = stack.pop(); + stack.push(Operation.get(a, Operation.Type.EQUALS, b)); + op = "if_acmpeq " + nextPc; + break; + } + case 166: { + condition = true; + nextPc = getAbsolutePos(pos, readShort()); + Token b = stack.pop(), a = stack.pop(); + stack.push(Operation.get(a, Operation.Type.NOT_EQUALS, b)); + op = "if_acmpne " + nextPc; + break; + } + case 167: + nextPc = getAbsolutePos(pos, readShort()); + op = "goto " + nextPc; + break; + // case 168: + // // TODO not supported yet + // op = "jsr " + getAbsolutePos(pos, readShort()); + // break; + // case 169: + // // TODO not supported yet + // op = "ret " + readByte(); + // break; + // case 170: { + // int start = pos; + // pos += 4 - ((pos - startByteCode) & 3); + // int def = readInt(); + // int low = readInt(), high = readInt(); + // int n = high - low + 1; + // op = "tableswitch default:" + getAbsolutePos(start, def); + // StringBuilder buff = new StringBuilder(); + // for (int i = 0; i < n; i++) { + // buff.append(' ').append(low++). + // append(":"). + // append(getAbsolutePos(start, readInt())); + // } + // op += buff.toString(); + // // pos += n * 4; + // break; + // } + // case 171: { + // int start = pos; + // pos += 4 - ((pos - startByteCode) & 3); + // int def = readInt(); + // int n = readInt(); + // op = "lookupswitch default:" + getAbsolutePos(start, def); + // StringBuilder buff = new StringBuilder(); + // for (int i = 0; i < n; i++) { + // buff.append(' '). + // append(readInt()). + // append(":"). + // append(getAbsolutePos(start, readInt())); + // } + // op += buff.toString(); + // // pos += n * 8; + // break; + // } + case 172: + op = "ireturn"; + endOfMethod = true; + break; + case 173: + op = "lreturn"; + endOfMethod = true; + break; + case 174: + op = "freturn"; + endOfMethod = true; + break; + case 175: + op = "dreturn"; + endOfMethod = true; + break; + case 176: + op = "areturn"; + endOfMethod = true; + break; + case 177: + op = "return"; + // no value returned + stack.push(null); + endOfMethod = true; + break; + // case 178: + // op = "getstatic " + getField(readShort()); + // break; + // case 179: + // op = "putstatic " + getField(readShort()); + // break; + case 180: { + String field = getField(readShort()); + Token p = stack.pop(); + String s = p + "." + field.substring(field.lastIndexOf('.') + 1, field.indexOf(' ')); + if (s.startsWith("this.")) { + s = s.substring(5); + } + stack.push(Variable.get(s, fieldMap.get(s))); + op = "getfield " + field; + break; + } + // case 181: + // op = "putfield " + getField(readShort()); + // break; + case 182: { + String method = getMethod(readShort()); + op = "invokevirtual " + method; + if (method.equals("java/lang/String.equals (Ljava/lang/Object;)Z")) { + Token a = stack.pop(); + Token b = stack.pop(); + stack.push(Operation.get(a, Operation.Type.EQUALS, b)); + } else if (method.equals("java/lang/Integer.intValue ()I")) { + // ignore + } else if (method.equals("java/lang/Long.longValue ()J")) { + // ignore + } + break; + } + case 183: { + String method = getMethod(readShort()); + op = "invokespecial " + method; + break; + } + case 184: + op = "invokestatic " + getMethod(readShort()); + break; + // case 185: { + // int methodRef = readShort(); + // readByte(); + // readByte(); + // op = "invokeinterface " + getMethod(methodRef); + // break; + // } + case 187: { + String className = constantPool[constantPool[readShort()].intValue()].toString(); + op = "new " + className; + break; + } + // case 188: + // op = "newarray " + readByte(); + // break; + // case 189: + // op = "anewarray " + cpString[readShort()]; + // break; + // case 190: + // op = "arraylength"; + // break; + // case 191: + // op = "athrow"; + // break; + // case 192: + // op = "checkcast " + cpString[readShort()]; + // break; + // case 193: + // op = "instanceof " + cpString[readShort()]; + // break; + // case 194: + // op = "monitorenter"; + // break; + // case 195: + // op = "monitorexit"; + // break; + // case 196: { + // opCode = readByte(); + // switch (opCode) { + // case 21: + // op = "wide iload " + readShort(); + // break; + // case 22: + // op = "wide lload " + readShort(); + // break; + // case 23: + // op = "wide fload " + readShort(); + // break; + // case 24: + // op = "wide dload " + readShort(); + // break; + // case 25: + // op = "wide aload " + readShort(); + // break; + // case 54: + // op = "wide istore " + readShort(); + // break; + // case 55: + // op = "wide lstore " + readShort(); + // break; + // case 56: + // op = "wide fstore " + readShort(); + // break; + // case 57: + // op = "wide dstore " + readShort(); + // break; + // case 58: + // op = "wide astore " + readShort(); + // break; + // case 132: { + // int var = readShort(); + // int off = (short) readShort(); + // op = "wide iinc " + var + " " + off; + // break; + // } + // case 169: + // op = "wide ret " + readShort(); + // break; + // default: + // throw new IciqlException( + // "Unsupported wide opCode " + opCode); + // } + // break; + // } + // case 197: + // op = "multianewarray " + cpString[readShort()] + " " + readByte(); + // break; + // case 198: { + // condition = true; + // nextPc = getAbsolutePos(pos, readShort()); + // Token a = stack.pop(); + // stack.push("(" + a + " IS NULL)"); + // op = "ifnull " + nextPc; + // break; + // } + // case 199: { + // condition = true; + // nextPc = getAbsolutePos(pos, readShort()); + // Token a = stack.pop(); + // stack.push("(" + a + " IS NOT NULL)"); + // op = "ifnonnull " + nextPc; + // break; + // } + case 200: + op = "goto_w " + getAbsolutePos(pos, readInt()); + break; + case 201: + op = "jsr_w " + getAbsolutePos(pos, readInt()); + break; + default: + throw new IciqlException("Unsupported opCode " + opCode); + } + debug(" " + startPos + ": " + op); + } - private void setVariable(int x, Token value) { - while (x >= variables.size()) { - variables.add(Variable.get("p" + variables.size(), null)); - } - variables.set(x, value); - } + private void setVariable(int x, Token value) { + while (x >= variables.size()) { + variables.add(Variable.get("p" + variables.size(), null)); + } + variables.set(x, value); + } - private Token getVariable(int x) { - if (x == 0) { - return Variable.THIS; - } - while (x >= variables.size()) { - variables.add(Variable.get("p" + variables.size(), null)); - } - return variables.get(x); - } + private Token getVariable(int x) { + if (x == 0) { + return Variable.THIS; + } + while (x >= variables.size()) { + variables.add(Variable.get("p" + variables.size(), null)); + } + return variables.get(x); + } - private String getField(int fieldRef) { - int field = constantPool[fieldRef].intValue(); - int classIndex = field >>> 16; - int nameAndType = constantPool[field & 0xffff].intValue(); - String className = constantPool[constantPool[classIndex].intValue()] + "." - + constantPool[nameAndType >>> 16] + " " + constantPool[nameAndType & 0xffff]; - return className; - } + private String getField(int fieldRef) { + int field = constantPool[fieldRef].intValue(); + int classIndex = field >>> 16; + int nameAndType = constantPool[field & 0xffff].intValue(); + String className = constantPool[constantPool[classIndex].intValue()] + "." + + constantPool[nameAndType >>> 16] + " " + constantPool[nameAndType & 0xffff]; + return className; + } - private String getMethod(int methodRef) { - int method = constantPool[methodRef].intValue(); - int classIndex = method >>> 16; - int nameAndType = constantPool[method & 0xffff].intValue(); - String className = constantPool[constantPool[classIndex].intValue()] + "." - + constantPool[nameAndType >>> 16] + " " + constantPool[nameAndType & 0xffff]; - return className; - } + private String getMethod(int methodRef) { + int method = constantPool[methodRef].intValue(); + int classIndex = method >>> 16; + int nameAndType = constantPool[method & 0xffff].intValue(); + String className = constantPool[constantPool[classIndex].intValue()] + "." + + constantPool[nameAndType >>> 16] + " " + constantPool[nameAndType & 0xffff]; + return className; + } - private Constant getConstant(int constantRef) { - Constant c = constantPool[constantRef]; - switch (c.getType()) { - case INT: - case FLOAT: - case DOUBLE: - case LONG: - return c; - case STRING_REF: - return constantPool[c.intValue()]; - default: - throw new IciqlException("Not a constant: " + constantRef); - } - } + private Constant getConstant(int constantRef) { + Constant c = constantPool[constantRef]; + switch (c.getType()) { + case INT: + case FLOAT: + case DOUBLE: + case LONG: + return c; + case STRING_REF: + return constantPool[c.intValue()]; + default: + throw new IciqlException("Not a constant: " + constantRef); + } + } - private String readString() { - int size = readShort(); - byte[] buff = data; - int p = pos, end = p + size; - char[] chars = new char[size]; - int j = 0; - for (; p < end; j++) { - int x = buff[p++] & 0xff; - if (x < 0x80) { - chars[j] = (char) x; - } else if (x >= 0xe0) { - chars[j] = (char) (((x & 0xf) << 12) + ((buff[p++] & 0x3f) << 6) + (buff[p++] & 0x3f)); - } else { - chars[j] = (char) (((x & 0x1f) << 6) + (buff[p++] & 0x3f)); - } - } - pos = p; - return new String(chars, 0, j); - } + private String readString() { + int size = readShort(); + byte[] buff = data; + int p = pos, end = p + size; + char[] chars = new char[size]; + int j = 0; + for (; p < end; j++) { + int x = buff[p++] & 0xff; + if (x < 0x80) { + chars[j] = (char) x; + } else if (x >= 0xe0) { + chars[j] = (char) (((x & 0xf) << 12) + ((buff[p++] & 0x3f) << 6) + (buff[p++] & 0x3f)); + } else { + chars[j] = (char) (((x & 0x1f) << 6) + (buff[p++] & 0x3f)); + } + } + pos = p; + return new String(chars, 0, j); + } - private int getAbsolutePos(int start, int offset) { - return start - startByteCode - 1 + (short) offset; - } + private int getAbsolutePos(int start, int offset) { + return start - startByteCode - 1 + (short) offset; + } - private int readByte() { - return data[pos++] & 0xff; - } + private int readByte() { + return data[pos++] & 0xff; + } - private int readShort() { - byte[] buff = data; - return ((buff[pos++] & 0xff) << 8) + (buff[pos++] & 0xff); - } + private int readShort() { + byte[] buff = data; + return ((buff[pos++] & 0xff) << 8) + (buff[pos++] & 0xff); + } - private int readInt() { - byte[] buff = data; - return (buff[pos++] << 24) + ((buff[pos++] & 0xff) << 16) + ((buff[pos++] & 0xff) << 8) - + (buff[pos++] & 0xff); - } + private int readInt() { + byte[] buff = data; + return (buff[pos++] << 24) + ((buff[pos++] & 0xff) << 16) + ((buff[pos++] & 0xff) << 8) + + (buff[pos++] & 0xff); + } - private long readLong() { - return ((long) (readInt()) << 32) + (readInt() & 0xffffffffL); - } + private long readLong() { + return ((long) (readInt()) << 32) + (readInt() & 0xffffffffL); + } } diff --git a/src/main/java/com/iciql/bytecode/Constant.java b/src/main/java/com/iciql/bytecode/Constant.java index 65cd66b..7adf8a9 100644 --- a/src/main/java/com/iciql/bytecode/Constant.java +++ b/src/main/java/com/iciql/bytecode/Constant.java @@ -24,15 +24,15 @@ import com.iciql.Token; */ public interface Constant extends Token { - /** - * The constant pool type. - */ - enum Type { - STRING, INT, FLOAT, DOUBLE, LONG, CLASS_REF, STRING_REF, FIELD_REF, METHOD_REF, INTERFACE_METHOD_REF, NAME_AND_TYPE - } + /** + * The constant pool type. + */ + enum Type { + STRING, INT, FLOAT, DOUBLE, LONG, CLASS_REF, STRING_REF, FIELD_REF, METHOD_REF, INTERFACE_METHOD_REF, NAME_AND_TYPE + } - Constant.Type getType(); + Constant.Type getType(); - int intValue(); + int intValue(); } diff --git a/src/main/java/com/iciql/bytecode/ConstantNumber.java b/src/main/java/com/iciql/bytecode/ConstantNumber.java index 934de3d..b0c25b8 100644 --- a/src/main/java/com/iciql/bytecode/ConstantNumber.java +++ b/src/main/java/com/iciql/bytecode/ConstantNumber.java @@ -25,46 +25,46 @@ import com.iciql.SQLStatement; */ public class ConstantNumber implements Constant { - private final String value; - private final Type type; - private final long longValue; + private final String value; + private final Type type; + private final long longValue; - private ConstantNumber(String value, long longValue, Type type) { - this.value = value; - this.longValue = longValue; - this.type = type; - } + private ConstantNumber(String value, long longValue, Type type) { + this.value = value; + this.longValue = longValue; + this.type = type; + } - static ConstantNumber get(String v) { - return new ConstantNumber(v, 0, Type.STRING); - } + static ConstantNumber get(String v) { + return new ConstantNumber(v, 0, Type.STRING); + } - static ConstantNumber get(int v) { - return new ConstantNumber("" + v, v, Type.INT); - } + static ConstantNumber get(int v) { + return new ConstantNumber("" + v, v, Type.INT); + } - static ConstantNumber get(long v) { - return new ConstantNumber("" + v, v, Type.LONG); - } + static ConstantNumber get(long v) { + return new ConstantNumber("" + v, v, Type.LONG); + } - static ConstantNumber get(String s, long x, Type type) { - return new ConstantNumber(s, x, type); - } + static ConstantNumber get(String s, long x, Type type) { + return new ConstantNumber(s, x, type); + } - public int intValue() { - return (int) longValue; - } + public int intValue() { + return (int) longValue; + } - public String toString() { - return value; - } + public String toString() { + return value; + } - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL(toString()); - } + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL(toString()); + } - public Constant.Type getType() { - return type; - } + public Constant.Type getType() { + return type; + } } diff --git a/src/main/java/com/iciql/bytecode/ConstantString.java b/src/main/java/com/iciql/bytecode/ConstantString.java index 985f97d..937082b 100644 --- a/src/main/java/com/iciql/bytecode/ConstantString.java +++ b/src/main/java/com/iciql/bytecode/ConstantString.java @@ -26,30 +26,30 @@ import com.iciql.util.StringUtils; */ public class ConstantString implements Constant { - private final String value; + private final String value; - private ConstantString(String value) { - this.value = value; - } + private ConstantString(String value) { + this.value = value; + } - static ConstantString get(String v) { - return new ConstantString(v); - } + static ConstantString get(String v) { + return new ConstantString(v); + } - public String toString() { - return value; - } + public String toString() { + return value; + } - public int intValue() { - return 0; - } + public int intValue() { + return 0; + } - public void appendSQL(SQLStatement stat, Query query) { - stat.appendSQL(StringUtils.quoteStringSQL(value)); - } + public void appendSQL(SQLStatement stat, Query query) { + stat.appendSQL(StringUtils.quoteStringSQL(value)); + } - public Constant.Type getType() { - return Constant.Type.STRING; - } + public Constant.Type getType() { + return Constant.Type.STRING; + } } diff --git a/src/main/java/com/iciql/bytecode/Function.java b/src/main/java/com/iciql/bytecode/Function.java index 56a55ea..31506c8 100644 --- a/src/main/java/com/iciql/bytecode/Function.java +++ b/src/main/java/com/iciql/bytecode/Function.java @@ -26,22 +26,22 @@ import com.iciql.Token; */ class Function implements Token { - private final String name; - private final Token expr; + private final String name; + private final Token expr; - Function(String name, Token expr) { - this.name = name; - this.expr = expr; - } + Function(String name, Token expr) { + this.name = name; + this.expr = expr; + } - public String toString() { - return name + "(" + expr + ")"; - } + public String toString() { + return name + "(" + expr + ")"; + } - public void appendSQL(SQLStatement stat, Query query) { - // untested - stat.appendSQL(name + "("); - expr.appendSQL(stat, query); - stat.appendSQL(")"); - } + public void appendSQL(SQLStatement stat, Query query) { + // untested + stat.appendSQL(name + "("); + expr.appendSQL(stat, query); + stat.appendSQL(")"); + } } diff --git a/src/main/java/com/iciql/bytecode/Not.java b/src/main/java/com/iciql/bytecode/Not.java index ab5ab84..d6d5333 100644 --- a/src/main/java/com/iciql/bytecode/Not.java +++ b/src/main/java/com/iciql/bytecode/Not.java @@ -26,30 +26,30 @@ import com.iciql.Token; */ public class Not implements Token { - private Token expr; - - private Not(Token expr) { - this.expr = expr; - } - - static Token get(Token expr) { - if (expr instanceof Not) { - return ((Not) expr).expr; - } else if (expr instanceof Operation) { - return ((Operation) expr).reverse(); - } - return new Not(expr); - } - - Token not() { - return expr; - } - - public void appendSQL(SQLStatement stat, Query query) { - // untested - stat.appendSQL("NOT("); - expr.appendSQL(stat, query); - stat.appendSQL(")"); - } + private Token expr; + + private Not(Token expr) { + this.expr = expr; + } + + static Token get(Token expr) { + if (expr instanceof Not) { + return ((Not) expr).expr; + } else if (expr instanceof Operation) { + return ((Operation) expr).reverse(); + } + return new Not(expr); + } + + Token not() { + return expr; + } + + public void appendSQL(SQLStatement stat, Query query) { + // untested + stat.appendSQL("NOT("); + expr.appendSQL(stat, query); + stat.appendSQL(")"); + } } diff --git a/src/main/java/com/iciql/bytecode/Null.java b/src/main/java/com/iciql/bytecode/Null.java index a28de56..a036b6e 100644 --- a/src/main/java/com/iciql/bytecode/Null.java +++ b/src/main/java/com/iciql/bytecode/Null.java @@ -26,19 +26,19 @@ import com.iciql.Token; */ public class Null implements Token { - static final Null INSTANCE = new Null(); + static final Null INSTANCE = new Null(); - private Null() { - // don't allow to create new instances - } + private Null() { + // don't allow to create new instances + } - public String toString() { - return "null"; - } + public String toString() { + return "null"; + } - public void appendSQL(SQLStatement stat, Query query) { - // untested - stat.appendSQL("NULL"); - } + public void appendSQL(SQLStatement stat, Query query) { + // untested + stat.appendSQL("NULL"); + } } diff --git a/src/main/java/com/iciql/bytecode/Operation.java b/src/main/java/com/iciql/bytecode/Operation.java index 7cd42d9..5b385b6 100644 --- a/src/main/java/com/iciql/bytecode/Operation.java +++ b/src/main/java/com/iciql/bytecode/Operation.java @@ -26,86 +26,86 @@ import com.iciql.Token; */ class Operation implements Token { - /** - * The operation type. - */ - enum Type { - EQUALS("=") { - Type reverse() { - return NOT_EQUALS; - } - }, - NOT_EQUALS("<>") { - Type reverse() { - return EQUALS; - } - }, - BIGGER(">") { - Type reverse() { - return SMALLER_EQUALS; - } - }, - BIGGER_EQUALS(">=") { - Type reverse() { - return SMALLER; - } - }, - SMALLER_EQUALS("<=") { - Type reverse() { - return BIGGER; - } - }, - SMALLER("<") { - Type reverse() { - return BIGGER_EQUALS; - } - }, - ADD("+"), SUBTRACT("-"), MULTIPLY("*"), DIVIDE("/"), MOD("%"); - - private String name; - - Type(String name) { - this.name = name; - } - - public String toString() { - return name; - } - - Type reverse() { - return null; - } - - } - - private final Token left, right; - private final Type op; - - private Operation(Token left, Type op, Token right) { - this.left = left; - this.op = op; - this.right = right; - } - - static Token get(Token left, Type op, Token right) { - if (op == Type.NOT_EQUALS && "0".equals(right.toString())) { - return left; - } - return new Operation(left, op, right); - } - - public String toString() { - return left + " " + op + " " + right; - } - - public Token reverse() { - return get(left, op.reverse(), right); - } - - public void appendSQL(SQLStatement stat, Query query) { - left.appendSQL(stat, query); - stat.appendSQL(op.toString()); - right.appendSQL(stat, query); - } + /** + * The operation type. + */ + enum Type { + EQUALS("=") { + Type reverse() { + return NOT_EQUALS; + } + }, + NOT_EQUALS("<>") { + Type reverse() { + return EQUALS; + } + }, + BIGGER(">") { + Type reverse() { + return SMALLER_EQUALS; + } + }, + BIGGER_EQUALS(">=") { + Type reverse() { + return SMALLER; + } + }, + SMALLER_EQUALS("<=") { + Type reverse() { + return BIGGER; + } + }, + SMALLER("<") { + Type reverse() { + return BIGGER_EQUALS; + } + }, + ADD("+"), SUBTRACT("-"), MULTIPLY("*"), DIVIDE("/"), MOD("%"); + + private String name; + + Type(String name) { + this.name = name; + } + + public String toString() { + return name; + } + + Type reverse() { + return null; + } + + } + + private final Token left, right; + private final Type op; + + private Operation(Token left, Type op, Token right) { + this.left = left; + this.op = op; + this.right = right; + } + + static Token get(Token left, Type op, Token right) { + if (op == Type.NOT_EQUALS && "0".equals(right.toString())) { + return left; + } + return new Operation(left, op, right); + } + + public String toString() { + return left + " " + op + " " + right; + } + + public Token reverse() { + return get(left, op.reverse(), right); + } + + public void appendSQL(SQLStatement stat, Query query) { + left.appendSQL(stat, query); + stat.appendSQL(op.toString()); + right.appendSQL(stat, query); + } } diff --git a/src/main/java/com/iciql/bytecode/Or.java b/src/main/java/com/iciql/bytecode/Or.java index 37da2a6..00e326e 100644 --- a/src/main/java/com/iciql/bytecode/Or.java +++ b/src/main/java/com/iciql/bytecode/Or.java @@ -26,22 +26,22 @@ import com.iciql.Token; */ public class Or implements Token { - private final Token left, right; - - private Or(Token left, Token right) { - this.left = left; - this.right = right; - } - - static Or get(Token left, Token right) { - return new Or(left, right); - } - - public void appendSQL(SQLStatement stat, Query query) { - // untested - left.appendSQL(stat, query); - stat.appendSQL(" OR "); - right.appendSQL(stat, query); - } + private final Token left, right; + + private Or(Token left, Token right) { + this.left = left; + this.right = right; + } + + static Or get(Token left, Token right) { + return new Or(left, right); + } + + public void appendSQL(SQLStatement stat, Query query) { + // untested + left.appendSQL(stat, query); + stat.appendSQL(" OR "); + right.appendSQL(stat, query); + } } diff --git a/src/main/java/com/iciql/bytecode/Variable.java b/src/main/java/com/iciql/bytecode/Variable.java index f3dbc01..da2a634 100644 --- a/src/main/java/com/iciql/bytecode/Variable.java +++ b/src/main/java/com/iciql/bytecode/Variable.java @@ -26,26 +26,26 @@ import com.iciql.Token; */ public class Variable implements Token { - static final Variable THIS = new Variable("this", null); + static final Variable THIS = new Variable("this", null); - private final String name; - private final Object obj; + private final String name; + private final Object obj; - private Variable(String name, Object obj) { - this.name = name; - this.obj = obj; - } + private Variable(String name, Object obj) { + this.name = name; + this.obj = obj; + } - static Variable get(String name, Object obj) { - return new Variable(name, obj); - } + static Variable get(String name, Object obj) { + return new Variable(name, obj); + } - public String toString() { - return name; - } + public String toString() { + return name; + } - public void appendSQL(SQLStatement stat, Query query) { - query.appendSQL(stat, null, obj); - } + public void appendSQL(SQLStatement stat, Query query) { + query.appendSQL(stat, null, obj); + } } diff --git a/src/main/java/com/iciql/bytecode/package.html b/src/main/java/com/iciql/bytecode/package.html index 5107481..b5a73a1 100644 --- a/src/main/java/com/iciql/bytecode/package.html +++ b/src/main/java/com/iciql/bytecode/package.html @@ -16,8 +16,9 @@ limitations under the License. --> - -Javadoc package documentation + + + Javadoc package documentation The class decompiler for natural syntax iciql clauses. diff --git a/src/main/java/com/iciql/package.html b/src/main/java/com/iciql/package.html index 769837b..4afe927 100644 --- a/src/main/java/com/iciql/package.html +++ b/src/main/java/com/iciql/package.html @@ -16,8 +16,9 @@ limitations under the License. --> - -Javadoc package documentation + + + Javadoc package documentation iciql (pronounced "icicle") is a Java JDBC SQL statement generator and simple object mapper diff --git a/src/main/java/com/iciql/util/GenerateModels.java b/src/main/java/com/iciql/util/GenerateModels.java index eac9f6c..b8dcaa4 100644 --- a/src/main/java/com/iciql/util/GenerateModels.java +++ b/src/main/java/com/iciql/util/GenerateModels.java @@ -17,6 +17,9 @@ package com.iciql.util; +import com.iciql.Db; +import com.iciql.DbInspector; + import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; @@ -29,165 +32,152 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; -import com.iciql.Db; -import com.iciql.DbInspector; - /** * Generates iciql models. */ public class GenerateModels { - /** - * The output stream where this tool writes to. - */ - protected PrintStream out = System.out; + /** + * The output stream where this tool writes to. + */ + protected PrintStream out = System.out; - public static void main(String... args) { - GenerateModels tool = new GenerateModels(); - try { - tool.runTool(args); - } catch (SQLException e) { - tool.out.print("Error: "); - tool.out.println(e.getMessage()); - tool.out.println(); - tool.showUsage(); - } - } + public static void main(String... args) { + GenerateModels tool = new GenerateModels(); + try { + tool.runTool(args); + } catch (SQLException e) { + tool.out.print("Error: "); + tool.out.println(e.getMessage()); + tool.out.println(); + tool.showUsage(); + } + } - public void runTool(String... args) throws SQLException { - String url = null; - String user = "sa"; - String password = ""; - String schema = null; - String table = null; - String packageName = ""; - String folder = null; - boolean annotateSchema = true; - boolean trimStrings = false; - for (int i = 0; args != null && i < args.length; i++) { - String arg = args[i]; - if (arg.equals("-url")) { - url = args[++i]; - } else if (arg.equals("-user")) { - user = args[++i]; - } else if (arg.equals("-password")) { - password = args[++i]; - } else if (arg.equals("-schema")) { - schema = args[++i]; - } else if (arg.equals("-table")) { - table = args[++i]; - } else if (arg.equals("-package")) { - packageName = args[++i]; - } else if (arg.equals("-folder")) { - folder = args[++i]; - } else if (arg.equals("-annotateSchema")) { - try { - annotateSchema = Boolean.parseBoolean(args[++i]); - } catch (Throwable t) { - throw new SQLException("Can not parse -annotateSchema value"); - } - } else if (arg.equals("-trimStrings")) { - try { - trimStrings = Boolean.parseBoolean(args[++i]); - } catch (Throwable t) { - throw new SQLException("Can not parse -trimStrings value"); - } - } else { - throwUnsupportedOption(arg); - } - } - if (url == null) { - throw new SQLException("URL not set"); - } - execute(url, user, password, schema, table, packageName, folder, annotateSchema, trimStrings); - } + public void runTool(String... args) throws SQLException { + String url = null; + String user = "sa"; + String password = ""; + String schema = null; + String table = null; + String packageName = ""; + String folder = null; + boolean annotateSchema = true; + boolean trimStrings = false; + for (int i = 0; args != null && i < args.length; i++) { + String arg = args[i]; + if (arg.equals("-url")) { + url = args[++i]; + } else if (arg.equals("-user")) { + user = args[++i]; + } else if (arg.equals("-password")) { + password = args[++i]; + } else if (arg.equals("-schema")) { + schema = args[++i]; + } else if (arg.equals("-table")) { + table = args[++i]; + } else if (arg.equals("-package")) { + packageName = args[++i]; + } else if (arg.equals("-folder")) { + folder = args[++i]; + } else if (arg.equals("-annotateSchema")) { + try { + annotateSchema = Boolean.parseBoolean(args[++i]); + } catch (Throwable t) { + throw new SQLException("Can not parse -annotateSchema value"); + } + } else if (arg.equals("-trimStrings")) { + try { + trimStrings = Boolean.parseBoolean(args[++i]); + } catch (Throwable t) { + throw new SQLException("Can not parse -trimStrings value"); + } + } else { + throwUnsupportedOption(arg); + } + } + if (url == null) { + throw new SQLException("URL not set"); + } + execute(url, user, password, schema, table, packageName, folder, annotateSchema, trimStrings); + } - /** - * Generates models from the database. - * - * @param url - * the database URL - * @param user - * the user name - * @param password - * the password - * @param schema - * the schema to read from. null for all schemas. - * @param table - * the table to model. null for all tables within schema. - * @param packageName - * the package name of the model classes. - * @param folder - * destination folder for model classes (package path not - * included) - * @param annotateSchema - * includes the schema in the table model annotations - * @param trimStrings - * automatically trim strings that exceed maxLength - */ - public static void execute(String url, String user, String password, String schema, String table, - String packageName, String folder, boolean annotateSchema, boolean trimStrings) - throws SQLException { - try { - Db db; - if (password == null) { - db = Db.open(url, user, (String) null); - } else { - db = Db.open(url, user, password); - } - DbInspector inspector = new DbInspector(db); - List models = inspector.generateModel(schema, table, packageName, annotateSchema, - trimStrings); - File parentFile; - if (StringUtils.isNullOrEmpty(folder)) { - parentFile = new File(System.getProperty("user.dir")); - } else { - parentFile = new File(folder); - } - parentFile.mkdirs(); - Pattern p = Pattern.compile("class ([a-zA-Z0-9]+)"); - for (String model : models) { - Matcher m = p.matcher(model); - if (m.find()) { - String className = m.group().substring("class".length()).trim(); - File classFile = new File(parentFile, className + ".java"); - Writer o = new FileWriter(classFile, false); - PrintWriter writer = new PrintWriter(new BufferedWriter(o)); - writer.write(model); - writer.close(); - System.out.println("Generated " + classFile.getAbsolutePath()); - } - } - } catch (IOException io) { - throw new SQLException("could not generate model", io); - } - } + /** + * Generates models from the database. + * + * @param url the database URL + * @param user the user name + * @param password the password + * @param schema the schema to read from. null for all schemas. + * @param table the table to model. null for all tables within schema. + * @param packageName the package name of the model classes. + * @param folder destination folder for model classes (package path not + * included) + * @param annotateSchema includes the schema in the table model annotations + * @param trimStrings automatically trim strings that exceed maxLength + */ + public static void execute(String url, String user, String password, String schema, String table, + String packageName, String folder, boolean annotateSchema, boolean trimStrings) + throws SQLException { + try { + Db db; + if (password == null) { + db = Db.open(url, user, (String) null); + } else { + db = Db.open(url, user, password); + } + DbInspector inspector = new DbInspector(db); + List models = inspector.generateModel(schema, table, packageName, annotateSchema, + trimStrings); + File parentFile; + if (StringUtils.isNullOrEmpty(folder)) { + parentFile = new File(System.getProperty("user.dir")); + } else { + parentFile = new File(folder); + } + parentFile.mkdirs(); + Pattern p = Pattern.compile("class ([a-zA-Z0-9]+)"); + for (String model : models) { + Matcher m = p.matcher(model); + if (m.find()) { + String className = m.group().substring("class".length()).trim(); + File classFile = new File(parentFile, className + ".java"); + Writer o = new FileWriter(classFile, false); + PrintWriter writer = new PrintWriter(new BufferedWriter(o)); + writer.write(model); + writer.close(); + System.out.println("Generated " + classFile.getAbsolutePath()); + } + } + } catch (IOException io) { + throw new SQLException("could not generate model", io); + } + } - /** - * Throw a SQLException saying this command line option is not supported. - * - * @param option - * the unsupported option - * @return this method never returns normally - */ - protected SQLException throwUnsupportedOption(String option) throws SQLException { - showUsage(); - throw new SQLException("Unsupported option: " + option); - } + /** + * Throw a SQLException saying this command line option is not supported. + * + * @param option the unsupported option + * @return this method never returns normally + */ + protected SQLException throwUnsupportedOption(String option) throws SQLException { + showUsage(); + throw new SQLException("Unsupported option: " + option); + } - protected void showUsage() { - out.println("GenerateModels"); - out.println("Usage:"); - out.println(); - out.println("(*) -url jdbc:h2:~test"); - out.println(" -user "); - out.println(" -password "); - out.println(" -schema "); - out.println(" -table "); - out.println(" -package "); - out.println(" -folder "); - out.println(" -annotateSchema "); - out.println(" -trimStrings "); - } + protected void showUsage() { + out.println("GenerateModels"); + out.println("Usage:"); + out.println(); + out.println("(*) -url jdbc:h2:~test"); + out.println(" -user "); + out.println(" -password "); + out.println(" -schema "); + out.println(" -table "); + out.println(" -package "); + out.println(" -folder "); + out.println(" -annotateSchema "); + out.println(" -trimStrings "); + } } diff --git a/src/main/java/com/iciql/util/IciqlLogger.java b/src/main/java/com/iciql/util/IciqlLogger.java index d8005bb..2a2caf6 100644 --- a/src/main/java/com/iciql/util/IciqlLogger.java +++ b/src/main/java/com/iciql/util/IciqlLogger.java @@ -16,6 +16,8 @@ package com.iciql.util; +import com.iciql.IciqlException; + import java.text.DecimalFormat; import java.text.MessageFormat; import java.util.Set; @@ -23,192 +25,189 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; -import com.iciql.IciqlException; - /** * Utility class to optionally log generated statements to IciqlListeners.
* Statement logging is disabled by default. *

* This class also tracks the counts for generated statements by major type. - * */ public class IciqlLogger { - /** - * Enumeration of the different statement types that are logged. - */ - public enum StatementType { - STAT, TOTAL, CREATE, INSERT, UPDATE, MERGE, DELETE, SELECT, DROP, WARN; - } - - /** - * Interface that defines an iciql listener. - */ - public interface IciqlListener { - void logIciql(StatementType type, String statement); - } - - private static final ExecutorService EXEC = Executors.newSingleThreadExecutor(); - private static final Set LISTENERS = Utils.newHashSet(); - private static final IciqlListener CONSOLE = new IciqlListener() { - - @Override - public void logIciql(StatementType type, String message) { - System.out.println(message); - } - }; - - private static final AtomicLong SELECT_COUNT = new AtomicLong(); - private static final AtomicLong CREATE_COUNT = new AtomicLong(); - private static final AtomicLong INSERT_COUNT = new AtomicLong(); - private static final AtomicLong UPDATE_COUNT = new AtomicLong(); - private static final AtomicLong MERGE_COUNT = new AtomicLong(); - private static final AtomicLong DELETE_COUNT = new AtomicLong(); - private static final AtomicLong DROP_COUNT = new AtomicLong(); - private static final AtomicLong WARN_COUNT = new AtomicLong(); - - /** - * Activates the Console Logger. - */ - public static void activateConsoleLogger() { - registerListener(CONSOLE); - } - - /** - * Deactivates the Console Logger. - */ - public static void deactivateConsoleLogger() { - unregisterListener(CONSOLE); - } - - /** - * Registers a listener with the relay. - * - * @param listener - */ - public static void registerListener(IciqlListener listener) { - LISTENERS.add(listener); - } - - /** - * Unregisters a listener with the relay. - * - * @param listener - */ - public static void unregisterListener(IciqlListener listener) { - if (!LISTENERS.remove(listener)) { - throw new IciqlException("Failed to remove iciql listener {0}", listener); - } - } - - public static void create(String statement) { - CREATE_COUNT.incrementAndGet(); - logStatement(StatementType.CREATE, statement); - } - - public static void insert(String statement) { - INSERT_COUNT.incrementAndGet(); - logStatement(StatementType.INSERT, statement); - } - - public static void update(String statement) { - UPDATE_COUNT.incrementAndGet(); - logStatement(StatementType.UPDATE, statement); - } - - public static void merge(String statement) { - MERGE_COUNT.incrementAndGet(); - logStatement(StatementType.MERGE, statement); - } - - public static void delete(String statement) { - DELETE_COUNT.incrementAndGet(); - logStatement(StatementType.DELETE, statement); - } - - public static void select(String statement) { - SELECT_COUNT.incrementAndGet(); - logStatement(StatementType.SELECT, statement); - } - - public static void drop(String statement) { - DROP_COUNT.incrementAndGet(); - logStatement(StatementType.DROP, statement); - } - - public static void warn(String message, Object... args) { - WARN_COUNT.incrementAndGet(); - logStatement(StatementType.WARN, args.length > 0 ? MessageFormat.format(message, args) : message); - } - - private static void logStatement(final StatementType type, final String statement) { - for (final IciqlListener listener : LISTENERS) { - EXEC.execute(new Runnable() { - public void run() { - listener.logIciql(type, statement); - } - }); - } - } - - public static long getCreateCount() { - return CREATE_COUNT.longValue(); - } - - public static long getInsertCount() { - return INSERT_COUNT.longValue(); - } - - public static long getUpdateCount() { - return UPDATE_COUNT.longValue(); - } - - public static long getMergeCount() { - return MERGE_COUNT.longValue(); - } - - public static long getDeleteCount() { - return DELETE_COUNT.longValue(); - } - - public static long getSelectCount() { - return SELECT_COUNT.longValue(); - } - - public static long getDropCount() { - return DROP_COUNT.longValue(); - } - - public static long getWarnCount() { - return WARN_COUNT.longValue(); - } - - public static long getTotalCount() { - return getCreateCount() + getInsertCount() + getUpdateCount() + getDeleteCount() + getMergeCount() - + getSelectCount() + getDropCount(); - } - - public static void logStats() { - logStatement(StatementType.STAT, "iciql Runtime Statistics"); - logStatement(StatementType.STAT, "========================"); - logStat(StatementType.WARN, getWarnCount()); - logStatement(StatementType.STAT, "========================"); - logStat(StatementType.CREATE, getCreateCount()); - logStat(StatementType.INSERT, getInsertCount()); - logStat(StatementType.UPDATE, getUpdateCount()); - logStat(StatementType.MERGE, getMergeCount()); - logStat(StatementType.DELETE, getDeleteCount()); - logStat(StatementType.SELECT, getSelectCount()); - logStat(StatementType.DROP, getDropCount()); - logStatement(StatementType.STAT, "========================"); - logStat(StatementType.TOTAL, getTotalCount()); - } - - private static void logStat(StatementType type, long value) { - if (value > 0) { - DecimalFormat df = new DecimalFormat("###,###,###,###"); - logStatement(StatementType.STAT, - StringUtils.pad(type.name(), 6, " ", true) + " = " + df.format(value)); - } - } + /** + * Enumeration of the different statement types that are logged. + */ + public enum StatementType { + STAT, TOTAL, CREATE, INSERT, UPDATE, MERGE, DELETE, SELECT, DROP, WARN; + } + + /** + * Interface that defines an iciql listener. + */ + public interface IciqlListener { + void logIciql(StatementType type, String statement); + } + + private static final ExecutorService EXEC = Executors.newSingleThreadExecutor(); + private static final Set LISTENERS = Utils.newHashSet(); + private static final IciqlListener CONSOLE = new IciqlListener() { + + @Override + public void logIciql(StatementType type, String message) { + System.out.println(message); + } + }; + + private static final AtomicLong SELECT_COUNT = new AtomicLong(); + private static final AtomicLong CREATE_COUNT = new AtomicLong(); + private static final AtomicLong INSERT_COUNT = new AtomicLong(); + private static final AtomicLong UPDATE_COUNT = new AtomicLong(); + private static final AtomicLong MERGE_COUNT = new AtomicLong(); + private static final AtomicLong DELETE_COUNT = new AtomicLong(); + private static final AtomicLong DROP_COUNT = new AtomicLong(); + private static final AtomicLong WARN_COUNT = new AtomicLong(); + + /** + * Activates the Console Logger. + */ + public static void activateConsoleLogger() { + registerListener(CONSOLE); + } + + /** + * Deactivates the Console Logger. + */ + public static void deactivateConsoleLogger() { + unregisterListener(CONSOLE); + } + + /** + * Registers a listener with the relay. + * + * @param listener + */ + public static void registerListener(IciqlListener listener) { + LISTENERS.add(listener); + } + + /** + * Unregisters a listener with the relay. + * + * @param listener + */ + public static void unregisterListener(IciqlListener listener) { + if (!LISTENERS.remove(listener)) { + throw new IciqlException("Failed to remove iciql listener {0}", listener); + } + } + + public static void create(String statement) { + CREATE_COUNT.incrementAndGet(); + logStatement(StatementType.CREATE, statement); + } + + public static void insert(String statement) { + INSERT_COUNT.incrementAndGet(); + logStatement(StatementType.INSERT, statement); + } + + public static void update(String statement) { + UPDATE_COUNT.incrementAndGet(); + logStatement(StatementType.UPDATE, statement); + } + + public static void merge(String statement) { + MERGE_COUNT.incrementAndGet(); + logStatement(StatementType.MERGE, statement); + } + + public static void delete(String statement) { + DELETE_COUNT.incrementAndGet(); + logStatement(StatementType.DELETE, statement); + } + + public static void select(String statement) { + SELECT_COUNT.incrementAndGet(); + logStatement(StatementType.SELECT, statement); + } + + public static void drop(String statement) { + DROP_COUNT.incrementAndGet(); + logStatement(StatementType.DROP, statement); + } + + public static void warn(String message, Object... args) { + WARN_COUNT.incrementAndGet(); + logStatement(StatementType.WARN, args.length > 0 ? MessageFormat.format(message, args) : message); + } + + private static void logStatement(final StatementType type, final String statement) { + for (final IciqlListener listener : LISTENERS) { + EXEC.execute(new Runnable() { + public void run() { + listener.logIciql(type, statement); + } + }); + } + } + + public static long getCreateCount() { + return CREATE_COUNT.longValue(); + } + + public static long getInsertCount() { + return INSERT_COUNT.longValue(); + } + + public static long getUpdateCount() { + return UPDATE_COUNT.longValue(); + } + + public static long getMergeCount() { + return MERGE_COUNT.longValue(); + } + + public static long getDeleteCount() { + return DELETE_COUNT.longValue(); + } + + public static long getSelectCount() { + return SELECT_COUNT.longValue(); + } + + public static long getDropCount() { + return DROP_COUNT.longValue(); + } + + public static long getWarnCount() { + return WARN_COUNT.longValue(); + } + + public static long getTotalCount() { + return getCreateCount() + getInsertCount() + getUpdateCount() + getDeleteCount() + getMergeCount() + + getSelectCount() + getDropCount(); + } + + public static void logStats() { + logStatement(StatementType.STAT, "iciql Runtime Statistics"); + logStatement(StatementType.STAT, "========================"); + logStat(StatementType.WARN, getWarnCount()); + logStatement(StatementType.STAT, "========================"); + logStat(StatementType.CREATE, getCreateCount()); + logStat(StatementType.INSERT, getInsertCount()); + logStat(StatementType.UPDATE, getUpdateCount()); + logStat(StatementType.MERGE, getMergeCount()); + logStat(StatementType.DELETE, getDeleteCount()); + logStat(StatementType.SELECT, getSelectCount()); + logStat(StatementType.DROP, getDropCount()); + logStatement(StatementType.STAT, "========================"); + logStat(StatementType.TOTAL, getTotalCount()); + } + + private static void logStat(StatementType type, long value) { + if (value > 0) { + DecimalFormat df = new DecimalFormat("###,###,###,###"); + logStatement(StatementType.STAT, + StringUtils.pad(type.name(), 6, " ", true) + " = " + df.format(value)); + } + } } \ No newline at end of file diff --git a/src/main/java/com/iciql/util/JdbcUtils.java b/src/main/java/com/iciql/util/JdbcUtils.java index 4a4a2b6..0f15eb6 100644 --- a/src/main/java/com/iciql/util/JdbcUtils.java +++ b/src/main/java/com/iciql/util/JdbcUtils.java @@ -17,6 +17,9 @@ package com.iciql.util; +import javax.naming.Context; +import javax.sql.DataSource; +import javax.sql.XAConnection; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; @@ -24,231 +27,212 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; -import javax.naming.Context; -import javax.sql.DataSource; -import javax.sql.XAConnection; - /** * This is a utility class with JDBC helper functions. */ public class JdbcUtils { - private static final String[] DRIVERS = { "h2:", "org.h2.Driver", "Cache:", - "com.intersys.jdbc.CacheDriver", "daffodilDB://", "in.co.daffodil.db.rmi.RmiDaffodilDBDriver", - "daffodil", "in.co.daffodil.db.jdbc.DaffodilDBDriver", "db2:", "COM.ibm.db2.jdbc.net.DB2Driver", - "derby:net:", "org.apache.derby.jdbc.ClientDriver", "derby://", - "org.apache.derby.jdbc.ClientDriver", "derby:", "org.apache.derby.jdbc.EmbeddedDriver", - "FrontBase:", "com.frontbase.jdbc.FBJDriver", "firebirdsql:", "org.firebirdsql.jdbc.FBDriver", - "hsqldb:", "org.hsqldb.jdbcDriver", "informix-sqli:", "com.informix.jdbc.IfxDriver", "jtds:", - "net.sourceforge.jtds.jdbc.Driver", "microsoft:", "com.microsoft.jdbc.sqlserver.SQLServerDriver", - "mimer:", "com.mimer.jdbc.Driver", "mysql:", "com.mysql.jdbc.Driver", "odbc:", - "sun.jdbc.odbc.JdbcOdbcDriver", "oracle:", "oracle.jdbc.driver.OracleDriver", "pervasive:", - "com.pervasive.jdbc.v2.Driver", "pointbase:micro:", "com.pointbase.me.jdbc.jdbcDriver", - "pointbase:", "com.pointbase.jdbc.jdbcUniversalDriver", "postgresql:", "org.postgresql.Driver", - "sybase:", "com.sybase.jdbc3.jdbc.SybDriver", "sqlserver:", - "com.microsoft.sqlserver.jdbc.SQLServerDriver", "teradata:", "com.ncr.teradata.TeraDriver", }; - - private JdbcUtils() { - // utility class - } - - /** - * Close a statement without throwing an exception. - * - * @param stat - * the statement or null - */ - public static void closeSilently(Statement stat) { - if (stat != null) { - try { - stat.close(); - } catch (SQLException e) { - // ignore - } - } - } - - /** - * Close a connection without throwing an exception. - * - * @param conn - * the connection or null - */ - public static void closeSilently(Connection conn) { - if (conn != null) { - try { - conn.close(); - } catch (SQLException e) { - // ignore - } - } - } - - /** - * Close a result set without throwing an exception. - * - * @param rs - * the result set or null - */ - public static void closeSilently(ResultSet rs) { - closeSilently(rs, false); - } - - /** - * Close a result set, and optionally its statement without throwing an - * exception. - * - * @param rs - * the result set or null - */ - public static void closeSilently(ResultSet rs, boolean closeStatement) { - if (rs != null) { - Statement stat = null; - if (closeStatement) { - try { - stat = rs.getStatement(); - } catch (SQLException e) { - // ignore - } - } - try { - rs.close(); - } catch (SQLException e) { - // ignore - } - closeSilently(stat); - } - } - - /** - * Close an XA connection set without throwing an exception. - * - * @param conn - * the XA connection or null - */ - public static void closeSilently(XAConnection conn) { - if (conn != null) { - try { - conn.close(); - } catch (SQLException e) { - // ignore - } - } - } - - /** - * Open a new database connection with the given settings. - * - * @param driver - * the driver class name - * @param url - * the database URL - * @param user - * the user name - * @param password - * the password - * @return the database connection - */ - public static Connection getConnection(String driver, String url, String user, String password) - throws SQLException { - Properties prop = new Properties(); - if (user != null) { - prop.setProperty("user", user); - } - if (password != null) { - prop.setProperty("password", password); - } - return getConnection(driver, url, prop); - } - - /** - * Escape table or schema patterns used for DatabaseMetaData functions. - * - * @param pattern - * the pattern - * @return the escaped pattern - */ - public static String escapeMetaDataPattern(String pattern) { - if (pattern == null || pattern.length() == 0) { - return pattern; - } - return StringUtils.replaceAll(pattern, "\\", "\\\\"); - } - - /** - * Open a new database connection with the given settings. - * - * @param driver - * the driver class name - * @param url - * the database URL - * @param prop - * the properties containing at least the user name and password - * @return the database connection - */ - public static Connection getConnection(String driver, String url, Properties prop) throws SQLException { - if (StringUtils.isNullOrEmpty(driver)) { - JdbcUtils.load(url); - } else { - Class d = Utils.loadClass(driver); - if (java.sql.Driver.class.isAssignableFrom(d)) { - return DriverManager.getConnection(url, prop); - } else if (javax.naming.Context.class.isAssignableFrom(d)) { - // JNDI context - try { - Context context = (Context) d.newInstance(); - DataSource ds = (DataSource) context.lookup(url); - String user = prop.getProperty("user"); - String password = prop.getProperty("password"); - if (StringUtils.isNullOrEmpty(user) && StringUtils.isNullOrEmpty(password)) { - return ds.getConnection(); - } - return ds.getConnection(user, password); - } catch (SQLException e) { - throw e; - } catch (Exception e) { - throw new SQLException("Failed to get connection for " + url, e); - } - } else { - // Don't know, but maybe it loaded a JDBC Driver - return DriverManager.getConnection(url, prop); - } - } - return DriverManager.getConnection(url, prop); - } - - /** - * Get the driver class name for the given URL, or null if the URL is - * unknown. - * - * @param url - * the database URL - * @return the driver class name - */ - public static String getDriver(String url) { - if (url.startsWith("jdbc:")) { - url = url.substring("jdbc:".length()); - for (int i = 0; i < DRIVERS.length; i += 2) { - String prefix = DRIVERS[i]; - if (url.startsWith(prefix)) { - return DRIVERS[i + 1]; - } - } - } - return null; - } - - /** - * Load the driver class for the given URL, if the database URL is known. - * - * @param url - * the database URL - */ - public static void load(String url) { - String driver = getDriver(url); - if (driver != null) { - Utils.loadClass(driver); - } - } + private static final String[] DRIVERS = {"h2:", "org.h2.Driver", "Cache:", + "com.intersys.jdbc.CacheDriver", "daffodilDB://", "in.co.daffodil.db.rmi.RmiDaffodilDBDriver", + "daffodil", "in.co.daffodil.db.jdbc.DaffodilDBDriver", "db2:", "COM.ibm.db2.jdbc.net.DB2Driver", + "derby:net:", "org.apache.derby.jdbc.ClientDriver", "derby://", + "org.apache.derby.jdbc.ClientDriver", "derby:", "org.apache.derby.jdbc.EmbeddedDriver", + "FrontBase:", "com.frontbase.jdbc.FBJDriver", "firebirdsql:", "org.firebirdsql.jdbc.FBDriver", + "hsqldb:", "org.hsqldb.jdbcDriver", "informix-sqli:", "com.informix.jdbc.IfxDriver", "jtds:", + "net.sourceforge.jtds.jdbc.Driver", "microsoft:", "com.microsoft.jdbc.sqlserver.SQLServerDriver", + "mimer:", "com.mimer.jdbc.Driver", "mysql:", "com.mysql.jdbc.Driver", "odbc:", + "sun.jdbc.odbc.JdbcOdbcDriver", "oracle:", "oracle.jdbc.driver.OracleDriver", "pervasive:", + "com.pervasive.jdbc.v2.Driver", "pointbase:micro:", "com.pointbase.me.jdbc.jdbcDriver", + "pointbase:", "com.pointbase.jdbc.jdbcUniversalDriver", "postgresql:", "org.postgresql.Driver", + "sybase:", "com.sybase.jdbc3.jdbc.SybDriver", "sqlserver:", + "com.microsoft.sqlserver.jdbc.SQLServerDriver", "teradata:", "com.ncr.teradata.TeraDriver",}; + + private JdbcUtils() { + // utility class + } + + /** + * Close a statement without throwing an exception. + * + * @param stat the statement or null + */ + public static void closeSilently(Statement stat) { + if (stat != null) { + try { + stat.close(); + } catch (SQLException e) { + // ignore + } + } + } + + /** + * Close a connection without throwing an exception. + * + * @param conn the connection or null + */ + public static void closeSilently(Connection conn) { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + // ignore + } + } + } + + /** + * Close a result set without throwing an exception. + * + * @param rs the result set or null + */ + public static void closeSilently(ResultSet rs) { + closeSilently(rs, false); + } + + /** + * Close a result set, and optionally its statement without throwing an + * exception. + * + * @param rs the result set or null + */ + public static void closeSilently(ResultSet rs, boolean closeStatement) { + if (rs != null) { + Statement stat = null; + if (closeStatement) { + try { + stat = rs.getStatement(); + } catch (SQLException e) { + // ignore + } + } + try { + rs.close(); + } catch (SQLException e) { + // ignore + } + closeSilently(stat); + } + } + + /** + * Close an XA connection set without throwing an exception. + * + * @param conn the XA connection or null + */ + public static void closeSilently(XAConnection conn) { + if (conn != null) { + try { + conn.close(); + } catch (SQLException e) { + // ignore + } + } + } + + /** + * Open a new database connection with the given settings. + * + * @param driver the driver class name + * @param url the database URL + * @param user the user name + * @param password the password + * @return the database connection + */ + public static Connection getConnection(String driver, String url, String user, String password) + throws SQLException { + Properties prop = new Properties(); + if (user != null) { + prop.setProperty("user", user); + } + if (password != null) { + prop.setProperty("password", password); + } + return getConnection(driver, url, prop); + } + + /** + * Escape table or schema patterns used for DatabaseMetaData functions. + * + * @param pattern the pattern + * @return the escaped pattern + */ + public static String escapeMetaDataPattern(String pattern) { + if (pattern == null || pattern.length() == 0) { + return pattern; + } + return StringUtils.replaceAll(pattern, "\\", "\\\\"); + } + + /** + * Open a new database connection with the given settings. + * + * @param driver the driver class name + * @param url the database URL + * @param prop the properties containing at least the user name and password + * @return the database connection + */ + public static Connection getConnection(String driver, String url, Properties prop) throws SQLException { + if (StringUtils.isNullOrEmpty(driver)) { + JdbcUtils.load(url); + } else { + Class d = Utils.loadClass(driver); + if (java.sql.Driver.class.isAssignableFrom(d)) { + return DriverManager.getConnection(url, prop); + } else if (javax.naming.Context.class.isAssignableFrom(d)) { + // JNDI context + try { + Context context = (Context) d.newInstance(); + DataSource ds = (DataSource) context.lookup(url); + String user = prop.getProperty("user"); + String password = prop.getProperty("password"); + if (StringUtils.isNullOrEmpty(user) && StringUtils.isNullOrEmpty(password)) { + return ds.getConnection(); + } + return ds.getConnection(user, password); + } catch (SQLException e) { + throw e; + } catch (Exception e) { + throw new SQLException("Failed to get connection for " + url, e); + } + } else { + // Don't know, but maybe it loaded a JDBC Driver + return DriverManager.getConnection(url, prop); + } + } + return DriverManager.getConnection(url, prop); + } + + /** + * Get the driver class name for the given URL, or null if the URL is + * unknown. + * + * @param url the database URL + * @return the driver class name + */ + public static String getDriver(String url) { + if (url.startsWith("jdbc:")) { + url = url.substring("jdbc:".length()); + for (int i = 0; i < DRIVERS.length; i += 2) { + String prefix = DRIVERS[i]; + if (url.startsWith(prefix)) { + return DRIVERS[i + 1]; + } + } + } + return null; + } + + /** + * Load the driver class for the given URL, if the database URL is known. + * + * @param url the database URL + */ + public static void load(String url) { + String driver = getDriver(url); + if (driver != null) { + Utils.loadClass(driver); + } + } } diff --git a/src/main/java/com/iciql/util/Slf4jIciqlListener.java b/src/main/java/com/iciql/util/Slf4jIciqlListener.java index ded393f..383d80f 100644 --- a/src/main/java/com/iciql/util/Slf4jIciqlListener.java +++ b/src/main/java/com/iciql/util/Slf4jIciqlListener.java @@ -16,77 +16,76 @@ package com.iciql.util; -import java.util.HashMap; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import com.iciql.Iciql; import com.iciql.util.IciqlLogger.IciqlListener; import com.iciql.util.IciqlLogger.StatementType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.Map; /** * Slf4jIciqlListener interfaces the IciqlLogger to the SLF4J logging framework. */ public class Slf4jIciqlListener implements IciqlListener { - private Logger logger = LoggerFactory.getLogger(Iciql.class); + private Logger logger = LoggerFactory.getLogger(Iciql.class); - /** - * Enumeration representing the SLF4J log levels. - */ - public enum Level { - ERROR, WARN, INFO, DEBUG, TRACE, OFF; - } + /** + * Enumeration representing the SLF4J log levels. + */ + public enum Level { + ERROR, WARN, INFO, DEBUG, TRACE, OFF; + } - private final Level defaultLevel; + private final Level defaultLevel; - private final Map levels; + private final Map levels; - public Slf4jIciqlListener() { - this(Level.TRACE); - } + public Slf4jIciqlListener() { + this(Level.TRACE); + } - public Slf4jIciqlListener(Level defaultLevel) { - this.defaultLevel = defaultLevel; - levels = new HashMap(); - for (StatementType type : StatementType.values()) { - levels.put(type, defaultLevel); - } - } + public Slf4jIciqlListener(Level defaultLevel) { + this.defaultLevel = defaultLevel; + levels = new HashMap(); + for (StatementType type : StatementType.values()) { + levels.put(type, defaultLevel); + } + } - /** - * Sets the logging level for a particular statement type. - * - * @param type - * @param level - */ - public void setLevel(StatementType type, Level level) { - levels.put(type, defaultLevel); - } + /** + * Sets the logging level for a particular statement type. + * + * @param type + * @param level + */ + public void setLevel(StatementType type, Level level) { + levels.put(type, defaultLevel); + } - @Override - public void logIciql(StatementType type, String statement) { - Level level = levels.get(type); - switch (level) { - case ERROR: - logger.error(statement); - break; - case WARN: - logger.warn(statement); - break; - case INFO: - logger.info(statement); - break; - case DEBUG: - logger.debug(statement); - break; - case TRACE: - logger.trace(statement); - break; - case OFF: - break; - } - } + @Override + public void logIciql(StatementType type, String statement) { + Level level = levels.get(type); + switch (level) { + case ERROR: + logger.error(statement); + break; + case WARN: + logger.warn(statement); + break; + case INFO: + logger.info(statement); + break; + case DEBUG: + logger.debug(statement); + break; + case TRACE: + logger.trace(statement); + break; + case OFF: + break; + } + } } diff --git a/src/main/java/com/iciql/util/StatementBuilder.java b/src/main/java/com/iciql/util/StatementBuilder.java index 47e8054..469d1d9 100644 --- a/src/main/java/com/iciql/util/StatementBuilder.java +++ b/src/main/java/com/iciql/util/StatementBuilder.java @@ -21,19 +21,19 @@ package com.iciql.util; * A utility class to build a statement. In addition to the methods supported by * StringBuilder, it allows to add a text only in the second iteration. This * simplified constructs such as: - * + *

*

  * StringBuilder buff = new StringBuilder();
  * for (int i = 0; i < args.length; i++) {
  * 	if (i > 0) {
  * 		buff.append(", ");
- * 	}
+ *    }
  * 	buff.append(args[i]);
  * }
  * 
- * + *

* to - * + *

*

  * StatementBuilder buff = new StatementBuilder();
  * for (String s : args) {
@@ -44,123 +44,117 @@ package com.iciql.util;
  */
 public class StatementBuilder {
 
-	private final StringBuilder builder = new StringBuilder();
-	private int index;
-
-	/**
-	 * Create a new builder.
-	 */
-	public StatementBuilder() {
-		// nothing to do
-	}
-
-	/**
-	 * Create a new builder.
-	 * 
-	 * @param string
-	 *            the initial string
-	 */
-	public StatementBuilder(String string) {
-		builder.append(string);
-	}
-
-	/**
-	 * Append a text.
-	 * 
-	 * @param s
-	 *            the text to append
-	 * @return itself
-	 */
-	public StatementBuilder append(String s) {
-		builder.append(s);
-		return this;
-	}
-
-	/**
-	 * Append a character.
-	 * 
-	 * @param c
-	 *            the character to append
-	 * @return itself
-	 */
-	public StatementBuilder append(char c) {
-		builder.append(c);
-		return this;
-	}
-
-	/**
-	 * Append a number.
-	 * 
-	 * @param x
-	 *            the number to append
-	 * @return itself
-	 */
-	public StatementBuilder append(long x) {
-		builder.append(x);
-		return this;
-	}
-
-	/**
-	 * Returns the current value of the loop counter.
-	 * 
-	 * @return the loop counter
-	 */
-	public int getCount() {
-		return index;
-	}
-
-	/**
-	 * Reset the loop counter.
-	 * 
-	 * @return itself
-	 */
-	public StatementBuilder resetCount() {
-		index = 0;
-		return this;
-	}
-
-	/**
-	 * Append a text, but only if appendExceptFirst was never called.
-	 * 
-	 * @param s
-	 *            the text to append
-	 */
-	public void appendOnlyFirst(String s) {
-		if (index == 0) {
-			builder.append(s);
-		}
-	}
-
-	/**
-	 * Append a text, except when this method is called the first time.
-	 * 
-	 * @param s
-	 *            the text to append
-	 */
-	public void appendExceptFirst(String s) {
-		if (index++ > 0) {
-			builder.append(s);
-		}
-	}
-
-	public void append(StatementBuilder sb) {
-		builder.append(sb);
-	}
-
-	public void insert(int offset, char c) {
-		builder.insert(offset, c);
-	}
-
-	public String toString() {
-		return builder.toString();
-	}
-
-	/**
-	 * Get the length.
-	 * 
-	 * @return the length
-	 */
-	public int length() {
-		return builder.length();
-	}
+    private final StringBuilder builder = new StringBuilder();
+    private int index;
+
+    /**
+     * Create a new builder.
+     */
+    public StatementBuilder() {
+        // nothing to do
+    }
+
+    /**
+     * Create a new builder.
+     *
+     * @param string the initial string
+     */
+    public StatementBuilder(String string) {
+        builder.append(string);
+    }
+
+    /**
+     * Append a text.
+     *
+     * @param s the text to append
+     * @return itself
+     */
+    public StatementBuilder append(String s) {
+        builder.append(s);
+        return this;
+    }
+
+    /**
+     * Append a character.
+     *
+     * @param c the character to append
+     * @return itself
+     */
+    public StatementBuilder append(char c) {
+        builder.append(c);
+        return this;
+    }
+
+    /**
+     * Append a number.
+     *
+     * @param x the number to append
+     * @return itself
+     */
+    public StatementBuilder append(long x) {
+        builder.append(x);
+        return this;
+    }
+
+    /**
+     * Returns the current value of the loop counter.
+     *
+     * @return the loop counter
+     */
+    public int getCount() {
+        return index;
+    }
+
+    /**
+     * Reset the loop counter.
+     *
+     * @return itself
+     */
+    public StatementBuilder resetCount() {
+        index = 0;
+        return this;
+    }
+
+    /**
+     * Append a text, but only if appendExceptFirst was never called.
+     *
+     * @param s the text to append
+     */
+    public void appendOnlyFirst(String s) {
+        if (index == 0) {
+            builder.append(s);
+        }
+    }
+
+    /**
+     * Append a text, except when this method is called the first time.
+     *
+     * @param s the text to append
+     */
+    public void appendExceptFirst(String s) {
+        if (index++ > 0) {
+            builder.append(s);
+        }
+    }
+
+    public void append(StatementBuilder sb) {
+        builder.append(sb);
+    }
+
+    public void insert(int offset, char c) {
+        builder.insert(offset, c);
+    }
+
+    public String toString() {
+        return builder.toString();
+    }
+
+    /**
+     * Get the length.
+     *
+     * @return the length
+     */
+    public int length() {
+        return builder.length();
+    }
 }
diff --git a/src/main/java/com/iciql/util/StringUtils.java b/src/main/java/com/iciql/util/StringUtils.java
index dd3f180..3cf8f83 100644
--- a/src/main/java/com/iciql/util/StringUtils.java
+++ b/src/main/java/com/iciql/util/StringUtils.java
@@ -29,354 +29,338 @@ import java.util.ArrayList;
 
 /**
  * Common string utilities.
- * 
  */
 public class StringUtils {
 
-	/**
-	 * Replace all occurrences of the before string with the after string.
-	 * 
-	 * @param s
-	 *            the string
-	 * @param before
-	 *            the old text
-	 * @param after
-	 *            the new text
-	 * @return the string with the before string replaced
-	 */
-	public static String replaceAll(String s, String before, String after) {
-		int next = s.indexOf(before);
-		if (next < 0) {
-			return s;
-		}
-		StringBuilder buff = new StringBuilder(s.length() - before.length() + after.length());
-		int index = 0;
-		while (true) {
-			buff.append(s.substring(index, next)).append(after);
-			index = next + before.length();
-			next = s.indexOf(before, index);
-			if (next < 0) {
-				buff.append(s.substring(index));
-				break;
-			}
-		}
-		return buff.toString();
-	}
+    /**
+     * Replace all occurrences of the before string with the after string.
+     *
+     * @param s      the string
+     * @param before the old text
+     * @param after  the new text
+     * @return the string with the before string replaced
+     */
+    public static String replaceAll(String s, String before, String after) {
+        int next = s.indexOf(before);
+        if (next < 0) {
+            return s;
+        }
+        StringBuilder buff = new StringBuilder(s.length() - before.length() + after.length());
+        int index = 0;
+        while (true) {
+            buff.append(s.substring(index, next)).append(after);
+            index = next + before.length();
+            next = s.indexOf(before, index);
+            if (next < 0) {
+                buff.append(s.substring(index));
+                break;
+            }
+        }
+        return buff.toString();
+    }
 
-	/**
-	 * Check if a String is null or empty (the length is null).
-	 * 
-	 * @param s
-	 *            the string to check
-	 * @return true if it is null or empty
-	 */
-	public static boolean isNullOrEmpty(String s) {
-		return s == null || s.length() == 0;
-	}
+    /**
+     * Check if a String is null or empty (the length is null).
+     *
+     * @param s the string to check
+     * @return true if it is null or empty
+     */
+    public static boolean isNullOrEmpty(String s) {
+        return s == null || s.length() == 0;
+    }
 
-	/**
-	 * Convert a string to a Java literal using the correct escape sequences.
-	 * The literal is not enclosed in double quotes. The result can be used in
-	 * properties files or in Java source code.
-	 * 
-	 * @param s
-	 *            the text to convert
-	 * @return the Java representation
-	 */
-	public static String javaEncode(String s) {
-		int length = s.length();
-		StringBuilder buff = new StringBuilder(length);
-		for (int i = 0; i < length; i++) {
-			char c = s.charAt(i);
-			switch (c) {
-			// case '\b':
-			// // BS backspace
-			// // not supported in properties files
-			// buff.append("\\b");
-			// break;
-			case '\t':
-				// HT horizontal tab
-				buff.append("\\t");
-				break;
-			case '\n':
-				// LF linefeed
-				buff.append("\\n");
-				break;
-			case '\f':
-				// FF form feed
-				buff.append("\\f");
-				break;
-			case '\r':
-				// CR carriage return
-				buff.append("\\r");
-				break;
-			case '"':
-				// double quote
-				buff.append("\\\"");
-				break;
-			case '\\':
-				// backslash
-				buff.append("\\\\");
-				break;
-			default:
-				int ch = c & 0xffff;
-				if (ch >= ' ' && (ch < 0x80)) {
-					buff.append(c);
-					// not supported in properties files
-					// } else if(ch < 0xff) {
-					// buff.append("\\");
-					// // make sure it's three characters (0x200 is octal 1000)
-					// buff.append(Integer.toOctalString(0x200 |
-					// ch).substring(1));
-				} else {
-					buff.append("\\u");
-					// make sure it's four characters
-					buff.append(Integer.toHexString(0x10000 | ch).substring(1));
-				}
-			}
-		}
-		return buff.toString();
-	}
+    /**
+     * Convert a string to a Java literal using the correct escape sequences.
+     * The literal is not enclosed in double quotes. The result can be used in
+     * properties files or in Java source code.
+     *
+     * @param s the text to convert
+     * @return the Java representation
+     */
+    public static String javaEncode(String s) {
+        int length = s.length();
+        StringBuilder buff = new StringBuilder(length);
+        for (int i = 0; i < length; i++) {
+            char c = s.charAt(i);
+            switch (c) {
+                // case '\b':
+                // // BS backspace
+                // // not supported in properties files
+                // buff.append("\\b");
+                // break;
+                case '\t':
+                    // HT horizontal tab
+                    buff.append("\\t");
+                    break;
+                case '\n':
+                    // LF linefeed
+                    buff.append("\\n");
+                    break;
+                case '\f':
+                    // FF form feed
+                    buff.append("\\f");
+                    break;
+                case '\r':
+                    // CR carriage return
+                    buff.append("\\r");
+                    break;
+                case '"':
+                    // double quote
+                    buff.append("\\\"");
+                    break;
+                case '\\':
+                    // backslash
+                    buff.append("\\\\");
+                    break;
+                default:
+                    int ch = c & 0xffff;
+                    if (ch >= ' ' && (ch < 0x80)) {
+                        buff.append(c);
+                        // not supported in properties files
+                        // } else if(ch < 0xff) {
+                        // buff.append("\\");
+                        // // make sure it's three characters (0x200 is octal 1000)
+                        // buff.append(Integer.toOctalString(0x200 |
+                        // ch).substring(1));
+                    } else {
+                        buff.append("\\u");
+                        // make sure it's four characters
+                        buff.append(Integer.toHexString(0x10000 | ch).substring(1));
+                    }
+            }
+        }
+        return buff.toString();
+    }
 
-	/**
-	 * Pad a string. This method is used for the SQL function RPAD and LPAD.
-	 * 
-	 * @param string
-	 *            the original string
-	 * @param n
-	 *            the target length
-	 * @param padding
-	 *            the padding string
-	 * @param right
-	 *            true if the padding should be appended at the end
-	 * @return the padded string
-	 */
-	public static String pad(String string, int n, String padding, boolean right) {
-		if (n < 0) {
-			n = 0;
-		}
-		if (n < string.length()) {
-			return string.substring(0, n);
-		} else if (n == string.length()) {
-			return string;
-		}
-		char paddingChar;
-		if (padding == null || padding.length() == 0) {
-			paddingChar = ' ';
-		} else {
-			paddingChar = padding.charAt(0);
-		}
-		StringBuilder buff = new StringBuilder(n);
-		n -= string.length();
-		if (right) {
-			buff.append(string);
-		}
-		for (int i = 0; i < n; i++) {
-			buff.append(paddingChar);
-		}
-		if (!right) {
-			buff.append(string);
-		}
-		return buff.toString();
-	}
+    /**
+     * Pad a string. This method is used for the SQL function RPAD and LPAD.
+     *
+     * @param string  the original string
+     * @param n       the target length
+     * @param padding the padding string
+     * @param right   true if the padding should be appended at the end
+     * @return the padded string
+     */
+    public static String pad(String string, int n, String padding, boolean right) {
+        if (n < 0) {
+            n = 0;
+        }
+        if (n < string.length()) {
+            return string.substring(0, n);
+        } else if (n == string.length()) {
+            return string;
+        }
+        char paddingChar;
+        if (padding == null || padding.length() == 0) {
+            paddingChar = ' ';
+        } else {
+            paddingChar = padding.charAt(0);
+        }
+        StringBuilder buff = new StringBuilder(n);
+        n -= string.length();
+        if (right) {
+            buff.append(string);
+        }
+        for (int i = 0; i < n; i++) {
+            buff.append(paddingChar);
+        }
+        if (!right) {
+            buff.append(string);
+        }
+        return buff.toString();
+    }
 
-	/**
-	 * Convert a string to a SQL literal. Null is converted to NULL. The text is
-	 * enclosed in single quotes. If there are any special characters, the
-	 * method STRINGDECODE is used.
-	 * 
-	 * @param s
-	 *            the text to convert.
-	 * @return the SQL literal
-	 */
-	public static String quoteStringSQL(String s) {
-		if (s == null) {
-			return "NULL";
-		}
-		int length = s.length();
-		StringBuilder buff = new StringBuilder(length + 2);
-		buff.append('\'');
-		for (int i = 0; i < length; i++) {
-			char c = s.charAt(i);
-			if (c == '\'') {
-				buff.append(c);
-			} else if (c < ' ' || c > 127) {
-				// need to start from the beginning because maybe there was a \
-				// that was not quoted
-				return "STRINGDECODE(" + quoteStringSQL(javaEncode(s)) + ")";
-			}
-			buff.append(c);
-		}
-		buff.append('\'');
-		return buff.toString();
-	}
+    /**
+     * Convert a string to a SQL literal. Null is converted to NULL. The text is
+     * enclosed in single quotes. If there are any special characters, the
+     * method STRINGDECODE is used.
+     *
+     * @param s the text to convert.
+     * @return the SQL literal
+     */
+    public static String quoteStringSQL(String s) {
+        if (s == null) {
+            return "NULL";
+        }
+        int length = s.length();
+        StringBuilder buff = new StringBuilder(length + 2);
+        buff.append('\'');
+        for (int i = 0; i < length; i++) {
+            char c = s.charAt(i);
+            if (c == '\'') {
+                buff.append(c);
+            } else if (c < ' ' || c > 127) {
+                // need to start from the beginning because maybe there was a \
+                // that was not quoted
+                return "STRINGDECODE(" + quoteStringSQL(javaEncode(s)) + ")";
+            }
+            buff.append(c);
+        }
+        buff.append('\'');
+        return buff.toString();
+    }
 
-	/**
-	 * Split a string into an array of strings using the given separator. A null
-	 * string will result in a null array, and an empty string in a zero element
-	 * array.
-	 * 
-	 * @param s
-	 *            the string to split
-	 * @param separatorChar
-	 *            the separator character
-	 * @param trim
-	 *            whether each element should be trimmed
-	 * @return the array list
-	 */
-	public static String[] arraySplit(String s, char separatorChar, boolean trim) {
-		if (s == null) {
-			return null;
-		}
-		int length = s.length();
-		if (length == 0) {
-			return new String[0];
-		}
-		ArrayList list = Utils.newArrayList();
-		StringBuilder buff = new StringBuilder(length);
-		for (int i = 0; i < length; i++) {
-			char c = s.charAt(i);
-			if (c == separatorChar) {
-				String e = buff.toString();
-				list.add(trim ? e.trim() : e);
-				buff.setLength(0);
-			} else if (c == '\\' && i < length - 1) {
-				buff.append(s.charAt(++i));
-			} else {
-				buff.append(c);
-			}
-		}
-		String e = buff.toString();
-		list.add(trim ? e.trim() : e);
-		String[] array = new String[list.size()];
-		list.toArray(array);
-		return array;
-	}
+    /**
+     * Split a string into an array of strings using the given separator. A null
+     * string will result in a null array, and an empty string in a zero element
+     * array.
+     *
+     * @param s             the string to split
+     * @param separatorChar the separator character
+     * @param trim          whether each element should be trimmed
+     * @return the array list
+     */
+    public static String[] arraySplit(String s, char separatorChar, boolean trim) {
+        if (s == null) {
+            return null;
+        }
+        int length = s.length();
+        if (length == 0) {
+            return new String[0];
+        }
+        ArrayList list = Utils.newArrayList();
+        StringBuilder buff = new StringBuilder(length);
+        for (int i = 0; i < length; i++) {
+            char c = s.charAt(i);
+            if (c == separatorChar) {
+                String e = buff.toString();
+                list.add(trim ? e.trim() : e);
+                buff.setLength(0);
+            } else if (c == '\\' && i < length - 1) {
+                buff.append(s.charAt(++i));
+            } else {
+                buff.append(c);
+            }
+        }
+        String e = buff.toString();
+        list.add(trim ? e.trim() : e);
+        String[] array = new String[list.size()];
+        list.toArray(array);
+        return array;
+    }
 
-	/**
-	 * Calculates the SHA1 of the string.
-	 * 
-	 * @param text
-	 * @return sha1 of the string
-	 */
-	public static String calculateSHA1(String text) {
-		try {
-			byte[] bytes = text.getBytes("iso-8859-1");
-			return calculateSHA1(bytes);
-		} catch (UnsupportedEncodingException u) {
-			throw new RuntimeException(u);
-		}
-	}
+    /**
+     * Calculates the SHA1 of the string.
+     *
+     * @param text
+     * @return sha1 of the string
+     */
+    public static String calculateSHA1(String text) {
+        try {
+            byte[] bytes = text.getBytes("iso-8859-1");
+            return calculateSHA1(bytes);
+        } catch (UnsupportedEncodingException u) {
+            throw new RuntimeException(u);
+        }
+    }
 
-	/**
-	 * Calculates the SHA1 of the byte array.
-	 * 
-	 * @param bytes
-	 * @return sha1 of the byte array
-	 */
-	public static String calculateSHA1(byte[] bytes) {
-		try {
-			MessageDigest md = MessageDigest.getInstance("SHA-1");
-			md.update(bytes, 0, bytes.length);
-			byte[] digest = md.digest();
-			StringBuilder sb = new StringBuilder(digest.length * 2);
-			for (int i = 0; i < digest.length; i++) {
-				if (((int) digest[i] & 0xff) < 0x10) {
-					sb.append('0');
-				}
-				sb.append(Integer.toHexString((int) digest[i] & 0xff));
-			}
-			return sb.toString();
-		} catch (NoSuchAlgorithmException t) {
-			throw new RuntimeException(t);
-		}
-	}
+    /**
+     * Calculates the SHA1 of the byte array.
+     *
+     * @param bytes
+     * @return sha1 of the byte array
+     */
+    public static String calculateSHA1(byte[] bytes) {
+        try {
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            md.update(bytes, 0, bytes.length);
+            byte[] digest = md.digest();
+            StringBuilder sb = new StringBuilder(digest.length * 2);
+            for (int i = 0; i < digest.length; i++) {
+                if (((int) digest[i] & 0xff) < 0x10) {
+                    sb.append('0');
+                }
+                sb.append(Integer.toHexString((int) digest[i] & 0xff));
+            }
+            return sb.toString();
+        } catch (NoSuchAlgorithmException t) {
+            throw new RuntimeException(t);
+        }
+    }
 
-	/**
-	 * Counts the occurrences of char c in the given string.
-	 * 
-	 * @param c
-	 *            the character to count
-	 * @param value
-	 *            the source string
-	 * @return the count of c in value
-	 */
-	public static int count(char c, String value) {
-		int count = 0;
-		for (char cv : value.toCharArray()) {
-			if (cv == c) {
-				count++;
-			}
-		}
-		return count;
-	}
+    /**
+     * Counts the occurrences of char c in the given string.
+     *
+     * @param c     the character to count
+     * @param value the source string
+     * @return the count of c in value
+     */
+    public static int count(char c, String value) {
+        int count = 0;
+        for (char cv : value.toCharArray()) {
+            if (cv == c) {
+                count++;
+            }
+        }
+        return count;
+    }
 
-	/**
-	 * Prepare text for html presentation. Replace sensitive characters with
-	 * html entities.
-	 * 
-	 * @param inStr
-	 * @param changeSpace
-	 * @return plain text escaped for html
-	 */
-	public static String escapeForHtml(String inStr, boolean changeSpace) {
-		StringBuffer retStr = new StringBuffer();
-		int i = 0;
-		while (i < inStr.length()) {
-			if (inStr.charAt(i) == '&') {
-				retStr.append("&");
-			} else if (inStr.charAt(i) == '<') {
-				retStr.append("<");
-			} else if (inStr.charAt(i) == '>') {
-				retStr.append(">");
-			} else if (inStr.charAt(i) == '\"') {
-				retStr.append(""");
-			} else if (changeSpace && inStr.charAt(i) == ' ') {
-				retStr.append(" ");
-			} else if (changeSpace && inStr.charAt(i) == '\t') {
-				retStr.append("    ");
-			} else {
-				retStr.append(inStr.charAt(i));
-			}
-			i++;
-		}
-		return retStr.toString();
-	}
+    /**
+     * Prepare text for html presentation. Replace sensitive characters with
+     * html entities.
+     *
+     * @param inStr
+     * @param changeSpace
+     * @return plain text escaped for html
+     */
+    public static String escapeForHtml(String inStr, boolean changeSpace) {
+        StringBuffer retStr = new StringBuffer();
+        int i = 0;
+        while (i < inStr.length()) {
+            if (inStr.charAt(i) == '&') {
+                retStr.append("&");
+            } else if (inStr.charAt(i) == '<') {
+                retStr.append("<");
+            } else if (inStr.charAt(i) == '>') {
+                retStr.append(">");
+            } else if (inStr.charAt(i) == '\"') {
+                retStr.append(""");
+            } else if (changeSpace && inStr.charAt(i) == ' ') {
+                retStr.append(" ");
+            } else if (changeSpace && inStr.charAt(i) == '\t') {
+                retStr.append("    ");
+            } else {
+                retStr.append(inStr.charAt(i));
+            }
+            i++;
+        }
+        return retStr.toString();
+    }
 
-	/**
-	 * Replaces carriage returns and line feeds with html line breaks.
-	 * 
-	 * @param string
-	 * @return plain text with html line breaks
-	 */
-	public static String breakLinesForHtml(String string) {
-		return string.replace("\r\n", "
").replace("\r", "
").replace("\n", "
"); - } + /** + * Replaces carriage returns and line feeds with html line breaks. + * + * @param string + * @return plain text with html line breaks + */ + public static String breakLinesForHtml(String string) { + return string.replace("\r\n", "
").replace("\r", "
").replace("\n", "
"); + } - /** - * Returns the string content of the specified file. - * - * @param file - * @param lineEnding - * @return the string content of the file - */ - public static String readContent(File file, String lineEnding) { - StringBuilder sb = new StringBuilder(); - try { - InputStreamReader is = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")); - BufferedReader reader = new BufferedReader(is); - String line = null; - while ((line = reader.readLine()) != null) { - sb.append(line); - if (lineEnding != null) { - sb.append(lineEnding); - } - } - reader.close(); - } catch (Throwable t) { - System.err.println("Failed to read content of " + file.getAbsolutePath()); - t.printStackTrace(); - } - return sb.toString(); - } + /** + * Returns the string content of the specified file. + * + * @param file + * @param lineEnding + * @return the string content of the file + */ + public static String readContent(File file, String lineEnding) { + StringBuilder sb = new StringBuilder(); + try { + InputStreamReader is = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8")); + BufferedReader reader = new BufferedReader(is); + String line = null; + while ((line = reader.readLine()) != null) { + sb.append(line); + if (lineEnding != null) { + sb.append(lineEnding); + } + } + reader.close(); + } catch (Throwable t) { + System.err.println("Failed to read content of " + file.getAbsolutePath()); + t.printStackTrace(); + } + return sb.toString(); + } } diff --git a/src/main/java/com/iciql/util/Utils.java b/src/main/java/com/iciql/util/Utils.java index ada7c34..16528d8 100644 --- a/src/main/java/com/iciql/util/Utils.java +++ b/src/main/java/com/iciql/util/Utils.java @@ -17,6 +17,13 @@ package com.iciql.util; +import com.iciql.Iciql.DataTypeAdapter; +import com.iciql.Iciql.EnumId; +import com.iciql.Iciql.EnumType; +import com.iciql.Iciql.IQEnum; +import com.iciql.Iciql.TypeAdapter; +import com.iciql.IciqlException; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -44,581 +51,570 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; -import com.iciql.Iciql.DataTypeAdapter; -import com.iciql.Iciql.EnumId; -import com.iciql.Iciql.EnumType; -import com.iciql.Iciql.IQEnum; -import com.iciql.Iciql.TypeAdapter; -import com.iciql.IciqlException; - /** * Generic utility methods. */ public class Utils { - public static final AtomicLong COUNTER = new AtomicLong(0); - - public static final AtomicInteger AS_COUNTER = new AtomicInteger(0); - - private static final boolean MAKE_ACCESSIBLE = true; - - private static final int BUFFER_BLOCK_SIZE = 4 * 1024; - - public static synchronized int nextAsCount() { - // prevent negative values and use a threadsafe counter - int count = AS_COUNTER.incrementAndGet(); - if (count == Integer.MAX_VALUE) { - count = 0; - AS_COUNTER.set(count); - } - return count; - } - - @SuppressWarnings("unchecked") - public static Class getClass(X x) { - return (Class) x.getClass(); - } - - public static Class loadClass(String className) { - try { - return Class.forName(className); - } catch (Exception e) { - throw new IciqlException(e); - } - } - - public static Iterable newArrayIterable(final T[] a) { - return Arrays.asList(a); - } - - public static ArrayList newArrayList() { - return new ArrayList(); - } - - public static ArrayList newArrayList(Collection c) { - return new ArrayList(c); - } - - public static HashSet newHashSet() { - return new HashSet(); - } - - public static HashSet newHashSet(Collection list) { - return new HashSet(list); - } - - public static HashMap newHashMap() { - return new HashMap(); - } - - public static Map newSynchronizedHashMap() { - HashMap map = newHashMap(); - return Collections.synchronizedMap(map); - } - - public static IdentityHashMap newIdentityHashMap() { - return new IdentityHashMap(); - } - - public static ThreadLocal newThreadLocal(final Class clazz) { - return new ThreadLocal() { - @SuppressWarnings("rawtypes") - @Override - protected T initialValue() { - try { - return clazz.newInstance(); - } catch (Exception e) { - if (MAKE_ACCESSIBLE) { - Constructor[] constructors = clazz.getDeclaredConstructors(); - // try 0 length constructors - for (Constructor c : constructors) { - if (c.getParameterTypes().length == 0) { - c.setAccessible(true); - try { - return clazz.newInstance(); - } catch (Exception e2) { - // ignore - } - } - } - } - throw new IciqlException(e, - "Missing default constructor? Exception trying to instantiate {0}: {1}", clazz.getName(), - e.getMessage()); - } - } - }; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - public static T newObject(Class clazz) { - // must create new instances - if (clazz == int.class || clazz == Integer.class) { - return (T) new Integer((int) (COUNTER.getAndIncrement() % Integer.MAX_VALUE)); - } else if (clazz == String.class) { - return (T) ("" + COUNTER.getAndIncrement()); - } else if (clazz == long.class || clazz == Long.class) { - return (T) new Long(COUNTER.getAndIncrement()); - } else if (clazz == short.class || clazz == Short.class) { - return (T) new Short((short) (COUNTER.getAndIncrement() % Short.MAX_VALUE)); - } else if (clazz == byte.class || clazz == Byte.class) { - return (T) new Byte((byte) (COUNTER.getAndIncrement() % Byte.MAX_VALUE)); - } else if (clazz == float.class || clazz == Float.class) { - return (T) new Float(COUNTER.getAndIncrement()); - } else if (clazz == double.class || clazz == Double.class) { - return (T) new Double(COUNTER.getAndIncrement()); - } else if (clazz == boolean.class || clazz == Boolean.class) { - COUNTER.getAndIncrement(); - return (T) new Boolean(false); - } else if (clazz == BigDecimal.class) { - return (T) new BigDecimal(COUNTER.getAndIncrement()); - } else if (clazz == BigInteger.class) { - return (T) new BigInteger("" + COUNTER.getAndIncrement()); - } else if (clazz == java.sql.Date.class) { - return (T) new java.sql.Date(COUNTER.getAndIncrement()); - } else if (clazz == java.sql.Time.class) { - return (T) new java.sql.Time(COUNTER.getAndIncrement()); - } else if (clazz == java.sql.Timestamp.class) { - return (T) new java.sql.Timestamp(COUNTER.getAndIncrement()); - } else if (clazz == java.util.Date.class) { - return (T) new java.util.Date(COUNTER.getAndIncrement()); - } else if (clazz == byte[].class) { - COUNTER.getAndIncrement(); - return (T) new byte[0]; - } else if (clazz.isEnum()) { - COUNTER.getAndIncrement(); - // enums can not be instantiated reflectively - // return first constant as reference - return clazz.getEnumConstants()[0]; - } else if (clazz == java.util.UUID.class) { - COUNTER.getAndIncrement(); - return (T) UUID.randomUUID(); - } else if (Set.class == clazz) { - COUNTER.getAndIncrement(); - return (T) new HashSet(); - } else if (List.class == clazz) { - COUNTER.getAndIncrement(); - return (T) new ArrayList(); - } else if (Map.class == clazz) { - COUNTER.getAndIncrement(); - return (T) new HashMap(); - } - try { - return clazz.newInstance(); - } catch (Exception e) { - if (MAKE_ACCESSIBLE) { - Constructor[] constructors = clazz.getDeclaredConstructors(); - // try 0 length constructors - for (Constructor c : constructors) { - if (c.getParameterTypes().length == 0) { - c.setAccessible(true); - try { - return clazz.newInstance(); - } catch (Exception e2) { - // ignore - } - } - } - // try 1 length constructors - for (Constructor c : constructors) { - if (c.getParameterTypes().length == 1) { - c.setAccessible(true); - try { - return (T) c.newInstance(new Object[1]); - } catch (Exception e2) { - // ignore - } - } - } - } - throw new IciqlException(e, "Missing default constructor?! Exception trying to instantiate {0}: {1}", - clazz.getName(), e.getMessage()); - } - } - - public static boolean isSimpleType(Class clazz) { - if (Number.class.isAssignableFrom(clazz)) { - return true; - } else if (clazz == String.class) { - return true; - } - return false; - } - - public static Object convert(Object o, Class targetType) { - if (o == null) { - return null; - } - Class currentType = o.getClass(); - if (targetType.isAssignableFrom(currentType)) { - return o; - } - - // convert from CLOB/TEXT/VARCHAR to String - if (targetType == String.class) { - if (Clob.class.isAssignableFrom(currentType)) { - Clob c = (Clob) o; - try { - Reader r = c.getCharacterStream(); - return readStringAndClose(r, -1); - } catch (Exception e) { - throw new IciqlException(e, "error converting CLOB to String: ", e.toString()); - } - } - return o.toString(); - } - - if (Boolean.class.isAssignableFrom(targetType) || boolean.class.isAssignableFrom(targetType)) { - // convert from number to boolean - if (Number.class.isAssignableFrom(currentType)) { - Number n = (Number) o; - return n.intValue() > 0; - } - // convert from string to boolean - if (String.class.isAssignableFrom(currentType)) { - String s = o.toString().toLowerCase(); - float f = 0f; - try { - f = Float.parseFloat(s); - } catch (Exception e) { - } - return f > 0 || s.equals("true") || s.equals("yes") || s.equals("y") || s.equals("on"); - } - } - - // convert from boolean to number - if (Boolean.class.isAssignableFrom(currentType)) { - Boolean b = (Boolean) o; - Integer n = b ? 1 : 0; - if (Number.class.isAssignableFrom(targetType)) { - return n.intValue(); - } else if (byte.class.isAssignableFrom(targetType)) { - return n.byteValue(); - } else if (short.class.isAssignableFrom(targetType)) { - return n.shortValue(); - } else if (int.class.isAssignableFrom(targetType)) { - return n.intValue(); - } else if (long.class.isAssignableFrom(targetType)) { - return n.longValue(); - } else if (float.class.isAssignableFrom(targetType)) { - return n.floatValue(); - } else if (double.class.isAssignableFrom(targetType)) { - return n.doubleValue(); - } else if (boolean.class.isAssignableFrom(targetType)) { - return b.booleanValue(); - } - } - - // convert from number to number - if (Number.class.isAssignableFrom(currentType)) { - Number n = (Number) o; - if (targetType == byte.class || targetType == Byte.class) { - return n.byteValue(); - } else if (targetType == short.class || targetType == Short.class) { - return n.shortValue(); - } else if (targetType == int.class || targetType == Integer.class) { - return n.intValue(); - } else if (targetType == long.class || targetType == Long.class) { - return n.longValue(); - } else if (targetType == double.class || targetType == Double.class) { - return n.doubleValue(); - } else if (targetType == float.class || targetType == Float.class) { - return n.floatValue(); - } else if (targetType == BigDecimal.class) { - return new BigDecimal(n.doubleValue()); - } else if (targetType == java.util.Date.class) { - return new java.util.Date(n.longValue()); - } else if (targetType == java.sql.Date.class) { - return new java.sql.Date(n.longValue()); - } else if (targetType == java.sql.Time.class) { - return new java.sql.Time(n.longValue()); - } else if (targetType == java.sql.Timestamp.class) { - return new java.sql.Timestamp(n.longValue()); - } - } - - if (Date.class.isAssignableFrom(currentType)) { - Date d = (Date) o; - if (targetType == Date.class) { - return o; - } else if (targetType == java.sql.Date.class) { - return new java.sql.Date(d.getTime()); - } else if (targetType == java.sql.Time.class) { - return new java.sql.Time(d.getTime()); - } else if (targetType == java.sql.Timestamp.class) { - return new java.sql.Timestamp(d.getTime()); - } - } - - // convert from BLOB - if (targetType == byte[].class) { - if (Blob.class.isAssignableFrom(currentType)) { - Blob b = (Blob) o; - try { - InputStream is = b.getBinaryStream(); - return readBlobAndClose(is, -1); - } catch (Exception e) { - throw new IciqlException(e, "error converting BLOB to byte[]: ", e.toString()); - } - } - } - throw new IciqlException("Can not convert the value {0} from {1} to {2}", o, currentType, targetType); - } - - /** - * Identify the EnumType for the field. - * - * @param f - * @return null or the EnumType - */ - public static EnumType getEnumType(Field f) { - EnumType enumType = null; - if (f.getType().isEnum()) { - enumType = EnumType.DEFAULT_TYPE; - if (f.getType().isAnnotationPresent(IQEnum.class)) { - // enum definition is annotated for all instances - IQEnum iqenum = f.getType().getAnnotation(IQEnum.class); - enumType = iqenum.value(); - } - if (f.isAnnotationPresent(IQEnum.class)) { - // this instance of the enum is annotated - IQEnum iqenum = f.getAnnotation(IQEnum.class); - enumType = iqenum.value(); - } - } - return enumType; - } - - /** - * Identify the EnumType from the annotations. - * - * @param annotations - * @return null or the EnumType - */ - public static EnumType getEnumType(Annotation [] annotations) { - EnumType enumType = null; - if (annotations != null) { - for (Annotation annotation : annotations) { - if (annotation instanceof IQEnum) { - enumType = ((IQEnum) annotation).value(); - break; - } - } - } - return enumType; - } - - public static Class getEnumTypeClass(Field f) { - if (f.getType().isEnum()) { - if (EnumId.class.isAssignableFrom(f.getType())) { - // custom enumid mapping - return ((EnumId) f.getType().getEnumConstants()[0]).enumIdClass(); - } - } - return null; - } - - public static Object convertEnum(Enum o, EnumType type) { - if (o == null) { - return null; - } - switch (type) { - case ORDINAL: - return o.ordinal(); - case ENUMID: - if (!EnumId.class.isAssignableFrom(o.getClass())) { - throw new IciqlException("Can not convert the enum {0} using ENUMID", o); - } - EnumId enumid = (EnumId) o; - return enumid.enumId(); - case NAME: - default: - return o.name(); - } - } - - public static Object convertEnum(Object o, Class targetType, EnumType type) { - if (o == null) { - return null; - } - Class currentType = o.getClass(); - if (targetType.isAssignableFrom(currentType)) { - return o; - } - // convert from VARCHAR/TEXT/INT to Enum - Enum[] values = (Enum[]) targetType.getEnumConstants(); - if (Clob.class.isAssignableFrom(currentType)) { - // TEXT/CLOB field - Clob c = (Clob) o; - String name = null; - try { - Reader r = c.getCharacterStream(); - name = readStringAndClose(r, -1); - } catch (Exception e) { - throw new IciqlException(e, "error converting CLOB to String: ", e.toString()); - } - - // find name match - if (type.equals(EnumType.ENUMID)) { - // ENUMID mapping - for (Enum value : values) { - EnumId enumid = (EnumId) value; - if (enumid.enumId().equals(name)) { - return value; - } - } - } else if (type.equals(EnumType.NAME)) { - // standard Enum.name() mapping - for (Enum value : values) { - if (value.name().equalsIgnoreCase(name)) { - return value; - } - } - } - } else if (String.class.isAssignableFrom(currentType)) { - // VARCHAR field - String name = (String) o; - if (type.equals(EnumType.ENUMID)) { - // ENUMID mapping - for (Enum value : values) { - EnumId enumid = (EnumId) value; - if (enumid.enumId().equals(name)) { - return value; - } - } - } else if (type.equals(EnumType.NAME)) { - // standard Enum.name() mapping - for (Enum value : values) { - if (value.name().equalsIgnoreCase(name)) { - return value; - } - } - } - } else if (Number.class.isAssignableFrom(currentType)) { - // INT field - int n = ((Number) o).intValue(); - if (type.equals(EnumType.ORDINAL)) { - // ORDINAL mapping - for (Enum value : values) { - if (value.ordinal() == n) { - return value; - } - } - } else if (type.equals(EnumType.ENUMID)) { - if (!EnumId.class.isAssignableFrom(targetType)) { - throw new IciqlException("Can not convert the value {0} from {1} to {2} using ENUMID", o, - currentType, targetType); - } - // ENUMID mapping - for (Enum value : values) { - EnumId enumid = (EnumId) value; - if (enumid.enumId().equals(n)) { - return value; - } - } - } - } else { - // custom object mapping - if (type.equals(EnumType.ENUMID)) { - if (!EnumId.class.isAssignableFrom(targetType)) { - throw new IciqlException("Can not convert the value {0} from {1} to {2} using ENUMID", o, - currentType, targetType); - } - // ENUMID mapping - for (Enum value : values) { - EnumId enumid = (EnumId) value; - if (enumid.enumId().equals(o)) { - return value; - } - } - } - } - throw new IciqlException("Can not convert the value {0} from {1} to {2}", o, currentType, targetType); - } - - /** - * Read a number of characters from a reader and close it. - * - * @param in - * the reader - * @param length - * the maximum number of characters to read, or -1 to read until - * the end of file - * @return the string read - */ - public static String readStringAndClose(Reader in, int length) throws IOException { - try { - if (length <= 0) { - length = Integer.MAX_VALUE; - } - int block = Math.min(BUFFER_BLOCK_SIZE, length); - StringWriter out = new StringWriter(length == Integer.MAX_VALUE ? block : length); - char[] buff = new char[block]; - while (length > 0) { - int len = Math.min(block, length); - len = in.read(buff, 0, len); - if (len < 0) { - break; - } - out.write(buff, 0, len); - length -= len; - } - return out.toString(); - } finally { - in.close(); - } - } - - /** - * Read a number of bytes from a stream and close it. - * - * @param in - * the stream - * @param length - * the maximum number of bytes to read, or -1 to read until the - * end of file - * @return the string read - */ - public static byte[] readBlobAndClose(InputStream in, int length) throws IOException { - try { - if (length <= 0) { - length = Integer.MAX_VALUE; - } - int block = Math.min(BUFFER_BLOCK_SIZE, length); - ByteArrayOutputStream out = new ByteArrayOutputStream(length == Integer.MAX_VALUE ? block : length); - byte[] buff = new byte[block]; - while (length > 0) { - int len = Math.min(block, length); - len = in.read(buff, 0, len); - if (len < 0) { - break; - } - out.write(buff, 0, len); - length -= len; - } - return out.toByteArray(); - } finally { - in.close(); - } - } - - /** - * Identify the data type adapter class in the annotations. - * - * @param annotations - * @return null or the data type adapter class - */ - public static Class> getDataTypeAdapter(Annotation [] annotations) { - Class> typeAdapter = null; - if (annotations != null) { - for (Annotation annotation : annotations) { - if (annotation instanceof TypeAdapter) { - typeAdapter = ((TypeAdapter) annotation).value(); - } else if (annotation.annotationType().isAnnotationPresent(TypeAdapter.class)) { - typeAdapter = annotation.annotationType().getAnnotation(TypeAdapter.class).value(); - } - } - } - return typeAdapter; - } + public static final AtomicLong COUNTER = new AtomicLong(0); + + public static final AtomicInteger AS_COUNTER = new AtomicInteger(0); + + private static final boolean MAKE_ACCESSIBLE = true; + + private static final int BUFFER_BLOCK_SIZE = 4 * 1024; + + public static synchronized int nextAsCount() { + // prevent negative values and use a threadsafe counter + int count = AS_COUNTER.incrementAndGet(); + if (count == Integer.MAX_VALUE) { + count = 0; + AS_COUNTER.set(count); + } + return count; + } + + @SuppressWarnings("unchecked") + public static Class getClass(X x) { + return (Class) x.getClass(); + } + + public static Class loadClass(String className) { + try { + return Class.forName(className); + } catch (Exception e) { + throw new IciqlException(e); + } + } + + public static Iterable newArrayIterable(final T[] a) { + return Arrays.asList(a); + } + + public static ArrayList newArrayList() { + return new ArrayList(); + } + + public static ArrayList newArrayList(Collection c) { + return new ArrayList(c); + } + + public static HashSet newHashSet() { + return new HashSet(); + } + + public static HashSet newHashSet(Collection list) { + return new HashSet(list); + } + + public static HashMap newHashMap() { + return new HashMap(); + } + + public static Map newSynchronizedHashMap() { + HashMap map = newHashMap(); + return Collections.synchronizedMap(map); + } + + public static IdentityHashMap newIdentityHashMap() { + return new IdentityHashMap(); + } + + public static ThreadLocal newThreadLocal(final Class clazz) { + return new ThreadLocal() { + @SuppressWarnings("rawtypes") + @Override + protected T initialValue() { + try { + return clazz.newInstance(); + } catch (Exception e) { + if (MAKE_ACCESSIBLE) { + Constructor[] constructors = clazz.getDeclaredConstructors(); + // try 0 length constructors + for (Constructor c : constructors) { + if (c.getParameterTypes().length == 0) { + c.setAccessible(true); + try { + return clazz.newInstance(); + } catch (Exception e2) { + // ignore + } + } + } + } + throw new IciqlException(e, + "Missing default constructor? Exception trying to instantiate {0}: {1}", clazz.getName(), + e.getMessage()); + } + } + }; + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + public static T newObject(Class clazz) { + // must create new instances + if (clazz == int.class || clazz == Integer.class) { + return (T) new Integer((int) (COUNTER.getAndIncrement() % Integer.MAX_VALUE)); + } else if (clazz == String.class) { + return (T) ("" + COUNTER.getAndIncrement()); + } else if (clazz == long.class || clazz == Long.class) { + return (T) new Long(COUNTER.getAndIncrement()); + } else if (clazz == short.class || clazz == Short.class) { + return (T) new Short((short) (COUNTER.getAndIncrement() % Short.MAX_VALUE)); + } else if (clazz == byte.class || clazz == Byte.class) { + return (T) new Byte((byte) (COUNTER.getAndIncrement() % Byte.MAX_VALUE)); + } else if (clazz == float.class || clazz == Float.class) { + return (T) new Float(COUNTER.getAndIncrement()); + } else if (clazz == double.class || clazz == Double.class) { + return (T) new Double(COUNTER.getAndIncrement()); + } else if (clazz == boolean.class || clazz == Boolean.class) { + COUNTER.getAndIncrement(); + return (T) new Boolean(false); + } else if (clazz == BigDecimal.class) { + return (T) new BigDecimal(COUNTER.getAndIncrement()); + } else if (clazz == BigInteger.class) { + return (T) new BigInteger("" + COUNTER.getAndIncrement()); + } else if (clazz == java.sql.Date.class) { + return (T) new java.sql.Date(COUNTER.getAndIncrement()); + } else if (clazz == java.sql.Time.class) { + return (T) new java.sql.Time(COUNTER.getAndIncrement()); + } else if (clazz == java.sql.Timestamp.class) { + return (T) new java.sql.Timestamp(COUNTER.getAndIncrement()); + } else if (clazz == java.util.Date.class) { + return (T) new java.util.Date(COUNTER.getAndIncrement()); + } else if (clazz == byte[].class) { + COUNTER.getAndIncrement(); + return (T) new byte[0]; + } else if (clazz.isEnum()) { + COUNTER.getAndIncrement(); + // enums can not be instantiated reflectively + // return first constant as reference + return clazz.getEnumConstants()[0]; + } else if (clazz == java.util.UUID.class) { + COUNTER.getAndIncrement(); + return (T) UUID.randomUUID(); + } else if (Set.class == clazz) { + COUNTER.getAndIncrement(); + return (T) new HashSet(); + } else if (List.class == clazz) { + COUNTER.getAndIncrement(); + return (T) new ArrayList(); + } else if (Map.class == clazz) { + COUNTER.getAndIncrement(); + return (T) new HashMap(); + } + try { + return clazz.newInstance(); + } catch (Exception e) { + if (MAKE_ACCESSIBLE) { + Constructor[] constructors = clazz.getDeclaredConstructors(); + // try 0 length constructors + for (Constructor c : constructors) { + if (c.getParameterTypes().length == 0) { + c.setAccessible(true); + try { + return clazz.newInstance(); + } catch (Exception e2) { + // ignore + } + } + } + // try 1 length constructors + for (Constructor c : constructors) { + if (c.getParameterTypes().length == 1) { + c.setAccessible(true); + try { + return (T) c.newInstance(new Object[1]); + } catch (Exception e2) { + // ignore + } + } + } + } + throw new IciqlException(e, "Missing default constructor?! Exception trying to instantiate {0}: {1}", + clazz.getName(), e.getMessage()); + } + } + + public static boolean isSimpleType(Class clazz) { + if (Number.class.isAssignableFrom(clazz)) { + return true; + } else if (clazz == String.class) { + return true; + } + return false; + } + + public static Object convert(Object o, Class targetType) { + if (o == null) { + return null; + } + Class currentType = o.getClass(); + if (targetType.isAssignableFrom(currentType)) { + return o; + } + + // convert from CLOB/TEXT/VARCHAR to String + if (targetType == String.class) { + if (Clob.class.isAssignableFrom(currentType)) { + Clob c = (Clob) o; + try { + Reader r = c.getCharacterStream(); + return readStringAndClose(r, -1); + } catch (Exception e) { + throw new IciqlException(e, "error converting CLOB to String: ", e.toString()); + } + } + return o.toString(); + } + + if (Boolean.class.isAssignableFrom(targetType) || boolean.class.isAssignableFrom(targetType)) { + // convert from number to boolean + if (Number.class.isAssignableFrom(currentType)) { + Number n = (Number) o; + return n.intValue() > 0; + } + // convert from string to boolean + if (String.class.isAssignableFrom(currentType)) { + String s = o.toString().toLowerCase(); + float f = 0f; + try { + f = Float.parseFloat(s); + } catch (Exception e) { + } + return f > 0 || s.equals("true") || s.equals("yes") || s.equals("y") || s.equals("on"); + } + } + + // convert from boolean to number + if (Boolean.class.isAssignableFrom(currentType)) { + Boolean b = (Boolean) o; + Integer n = b ? 1 : 0; + if (Number.class.isAssignableFrom(targetType)) { + return n.intValue(); + } else if (byte.class.isAssignableFrom(targetType)) { + return n.byteValue(); + } else if (short.class.isAssignableFrom(targetType)) { + return n.shortValue(); + } else if (int.class.isAssignableFrom(targetType)) { + return n.intValue(); + } else if (long.class.isAssignableFrom(targetType)) { + return n.longValue(); + } else if (float.class.isAssignableFrom(targetType)) { + return n.floatValue(); + } else if (double.class.isAssignableFrom(targetType)) { + return n.doubleValue(); + } else if (boolean.class.isAssignableFrom(targetType)) { + return b.booleanValue(); + } + } + + // convert from number to number + if (Number.class.isAssignableFrom(currentType)) { + Number n = (Number) o; + if (targetType == byte.class || targetType == Byte.class) { + return n.byteValue(); + } else if (targetType == short.class || targetType == Short.class) { + return n.shortValue(); + } else if (targetType == int.class || targetType == Integer.class) { + return n.intValue(); + } else if (targetType == long.class || targetType == Long.class) { + return n.longValue(); + } else if (targetType == double.class || targetType == Double.class) { + return n.doubleValue(); + } else if (targetType == float.class || targetType == Float.class) { + return n.floatValue(); + } else if (targetType == BigDecimal.class) { + return new BigDecimal(n.doubleValue()); + } else if (targetType == java.util.Date.class) { + return new java.util.Date(n.longValue()); + } else if (targetType == java.sql.Date.class) { + return new java.sql.Date(n.longValue()); + } else if (targetType == java.sql.Time.class) { + return new java.sql.Time(n.longValue()); + } else if (targetType == java.sql.Timestamp.class) { + return new java.sql.Timestamp(n.longValue()); + } + } + + if (Date.class.isAssignableFrom(currentType)) { + Date d = (Date) o; + if (targetType == Date.class) { + return o; + } else if (targetType == java.sql.Date.class) { + return new java.sql.Date(d.getTime()); + } else if (targetType == java.sql.Time.class) { + return new java.sql.Time(d.getTime()); + } else if (targetType == java.sql.Timestamp.class) { + return new java.sql.Timestamp(d.getTime()); + } + } + + // convert from BLOB + if (targetType == byte[].class) { + if (Blob.class.isAssignableFrom(currentType)) { + Blob b = (Blob) o; + try { + InputStream is = b.getBinaryStream(); + return readBlobAndClose(is, -1); + } catch (Exception e) { + throw new IciqlException(e, "error converting BLOB to byte[]: ", e.toString()); + } + } + } + throw new IciqlException("Can not convert the value {0} from {1} to {2}", o, currentType, targetType); + } + + /** + * Identify the EnumType for the field. + * + * @param f + * @return null or the EnumType + */ + public static EnumType getEnumType(Field f) { + EnumType enumType = null; + if (f.getType().isEnum()) { + enumType = EnumType.DEFAULT_TYPE; + if (f.getType().isAnnotationPresent(IQEnum.class)) { + // enum definition is annotated for all instances + IQEnum iqenum = f.getType().getAnnotation(IQEnum.class); + enumType = iqenum.value(); + } + if (f.isAnnotationPresent(IQEnum.class)) { + // this instance of the enum is annotated + IQEnum iqenum = f.getAnnotation(IQEnum.class); + enumType = iqenum.value(); + } + } + return enumType; + } + + /** + * Identify the EnumType from the annotations. + * + * @param annotations + * @return null or the EnumType + */ + public static EnumType getEnumType(Annotation[] annotations) { + EnumType enumType = null; + if (annotations != null) { + for (Annotation annotation : annotations) { + if (annotation instanceof IQEnum) { + enumType = ((IQEnum) annotation).value(); + break; + } + } + } + return enumType; + } + + public static Class getEnumTypeClass(Field f) { + if (f.getType().isEnum()) { + if (EnumId.class.isAssignableFrom(f.getType())) { + // custom enumid mapping + return ((EnumId) f.getType().getEnumConstants()[0]).enumIdClass(); + } + } + return null; + } + + public static Object convertEnum(Enum o, EnumType type) { + if (o == null) { + return null; + } + switch (type) { + case ORDINAL: + return o.ordinal(); + case ENUMID: + if (!EnumId.class.isAssignableFrom(o.getClass())) { + throw new IciqlException("Can not convert the enum {0} using ENUMID", o); + } + EnumId enumid = (EnumId) o; + return enumid.enumId(); + case NAME: + default: + return o.name(); + } + } + + public static Object convertEnum(Object o, Class targetType, EnumType type) { + if (o == null) { + return null; + } + Class currentType = o.getClass(); + if (targetType.isAssignableFrom(currentType)) { + return o; + } + // convert from VARCHAR/TEXT/INT to Enum + Enum[] values = (Enum[]) targetType.getEnumConstants(); + if (Clob.class.isAssignableFrom(currentType)) { + // TEXT/CLOB field + Clob c = (Clob) o; + String name = null; + try { + Reader r = c.getCharacterStream(); + name = readStringAndClose(r, -1); + } catch (Exception e) { + throw new IciqlException(e, "error converting CLOB to String: ", e.toString()); + } + + // find name match + if (type.equals(EnumType.ENUMID)) { + // ENUMID mapping + for (Enum value : values) { + EnumId enumid = (EnumId) value; + if (enumid.enumId().equals(name)) { + return value; + } + } + } else if (type.equals(EnumType.NAME)) { + // standard Enum.name() mapping + for (Enum value : values) { + if (value.name().equalsIgnoreCase(name)) { + return value; + } + } + } + } else if (String.class.isAssignableFrom(currentType)) { + // VARCHAR field + String name = (String) o; + if (type.equals(EnumType.ENUMID)) { + // ENUMID mapping + for (Enum value : values) { + EnumId enumid = (EnumId) value; + if (enumid.enumId().equals(name)) { + return value; + } + } + } else if (type.equals(EnumType.NAME)) { + // standard Enum.name() mapping + for (Enum value : values) { + if (value.name().equalsIgnoreCase(name)) { + return value; + } + } + } + } else if (Number.class.isAssignableFrom(currentType)) { + // INT field + int n = ((Number) o).intValue(); + if (type.equals(EnumType.ORDINAL)) { + // ORDINAL mapping + for (Enum value : values) { + if (value.ordinal() == n) { + return value; + } + } + } else if (type.equals(EnumType.ENUMID)) { + if (!EnumId.class.isAssignableFrom(targetType)) { + throw new IciqlException("Can not convert the value {0} from {1} to {2} using ENUMID", o, + currentType, targetType); + } + // ENUMID mapping + for (Enum value : values) { + EnumId enumid = (EnumId) value; + if (enumid.enumId().equals(n)) { + return value; + } + } + } + } else { + // custom object mapping + if (type.equals(EnumType.ENUMID)) { + if (!EnumId.class.isAssignableFrom(targetType)) { + throw new IciqlException("Can not convert the value {0} from {1} to {2} using ENUMID", o, + currentType, targetType); + } + // ENUMID mapping + for (Enum value : values) { + EnumId enumid = (EnumId) value; + if (enumid.enumId().equals(o)) { + return value; + } + } + } + } + throw new IciqlException("Can not convert the value {0} from {1} to {2}", o, currentType, targetType); + } + + /** + * Read a number of characters from a reader and close it. + * + * @param in the reader + * @param length the maximum number of characters to read, or -1 to read until + * the end of file + * @return the string read + */ + public static String readStringAndClose(Reader in, int length) throws IOException { + try { + if (length <= 0) { + length = Integer.MAX_VALUE; + } + int block = Math.min(BUFFER_BLOCK_SIZE, length); + StringWriter out = new StringWriter(length == Integer.MAX_VALUE ? block : length); + char[] buff = new char[block]; + while (length > 0) { + int len = Math.min(block, length); + len = in.read(buff, 0, len); + if (len < 0) { + break; + } + out.write(buff, 0, len); + length -= len; + } + return out.toString(); + } finally { + in.close(); + } + } + + /** + * Read a number of bytes from a stream and close it. + * + * @param in the stream + * @param length the maximum number of bytes to read, or -1 to read until the + * end of file + * @return the string read + */ + public static byte[] readBlobAndClose(InputStream in, int length) throws IOException { + try { + if (length <= 0) { + length = Integer.MAX_VALUE; + } + int block = Math.min(BUFFER_BLOCK_SIZE, length); + ByteArrayOutputStream out = new ByteArrayOutputStream(length == Integer.MAX_VALUE ? block : length); + byte[] buff = new byte[block]; + while (length > 0) { + int len = Math.min(block, length); + len = in.read(buff, 0, len); + if (len < 0) { + break; + } + out.write(buff, 0, len); + length -= len; + } + return out.toByteArray(); + } finally { + in.close(); + } + } + + /** + * Identify the data type adapter class in the annotations. + * + * @param annotations + * @return null or the data type adapter class + */ + public static Class> getDataTypeAdapter(Annotation[] annotations) { + Class> typeAdapter = null; + if (annotations != null) { + for (Annotation annotation : annotations) { + if (annotation instanceof TypeAdapter) { + typeAdapter = ((TypeAdapter) annotation).value(); + } else if (annotation.annotationType().isAnnotationPresent(TypeAdapter.class)) { + typeAdapter = annotation.annotationType().getAnnotation(TypeAdapter.class).value(); + } + } + } + return typeAdapter; + } } diff --git a/src/main/java/com/iciql/util/WeakIdentityHashMap.java b/src/main/java/com/iciql/util/WeakIdentityHashMap.java index bc03cd0..0a2ad1e 100644 --- a/src/main/java/com/iciql/util/WeakIdentityHashMap.java +++ b/src/main/java/com/iciql/util/WeakIdentityHashMap.java @@ -17,227 +17,225 @@ package com.iciql.util; +import com.iciql.IciqlException; + import java.lang.ref.WeakReference; import java.util.Collection; import java.util.Map; import java.util.Set; -import com.iciql.IciqlException; - /** * This hash map uses weak references, so that elements that are no longer * referenced elsewhere can be garbage collected. It also uses object identity * to compare keys. The garbage collection happens when trying to add new data, * or when resizing. - * - * @param - * the keys - * @param - * the value + * + * @param the keys + * @param the value */ public class WeakIdentityHashMap implements Map { - private static final int MAX_LOAD = 90; - private static final WeakReference DELETED_KEY = new WeakReference(null); - private int mask, len, size, deletedCount, level; - private int maxSize, minSize, maxDeleted; - private WeakReference[] keys; - private V[] values; - - public WeakIdentityHashMap() { - reset(2); - } - - public int size() { - return size; - } - - private void checkSizePut() { - if (deletedCount > size) { - rehash(level); - } - if (size + deletedCount >= maxSize) { - rehash(level + 1); - } - } - - private void checkSizeRemove() { - if (size < minSize && level > 0) { - rehash(level - 1); - } else if (deletedCount > maxDeleted) { - rehash(level); - } - } - - private int getIndex(Object key) { - return System.identityHashCode(key) & mask; - } - - @SuppressWarnings("unchecked") - private void reset(int newLevel) { - minSize = size * 3 / 4; - size = 0; - level = newLevel; - len = 2 << level; - mask = len - 1; - maxSize = (int) (len * MAX_LOAD / 100L); - deletedCount = 0; - maxDeleted = 20 + len / 2; - keys = new WeakReference[len]; - values = (V[]) new Object[len]; - } - - public V put(K key, V value) { - checkSizePut(); - int index = getIndex(key); - int plus = 1; - int deleted = -1; - do { - WeakReference k = keys[index]; - if (k == null) { - // found an empty record - if (deleted >= 0) { - index = deleted; - deletedCount--; - } - size++; - keys[index] = new WeakReference(key); - values[index] = value; - return null; - } else if (k == DELETED_KEY) { - if (deleted < 0) { - // found the first deleted record - deleted = index; - } - } else { - Object r = k.get(); - if (r == null) { - delete(index); - } else if (r == key) { - // update existing - V old = values[index]; - values[index] = value; - return old; - } - } - index = (index + plus++) & mask; - } while (plus <= len); - throw new IciqlException("Hashmap is full"); - } - - public V remove(Object key) { - checkSizeRemove(); - int index = getIndex(key); - int plus = 1; - do { - WeakReference k = keys[index]; - if (k == null) { - // found an empty record - return null; - } else if (k == DELETED_KEY) { - // continue - } else { - Object r = k.get(); - if (r == null) { - delete(index); - } else if (r == key) { - // found the record - V old = values[index]; - delete(index); - return old; - } - } - index = (index + plus++) & mask; - k = keys[index]; - } while (plus <= len); - // not found - return null; - } - - @SuppressWarnings("unchecked") - private void delete(int index) { - keys[index] = (WeakReference) DELETED_KEY; - values[index] = null; - deletedCount++; - size--; - } - - private void rehash(int newLevel) { - WeakReference[] oldKeys = keys; - V[] oldValues = values; - reset(newLevel); - for (int i = 0; i < oldKeys.length; i++) { - WeakReference k = oldKeys[i]; - if (k != null && k != DELETED_KEY) { - K key = k.get(); - if (key != null) { - put(key, oldValues[i]); - } - } - } - } - - public V get(Object key) { - int index = getIndex(key); - int plus = 1; - do { - WeakReference k = keys[index]; - if (k == null) { - return null; - } else if (k == DELETED_KEY) { - // continue - } else { - Object r = k.get(); - if (r == null) { - delete(index); - } else if (r == key) { - return values[index]; - } - } - index = (index + plus++) & mask; - } while (plus <= len); - return null; - } - - public void clear() { - reset(2); - } - - public boolean containsKey(Object key) { - return get(key) != null; - } - - public boolean containsValue(Object value) { - if (value == null) { - return false; - } - for (V item : values) { - if (value.equals(item)) { - return true; - } - } - return false; - } - - public Set> entrySet() { - throw new UnsupportedOperationException(); - } - - public boolean isEmpty() { - return size == 0; - } - - public Set keySet() { - throw new UnsupportedOperationException(); - } - - public void putAll(Map m) { - throw new UnsupportedOperationException(); - } - - public Collection values() { - throw new UnsupportedOperationException(); - } + private static final int MAX_LOAD = 90; + private static final WeakReference DELETED_KEY = new WeakReference(null); + private int mask, len, size, deletedCount, level; + private int maxSize, minSize, maxDeleted; + private WeakReference[] keys; + private V[] values; + + public WeakIdentityHashMap() { + reset(2); + } + + public int size() { + return size; + } + + private void checkSizePut() { + if (deletedCount > size) { + rehash(level); + } + if (size + deletedCount >= maxSize) { + rehash(level + 1); + } + } + + private void checkSizeRemove() { + if (size < minSize && level > 0) { + rehash(level - 1); + } else if (deletedCount > maxDeleted) { + rehash(level); + } + } + + private int getIndex(Object key) { + return System.identityHashCode(key) & mask; + } + + @SuppressWarnings("unchecked") + private void reset(int newLevel) { + minSize = size * 3 / 4; + size = 0; + level = newLevel; + len = 2 << level; + mask = len - 1; + maxSize = (int) (len * MAX_LOAD / 100L); + deletedCount = 0; + maxDeleted = 20 + len / 2; + keys = new WeakReference[len]; + values = (V[]) new Object[len]; + } + + public V put(K key, V value) { + checkSizePut(); + int index = getIndex(key); + int plus = 1; + int deleted = -1; + do { + WeakReference k = keys[index]; + if (k == null) { + // found an empty record + if (deleted >= 0) { + index = deleted; + deletedCount--; + } + size++; + keys[index] = new WeakReference(key); + values[index] = value; + return null; + } else if (k == DELETED_KEY) { + if (deleted < 0) { + // found the first deleted record + deleted = index; + } + } else { + Object r = k.get(); + if (r == null) { + delete(index); + } else if (r == key) { + // update existing + V old = values[index]; + values[index] = value; + return old; + } + } + index = (index + plus++) & mask; + } while (plus <= len); + throw new IciqlException("Hashmap is full"); + } + + public V remove(Object key) { + checkSizeRemove(); + int index = getIndex(key); + int plus = 1; + do { + WeakReference k = keys[index]; + if (k == null) { + // found an empty record + return null; + } else if (k == DELETED_KEY) { + // continue + } else { + Object r = k.get(); + if (r == null) { + delete(index); + } else if (r == key) { + // found the record + V old = values[index]; + delete(index); + return old; + } + } + index = (index + plus++) & mask; + k = keys[index]; + } while (plus <= len); + // not found + return null; + } + + @SuppressWarnings("unchecked") + private void delete(int index) { + keys[index] = (WeakReference) DELETED_KEY; + values[index] = null; + deletedCount++; + size--; + } + + private void rehash(int newLevel) { + WeakReference[] oldKeys = keys; + V[] oldValues = values; + reset(newLevel); + for (int i = 0; i < oldKeys.length; i++) { + WeakReference k = oldKeys[i]; + if (k != null && k != DELETED_KEY) { + K key = k.get(); + if (key != null) { + put(key, oldValues[i]); + } + } + } + } + + public V get(Object key) { + int index = getIndex(key); + int plus = 1; + do { + WeakReference k = keys[index]; + if (k == null) { + return null; + } else if (k == DELETED_KEY) { + // continue + } else { + Object r = k.get(); + if (r == null) { + delete(index); + } else if (r == key) { + return values[index]; + } + } + index = (index + plus++) & mask; + } while (plus <= len); + return null; + } + + public void clear() { + reset(2); + } + + public boolean containsKey(Object key) { + return get(key) != null; + } + + public boolean containsValue(Object value) { + if (value == null) { + return false; + } + for (V item : values) { + if (value.equals(item)) { + return true; + } + } + return false; + } + + public Set> entrySet() { + throw new UnsupportedOperationException(); + } + + public boolean isEmpty() { + return size == 0; + } + + public Set keySet() { + throw new UnsupportedOperationException(); + } + + public void putAll(Map m) { + throw new UnsupportedOperationException(); + } + + public Collection values() { + throw new UnsupportedOperationException(); + } } diff --git a/src/main/java/com/iciql/util/package.html b/src/main/java/com/iciql/util/package.html index 3d24dee..62adcb5 100644 --- a/src/main/java/com/iciql/util/package.html +++ b/src/main/java/com/iciql/util/package.html @@ -16,8 +16,9 @@ limitations under the License. --> - -Javadoc package documentation + + + Javadoc package documentation Utility classes for iciql. diff --git a/src/main/java/java/lang/AutoCloseable.java b/src/main/java/java/lang/AutoCloseable.java index 2141fbc..4484403 100644 --- a/src/main/java/java/lang/AutoCloseable.java +++ b/src/main/java/java/lang/AutoCloseable.java @@ -36,18 +36,18 @@ public interface AutoCloseable { * Closes this resource, relinquishing any underlying resources. * This method is invoked automatically by the {@code * try}-with-resources statement. - * + *

*

Classes implementing this method are strongly encouraged to * be declared to throw more specific exceptions (or no exception * at all, if the close cannot fail). - * + *

*

Note that unlike the {@link java.io.Closeable#close close} * method of {@link java.io.Closeable}, this {@code close} method * is not required to be idempotent. In other words, * calling this {@code close} method more than once may have some * visible side effect, unlike {@code Closeable.close} which is * required to have no effect if called more than once. - * + *

* However, while not required to be idempotent, implementers of * this interface are strongly encouraged to make their {@code * close} methods idempotent. diff --git a/src/test/java/com/iciql/test/AliasMapTest.java b/src/test/java/com/iciql/test/AliasMapTest.java index 092f38b..a5e7eee 100644 --- a/src/test/java/com/iciql/test/AliasMapTest.java +++ b/src/test/java/com/iciql/test/AliasMapTest.java @@ -17,123 +17,122 @@ package com.iciql.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - import com.iciql.Db; import com.iciql.IciqlException; import com.iciql.test.models.PrimitivesModel; import com.iciql.test.models.Product; import com.iciql.util.Utils; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Tests object and primitive alias referencing. */ public class AliasMapTest { - /** - * Tests that columns (p.unitsInStock) are not compared by value with the - * value (9), but by reference (using an identity hash map). See - * http://code.google.com/p/h2database/issues/detail?id=119 - * - * @author d moebius at scoop dash gmbh dot de - */ - @Test - public void testObjectAliasMapping() throws Exception { - Db db = IciqlSuite.openNewDb(); - db.insertAll(Product.getList()); + /** + * Tests that columns (p.unitsInStock) are not compared by value with the + * value (9), but by reference (using an identity hash map). See + * http://code.google.com/p/h2database/issues/detail?id=119 + * + * @author d moebius at scoop dash gmbh dot de + */ + @Test + public void testObjectAliasMapping() throws Exception { + Db db = IciqlSuite.openNewDb(); + db.insertAll(Product.getList()); - // baseline count is the next id value - long bc = Utils.COUNTER.get(); - // number of fields in primitives model class - // each from() call will increment Utils.COUNTER by this amount - int fc = Product.class.getFields().length; + // baseline count is the next id value + long bc = Utils.COUNTER.get(); + // number of fields in primitives model class + // each from() call will increment Utils.COUNTER by this amount + int fc = Product.class.getFields().length; - Product p = new Product(); - // This test confirms standard object referencing querying. - long count = db.from(p).where(p.productId).is(9).selectCount(); - assertEquals(1, count); - // Confirms that productId counter value is baseline counter value - assertEquals(bc, p.productId.intValue()); - try { - // This test compares "bc + fc" which is the counter value of - // unitsInStock assigned by Utils.newObject() after the 2nd pass - // through from(). - // - // Object fields map by REFERENCE, not value. - db.from(p).where(Long.valueOf(bc + fc).intValue()).is(9).orderBy(p.productId).select(); - assertTrue("Fail: object field is mapping by value.", false); - } catch (IciqlException e) { - assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode()); - assertEquals(bc + 5, p.productId.intValue()); - } + Product p = new Product(); + // This test confirms standard object referencing querying. + long count = db.from(p).where(p.productId).is(9).selectCount(); + assertEquals(1, count); + // Confirms that productId counter value is baseline counter value + assertEquals(bc, p.productId.intValue()); + try { + // This test compares "bc + fc" which is the counter value of + // unitsInStock assigned by Utils.newObject() after the 2nd pass + // through from(). + // + // Object fields map by REFERENCE, not value. + db.from(p).where(Long.valueOf(bc + fc).intValue()).is(9).orderBy(p.productId).select(); + assertTrue("Fail: object field is mapping by value.", false); + } catch (IciqlException e) { + assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode()); + assertEquals(bc + 5, p.productId.intValue()); + } - try { - // This test compares Integer(bc) which is the counter value of - // unitsInStock assigned by Utils.newObject() after the 3rd pass - // through from(). - // - // Object fields map by REFERENCE, not value. - db.from(p).where(Long.valueOf(bc).intValue()).is(9).orderBy(p.productId).select(); - assertTrue("Fail: object field is mapping by value.", false); - } catch (IciqlException e) { - assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode()); - assertEquals(bc + (2 * fc), p.productId.intValue()); - } + try { + // This test compares Integer(bc) which is the counter value of + // unitsInStock assigned by Utils.newObject() after the 3rd pass + // through from(). + // + // Object fields map by REFERENCE, not value. + db.from(p).where(Long.valueOf(bc).intValue()).is(9).orderBy(p.productId).select(); + assertTrue("Fail: object field is mapping by value.", false); + } catch (IciqlException e) { + assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode()); + assertEquals(bc + (2 * fc), p.productId.intValue()); + } - db.close(); - } + db.close(); + } - /** - * Confirms that primitive aliases ARE mapped by value. - */ - @Test - public void testPrimitiveAliasMapping() throws Exception { - Db db = IciqlSuite.openNewDb(); - PrimitivesModel model = new PrimitivesModel(); - model.myLong = 100L; - db.insert(model); - model.myLong = 200L; - db.insert(model); + /** + * Confirms that primitive aliases ARE mapped by value. + */ + @Test + public void testPrimitiveAliasMapping() throws Exception { + Db db = IciqlSuite.openNewDb(); + PrimitivesModel model = new PrimitivesModel(); + model.myLong = 100L; + db.insert(model); + model.myLong = 200L; + db.insert(model); - // baseline count is the next id value - long bc = Utils.COUNTER.get(); - // number of fields in primitives model class - // each from() call will increment Utils.COUNTER by this amount - int fc = PrimitivesModel.class.getFields().length; + // baseline count is the next id value + long bc = Utils.COUNTER.get(); + // number of fields in primitives model class + // each from() call will increment Utils.COUNTER by this amount + int fc = PrimitivesModel.class.getFields().length; - PrimitivesModel p = new PrimitivesModel(); - // This test confirms standard primitive referencing querying. - long count = db.from(p).where(p.myLong).is(100L).selectCount(); - assertEquals(1, count); - // Confirms that myLong counter value is bc - assertEquals(bc, p.myLong); - try { - // This test compares "bc + fc" which is the counter value - // of myLong assigned by Utils.newObject() after the 2nd pass - // through from(). - // - // Primitive fields map by VALUE. - count = db.from(p).where(bc + fc).is(100L).selectCount(); - assertEquals(1, count); - assertEquals(bc + fc, p.myLong); - } catch (IciqlException e) { - assertTrue(e.getMessage(), false); - } - try { - // This test compares "bc" which was the counter value of - // myLong assigned by Utils.newObject() after the 1st pass - // through from(). "bc" is unmapped now and will throw an - // exception. - // - // Primitive fields map by VALUE. - db.from(p).where(bc).is(100L).select(); - } catch (IciqlException e) { - assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode()); - assertEquals(bc + (2 * fc), p.myLong); - } - db.close(); - } + PrimitivesModel p = new PrimitivesModel(); + // This test confirms standard primitive referencing querying. + long count = db.from(p).where(p.myLong).is(100L).selectCount(); + assertEquals(1, count); + // Confirms that myLong counter value is bc + assertEquals(bc, p.myLong); + try { + // This test compares "bc + fc" which is the counter value + // of myLong assigned by Utils.newObject() after the 2nd pass + // through from(). + // + // Primitive fields map by VALUE. + count = db.from(p).where(bc + fc).is(100L).selectCount(); + assertEquals(1, count); + assertEquals(bc + fc, p.myLong); + } catch (IciqlException e) { + assertTrue(e.getMessage(), false); + } + try { + // This test compares "bc" which was the counter value of + // myLong assigned by Utils.newObject() after the 1st pass + // through from(). "bc" is unmapped now and will throw an + // exception. + // + // Primitive fields map by VALUE. + db.from(p).where(bc).is(100L).select(); + } catch (IciqlException e) { + assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode()); + assertEquals(bc + (2 * fc), p.myLong); + } + db.close(); + } } \ No newline at end of file diff --git a/src/test/java/com/iciql/test/AnnotationsTest.java b/src/test/java/com/iciql/test/AnnotationsTest.java index 6aa75ad..f96fe76 100644 --- a/src/test/java/com/iciql/test/AnnotationsTest.java +++ b/src/test/java/com/iciql/test/AnnotationsTest.java @@ -17,18 +17,6 @@ package com.iciql.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.sql.DatabaseMetaData; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - import com.iciql.Db; import com.iciql.IciqlException; import com.iciql.test.models.Product; @@ -37,160 +25,171 @@ import com.iciql.test.models.ProductInheritedAnnotation; import com.iciql.test.models.ProductMixedAnnotation; import com.iciql.test.models.ProductNoCreateTable; import com.iciql.util.Utils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.sql.DatabaseMetaData; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Test annotation processing. */ public class AnnotationsTest { - /** - * This object represents a database (actually a connection to the - * database). - */ - - private Db db; - - @Before - public void setUp() { - db = IciqlSuite.openNewDb(); - db.insertAll(Product.getList()); - db.insertAll(ProductAnnotationOnly.getList()); - db.insertAll(ProductMixedAnnotation.getList()); - } - - @After - public void tearDown() { - db.close(); - } - - @Test - public void testIndexCreation() throws SQLException { - // test indexes are created, and columns are in the right order - DatabaseMetaData meta = db.getConnection().getMetaData(); - String schema = IciqlSuite.getDefaultSchema(db); - boolean toUpper = meta.storesUpperCaseIdentifiers(); - boolean toLower = meta.storesLowerCaseIdentifiers(); - ResultSet rs = meta.getIndexInfo(null, prepName(schema, toUpper, toLower), - prepName("ANNOTATEDPRODUCT", toUpper, toLower), false, true); - - List list = Utils.newArrayList(); - while (rs.next()) { - String col = rs.getString("COLUMN_NAME"); - String index = rs.getString("INDEX_NAME"); - list.add((col + ":" + index).toLowerCase()); - } - assertTrue(list.contains("name:annotatedproduct_idx_0")); - assertTrue(list.contains("cat:annotatedproduct_idx_0")); - assertTrue(list.contains("name:nameidx")); - } - - private String prepName(String name, boolean upper, boolean lower) { - if (name == null) { - return null; - } - if (upper) { - return name.toUpperCase(); - } else if (lower) { - return name.toLowerCase(); - } - return name; - } - - @Test - public void testProductAnnotationOnly() { - ProductAnnotationOnly p = new ProductAnnotationOnly(); - assertEquals(10, db.from(p).selectCount()); - - // test IQColumn.name="cat" - assertEquals(2, db.from(p).where(p.category).is("Beverages").selectCount()); - - // test IQTable.annotationsOnly=true - // public String unmappedField is ignored by iciql - try { - db.from(p).where(p.unmappedField).is("unmapped").selectCount(); - assertTrue("this should never execute", false); - } catch (IciqlException e) { - assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode()); - } - - // 10 objects, 10 autoIncremented unique values - assertEquals(10, db.from(p).selectDistinct(p.productName).size()); - - // test IQTable.primaryKey=id - try { - db.insertAll(ProductAnnotationOnly.getList()); - } catch (IciqlException e) { - assertEquals(IciqlException.CODE_DUPLICATE_KEY, e.getIciqlCode()); - } - } - - @Test - public void testProductMixedAnnotation() { - ProductMixedAnnotation p = new ProductMixedAnnotation(); - - // test IQColumn.name="cat" - assertEquals(2, db.from(p).where(p.category).is("Beverages").selectCount()); - - // test IQTable.annotationsOnly=false - // public String mappedField is reflectively mapped by iciql - assertEquals(10, db.from(p).where(p.mappedField).is("mapped").selectCount()); - - // test IQIgnore annotation - assertEquals(null, db.from(p).selectFirst().productDescription); - - // test IQColumn.primaryKey=true - try { - db.insertAll(ProductMixedAnnotation.getList()); - } catch (IciqlException e) { - assertEquals(IciqlException.CODE_DUPLICATE_KEY, e.getIciqlCode()); - } - } - - @Test - public void testTrimStringAnnotation() { - ProductAnnotationOnly p = new ProductAnnotationOnly(); - ProductAnnotationOnly prod = db.from(p).selectFirst(); - String oldValue = prod.category; - String newValue = "01234567890123456789"; - // 2 chars exceeds field max - prod.category = newValue; - db.update(prod); - - ProductAnnotationOnly newProd = db.from(p).where(p.productId).is(prod.productId).selectFirst(); - assertEquals(newValue.substring(0, 15), newProd.category); - - newProd.category = oldValue; - db.update(newProd); - } - - @Test - public void testColumnInheritanceAnnotation() { - ProductInheritedAnnotation table = new ProductInheritedAnnotation(); - List inserted = ProductInheritedAnnotation.getData(); - db.insertAll(inserted); - - List retrieved = db.from(table).select(); - - for (int j = 0; j < retrieved.size(); j++) { - ProductInheritedAnnotation i = inserted.get(j); - ProductInheritedAnnotation r = retrieved.get(j); - assertEquals(i.category, r.category); - assertEquals(i.mappedField, r.mappedField); - assertEquals(i.unitsInStock, r.unitsInStock); - assertEquals(i.unitPrice, r.unitPrice); - assertEquals(i.name(), r.name()); - assertEquals(i.id(), r.id()); - } - } - - @Test - public void testCreateTableIfRequiredAnnotation() { - // tests IQTable.createTableIfRequired=false - try { - db.insertAll(ProductNoCreateTable.getList()); - } catch (IciqlException e) { - assertEquals(IciqlException.CODE_OBJECT_NOT_FOUND, e.getIciqlCode()); - } - } + /** + * This object represents a database (actually a connection to the + * database). + */ + + private Db db; + + @Before + public void setUp() { + db = IciqlSuite.openNewDb(); + db.insertAll(Product.getList()); + db.insertAll(ProductAnnotationOnly.getList()); + db.insertAll(ProductMixedAnnotation.getList()); + } + + @After + public void tearDown() { + db.close(); + } + + @Test + public void testIndexCreation() throws SQLException { + // test indexes are created, and columns are in the right order + DatabaseMetaData meta = db.getConnection().getMetaData(); + String schema = IciqlSuite.getDefaultSchema(db); + boolean toUpper = meta.storesUpperCaseIdentifiers(); + boolean toLower = meta.storesLowerCaseIdentifiers(); + ResultSet rs = meta.getIndexInfo(null, prepName(schema, toUpper, toLower), + prepName("ANNOTATEDPRODUCT", toUpper, toLower), false, true); + + List list = Utils.newArrayList(); + while (rs.next()) { + String col = rs.getString("COLUMN_NAME"); + String index = rs.getString("INDEX_NAME"); + list.add((col + ":" + index).toLowerCase()); + } + assertTrue(list.contains("name:annotatedproduct_idx_0")); + assertTrue(list.contains("cat:annotatedproduct_idx_0")); + assertTrue(list.contains("name:nameidx")); + } + + private String prepName(String name, boolean upper, boolean lower) { + if (name == null) { + return null; + } + if (upper) { + return name.toUpperCase(); + } else if (lower) { + return name.toLowerCase(); + } + return name; + } + + @Test + public void testProductAnnotationOnly() { + ProductAnnotationOnly p = new ProductAnnotationOnly(); + assertEquals(10, db.from(p).selectCount()); + + // test IQColumn.name="cat" + assertEquals(2, db.from(p).where(p.category).is("Beverages").selectCount()); + + // test IQTable.annotationsOnly=true + // public String unmappedField is ignored by iciql + try { + db.from(p).where(p.unmappedField).is("unmapped").selectCount(); + assertTrue("this should never execute", false); + } catch (IciqlException e) { + assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode()); + } + + // 10 objects, 10 autoIncremented unique values + assertEquals(10, db.from(p).selectDistinct(p.productName).size()); + + // test IQTable.primaryKey=id + try { + db.insertAll(ProductAnnotationOnly.getList()); + } catch (IciqlException e) { + assertEquals(IciqlException.CODE_DUPLICATE_KEY, e.getIciqlCode()); + } + } + + @Test + public void testProductMixedAnnotation() { + ProductMixedAnnotation p = new ProductMixedAnnotation(); + + // test IQColumn.name="cat" + assertEquals(2, db.from(p).where(p.category).is("Beverages").selectCount()); + + // test IQTable.annotationsOnly=false + // public String mappedField is reflectively mapped by iciql + assertEquals(10, db.from(p).where(p.mappedField).is("mapped").selectCount()); + + // test IQIgnore annotation + assertEquals(null, db.from(p).selectFirst().productDescription); + + // test IQColumn.primaryKey=true + try { + db.insertAll(ProductMixedAnnotation.getList()); + } catch (IciqlException e) { + assertEquals(IciqlException.CODE_DUPLICATE_KEY, e.getIciqlCode()); + } + } + + @Test + public void testTrimStringAnnotation() { + ProductAnnotationOnly p = new ProductAnnotationOnly(); + ProductAnnotationOnly prod = db.from(p).selectFirst(); + String oldValue = prod.category; + String newValue = "01234567890123456789"; + // 2 chars exceeds field max + prod.category = newValue; + db.update(prod); + + ProductAnnotationOnly newProd = db.from(p).where(p.productId).is(prod.productId).selectFirst(); + assertEquals(newValue.substring(0, 15), newProd.category); + + newProd.category = oldValue; + db.update(newProd); + } + + @Test + public void testColumnInheritanceAnnotation() { + ProductInheritedAnnotation table = new ProductInheritedAnnotation(); + List inserted = ProductInheritedAnnotation.getData(); + db.insertAll(inserted); + + List retrieved = db.from(table).select(); + + for (int j = 0; j < retrieved.size(); j++) { + ProductInheritedAnnotation i = inserted.get(j); + ProductInheritedAnnotation r = retrieved.get(j); + assertEquals(i.category, r.category); + assertEquals(i.mappedField, r.mappedField); + assertEquals(i.unitsInStock, r.unitsInStock); + assertEquals(i.unitPrice, r.unitPrice); + assertEquals(i.name(), r.name()); + assertEquals(i.id(), r.id()); + } + } + + @Test + public void testCreateTableIfRequiredAnnotation() { + // tests IQTable.createTableIfRequired=false + try { + db.insertAll(ProductNoCreateTable.getList()); + } catch (IciqlException e) { + assertEquals(IciqlException.CODE_OBJECT_NOT_FOUND, e.getIciqlCode()); + } + } } diff --git a/src/test/java/com/iciql/test/BooleanModelTest.java b/src/test/java/com/iciql/test/BooleanModelTest.java index f5cd5e7..3744204 100644 --- a/src/test/java/com/iciql/test/BooleanModelTest.java +++ b/src/test/java/com/iciql/test/BooleanModelTest.java @@ -16,17 +16,16 @@ package com.iciql.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.junit.Test; - import com.iciql.Db; import com.iciql.test.models.BooleanModel; import com.iciql.test.models.BooleanModel.BooleanAsIntModel; import com.iciql.test.models.BooleanModel.BooleanAsPrimitiveShortModel; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Tests interchangeable mapping of INT columns with Booleans and BOOL columns @@ -39,135 +38,135 @@ import com.iciql.test.models.BooleanModel.BooleanAsPrimitiveShortModel; */ public class BooleanModelTest { - @Test - public void testBooleanColumn() { - Db db = IciqlSuite.openNewDb(); - db.insertAll(BooleanModel.getList()); - BooleanAsIntModel b = new BooleanAsIntModel(); - List models = db.from(b).select(); - int count = 0; - for (BooleanAsIntModel model : models) { - if ((model.id % 2) == 1) { - // assert that odd ids are true - assertTrue(model.mybool > 0); - } else { - // assert that even ids are false - assertTrue(model.mybool == 0); - } - - // count true values - if (model.mybool > 0) { - count++; - } - } - assertEquals(2, count); - - // invert boolean values and update - for (BooleanAsIntModel model : models) { - model.mybool = model.mybool > 0 ? 0 : 1; - } - db.updateAll(models); - - // check even ids are true - models = db.from(b).select(); - for (BooleanAsIntModel model : models) { - if ((model.id % 2) == 1) { - // assert that odd ids are false - assertTrue(model.mybool == 0); - } else { - // assert that even ids are true - assertTrue(model.mybool > 0); - } - } - db.close(); - } - - @Test - public void testIntColumn() { - Db db = IciqlSuite.openNewDb(); - // insert INT column - db.insertAll(BooleanAsIntModel.getList()); - - // select all rows with INT column and map to Boolean - BooleanModel b = new BooleanModel(); - List models = db.from(b).select(); - int count = 0; - for (BooleanModel model : models) { - if ((model.id % 2) == 1) { - // assert that odd ids are true - assertTrue(model.mybool); - } else { - // assert that even ids are false - assertTrue(!model.mybool); - } - - // count true values - if (model.mybool) { - count++; - } - } - assertEquals(2, count); - - // invert boolean values and update - for (BooleanModel model : models) { - model.mybool = !model.mybool; - } - db.updateAll(models); - - // check even ids are true - models = db.from(b).select(); - for (BooleanModel model : models) { - if ((model.id % 2) == 1) { - // assert that odd ids are false - assertTrue(!model.mybool); - } else { - // assert that even ids are true - assertTrue(model.mybool); - } - } - db.close(); - } - - @Test - public void testPrimitiveShortBooleanColumn() { - Db db = IciqlSuite.openNewDb(); - db.insertAll(BooleanModel.getList()); - BooleanAsPrimitiveShortModel b = new BooleanAsPrimitiveShortModel(); - List models = db.from(b).select(); - int count = 0; - for (BooleanAsPrimitiveShortModel model : models) { - if ((model.id % 2) == 1) { - // assert that odd ids are true - assertTrue(model.mybool > 0); - } else { - // assert that even ids are false - assertTrue(model.mybool == 0); - } - - // count true values - if (model.mybool > 0) { - count++; - } - } - assertEquals(2, count); - - // invert boolean values and update - for (BooleanAsPrimitiveShortModel model : models) { - model.mybool = (short) (model.mybool > 0 ? 0 : 1); - } - db.updateAll(models); - - // check even ids are true - models = db.from(b).select(); - for (BooleanAsPrimitiveShortModel model : models) { - if ((model.id % 2) == 1) { - // assert that odd ids are false - assertTrue(model.mybool == 0); - } else { - // assert that even ids are true - assertTrue(model.mybool > 0); - } - } - db.close(); - } + @Test + public void testBooleanColumn() { + Db db = IciqlSuite.openNewDb(); + db.insertAll(BooleanModel.getList()); + BooleanAsIntModel b = new BooleanAsIntModel(); + List models = db.from(b).select(); + int count = 0; + for (BooleanAsIntModel model : models) { + if ((model.id % 2) == 1) { + // assert that odd ids are true + assertTrue(model.mybool > 0); + } else { + // assert that even ids are false + assertTrue(model.mybool == 0); + } + + // count true values + if (model.mybool > 0) { + count++; + } + } + assertEquals(2, count); + + // invert boolean values and update + for (BooleanAsIntModel model : models) { + model.mybool = model.mybool > 0 ? 0 : 1; + } + db.updateAll(models); + + // check even ids are true + models = db.from(b).select(); + for (BooleanAsIntModel model : models) { + if ((model.id % 2) == 1) { + // assert that odd ids are false + assertTrue(model.mybool == 0); + } else { + // assert that even ids are true + assertTrue(model.mybool > 0); + } + } + db.close(); + } + + @Test + public void testIntColumn() { + Db db = IciqlSuite.openNewDb(); + // insert INT column + db.insertAll(BooleanAsIntModel.getList()); + + // select all rows with INT column and map to Boolean + BooleanModel b = new BooleanModel(); + List models = db.from(b).select(); + int count = 0; + for (BooleanModel model : models) { + if ((model.id % 2) == 1) { + // assert that odd ids are true + assertTrue(model.mybool); + } else { + // assert that even ids are false + assertTrue(!model.mybool); + } + + // count true values + if (model.mybool) { + count++; + } + } + assertEquals(2, count); + + // invert boolean values and update + for (BooleanModel model : models) { + model.mybool = !model.mybool; + } + db.updateAll(models); + + // check even ids are true + models = db.from(b).select(); + for (BooleanModel model : models) { + if ((model.id % 2) == 1) { + // assert that odd ids are false + assertTrue(!model.mybool); + } else { + // assert that even ids are true + assertTrue(model.mybool); + } + } + db.close(); + } + + @Test + public void testPrimitiveShortBooleanColumn() { + Db db = IciqlSuite.openNewDb(); + db.insertAll(BooleanModel.getList()); + BooleanAsPrimitiveShortModel b = new BooleanAsPrimitiveShortModel(); + List models = db.from(b).select(); + int count = 0; + for (BooleanAsPrimitiveShortModel model : models) { + if ((model.id % 2) == 1) { + // assert that odd ids are true + assertTrue(model.mybool > 0); + } else { + // assert that even ids are false + assertTrue(model.mybool == 0); + } + + // count true values + if (model.mybool > 0) { + count++; + } + } + assertEquals(2, count); + + // invert boolean values and update + for (BooleanAsPrimitiveShortModel model : models) { + model.mybool = (short) (model.mybool > 0 ? 0 : 1); + } + db.updateAll(models); + + // check even ids are true + models = db.from(b).select(); + for (BooleanAsPrimitiveShortModel model : models) { + if ((model.id % 2) == 1) { + // assert that odd ids are false + assertTrue(model.mybool == 0); + } else { + // assert that even ids are true + assertTrue(model.mybool > 0); + } + } + db.close(); + } } diff --git a/src/test/java/com/iciql/test/ClobTest.java b/src/test/java/com/iciql/test/ClobTest.java index 49cee72..89e21cd 100644 --- a/src/test/java/com/iciql/test/ClobTest.java +++ b/src/test/java/com/iciql/test/ClobTest.java @@ -17,99 +17,98 @@ package com.iciql.test; -import static com.iciql.Define.primaryKey; -import static com.iciql.Define.tableName; -import static org.junit.Assert.assertEquals; +import com.iciql.Db; +import com.iciql.Iciql; +import org.junit.Test; import java.text.MessageFormat; import java.util.Arrays; import java.util.List; -import org.junit.Test; - -import com.iciql.Db; -import com.iciql.Iciql; +import static com.iciql.Define.primaryKey; +import static com.iciql.Define.tableName; +import static org.junit.Assert.assertEquals; /** * Tests if converting a CLOB to a String works. */ public class ClobTest { - @Test - public void testClob() throws Exception { - String create = "CREATE TABLE CLOB_TEST(ID INT PRIMARY KEY, WORDS {0})"; - Db db = IciqlSuite.openNewDb(); - db.executeUpdate(MessageFormat.format(create, "VARCHAR(255)")); - db.insertAll(StringRecord.getList()); - testSimpleUpdate(db, "VARCHAR fail"); - db.executeUpdate("DROP TABLE CLOB_TEST"); - db.close(); - - db = IciqlSuite.openNewDb(); - db.executeUpdate(MessageFormat.format(create, db.getDialect().convertSqlType("CLOB"))); - db.insertAll(StringRecord.getList()); - testSimpleUpdate(db, "CLOB fail because of single quote artifacts"); - db.executeUpdate("DROP TABLE CLOB_TEST"); - db.close(); - } - - private void testSimpleUpdate(Db db, String failureMsg) { - String newWords = "I changed the words"; - StringRecord r = new StringRecord(); - StringRecord originalRecord = db.from(r).where(r.id).is(2).selectFirst(); - String oldWords = originalRecord.words; - originalRecord.words = newWords; - db.update(originalRecord); - - StringRecord r2 = new StringRecord(); - StringRecord revisedRecord = db.from(r2).where(r2.id).is(2).selectFirst(); - assertEquals(failureMsg, newWords, revisedRecord.words); - - // undo update - originalRecord.words = oldWords; - db.update(originalRecord); - } - - /** - * A simple class used in this test. - */ - public static class StringRecord implements Iciql { - - public Integer id; - public String words; - - public StringRecord() { - // public constructor - } - - private StringRecord(int id, String words) { - this.id = id; - this.words = words; - } - - public void defineIQ() { - tableName("CLOB_TEST"); - primaryKey(id); - } - - private static StringRecord create(int id, String words) { - return new StringRecord(id, words); - } - - public static List getList() { - StringRecord[] list = { - create(1, "Once upon a midnight dreary, while I pondered weak and weary,"), - create(2, "Over many a quaint and curious volume of forgotten lore,"), - create(3, "While I nodded, nearly napping, suddenly there came a tapping,"), - create(4, "As of some one gently rapping, rapping at my chamber door."), - create(5, "`'Tis some visitor,' I muttered, `tapping at my chamber door -"), - create(6, "Only this, and nothing more.'") }; - - return Arrays.asList(list); - } - - public String toString() { - return id + ": " + words; - } - } + @Test + public void testClob() throws Exception { + String create = "CREATE TABLE CLOB_TEST(ID INT PRIMARY KEY, WORDS {0})"; + Db db = IciqlSuite.openNewDb(); + db.executeUpdate(MessageFormat.format(create, "VARCHAR(255)")); + db.insertAll(StringRecord.getList()); + testSimpleUpdate(db, "VARCHAR fail"); + db.executeUpdate("DROP TABLE CLOB_TEST"); + db.close(); + + db = IciqlSuite.openNewDb(); + db.executeUpdate(MessageFormat.format(create, db.getDialect().convertSqlType("CLOB"))); + db.insertAll(StringRecord.getList()); + testSimpleUpdate(db, "CLOB fail because of single quote artifacts"); + db.executeUpdate("DROP TABLE CLOB_TEST"); + db.close(); + } + + private void testSimpleUpdate(Db db, String failureMsg) { + String newWords = "I changed the words"; + StringRecord r = new StringRecord(); + StringRecord originalRecord = db.from(r).where(r.id).is(2).selectFirst(); + String oldWords = originalRecord.words; + originalRecord.words = newWords; + db.update(originalRecord); + + StringRecord r2 = new StringRecord(); + StringRecord revisedRecord = db.from(r2).where(r2.id).is(2).selectFirst(); + assertEquals(failureMsg, newWords, revisedRecord.words); + + // undo update + originalRecord.words = oldWords; + db.update(originalRecord); + } + + /** + * A simple class used in this test. + */ + public static class StringRecord implements Iciql { + + public Integer id; + public String words; + + public StringRecord() { + // public constructor + } + + private StringRecord(int id, String words) { + this.id = id; + this.words = words; + } + + public void defineIQ() { + tableName("CLOB_TEST"); + primaryKey(id); + } + + private static StringRecord create(int id, String words) { + return new StringRecord(id, words); + } + + public static List getList() { + StringRecord[] list = { + create(1, "Once upon a midnight dreary, while I pondered weak and weary,"), + create(2, "Over many a quaint and curious volume of forgotten lore,"), + create(3, "While I nodded, nearly napping, suddenly there came a tapping,"), + create(4, "As of some one gently rapping, rapping at my chamber door."), + create(5, "`'Tis some visitor,' I muttered, `tapping at my chamber door -"), + create(6, "Only this, and nothing more.'")}; + + return Arrays.asList(list); + } + + public String toString() { + return id + ": " + words; + } + } } diff --git a/src/test/java/com/iciql/test/ConcurrencyTest.java b/src/test/java/com/iciql/test/ConcurrencyTest.java index e248265..e7297f1 100644 --- a/src/test/java/com/iciql/test/ConcurrencyTest.java +++ b/src/test/java/com/iciql/test/ConcurrencyTest.java @@ -16,184 +16,183 @@ package com.iciql.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - import com.iciql.Db; import com.iciql.IciqlException; import com.iciql.Query; import com.iciql.test.models.Product; import com.iciql.util.Utils; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Tests concurrency and alias instance sharing. */ public class ConcurrencyTest { - private int numberOfTests = 800; - - @Before - public void setUp() { - Db db = IciqlSuite.openNewDb(); - db.insertAll(Product.getList()); - } - - @Test - public void testAliasSharing() throws Exception { - Db db = IciqlSuite.openCurrentDb(); - try { - // Single-threaded example of why aliases can NOT be shared. - Product p = new Product(); - Query query1 = db.from(p); - Query query2 = db.from(p); - - // if you could share alias instances both counts should be equal - long count1 = 0; - try { - count1 = query1.where(p.category).is("Beverages").selectCount(); - } catch (IciqlException e) { - assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode()); - } - long count2 = query2.where(p.category).is("Beverages").selectCount(); - - // but they aren't - assertEquals(0, count1); - assertEquals(2, count2); - assertTrue(count1 != count2); - } finally { - db.close(); - } - } - - @Test - @Ignore - public void testConcurrencyFinal() throws Exception { - // Multi-threaded example of why aliases can NOT be shared. - // - // This test looks like it _could_ work and you may find that it _can_ - // work, but you should also find that it _will_ fail. - - List threads = Utils.newArrayList(); - final AtomicInteger failures = new AtomicInteger(0); - final Product p = new Product(); - for (int i = 0; i < numberOfTests; i++) { - final int testNumber = i; - Thread t = new Thread(new Runnable() { - public void run() { - try { - int testCase = testNumber % 10; - test(testCase, p); - } catch (AssertionError e) { - failures.incrementAndGet(); - } catch (IciqlException e) { - failures.incrementAndGet(); - if (e.getIciqlCode() != IciqlException.CODE_UNMAPPED_FIELD) { - System.err.println("UNEXPECTED ERROR in testConcurrencyFinal()"); - e.printStackTrace(); - } - } - } - }, "ICIQL-" + i); - t.start(); - threads.add(t); - } - - // wait till all threads complete - for (Thread t : threads) { - t.join(); - } - - assertTrue("This should fail. Try running a few more times.", failures.get() > 0); - } - - @Test - @Ignore - public void testConcurrencyThreadLocal() throws Exception { - List threads = Utils.newArrayList(); - final AtomicInteger failures = new AtomicInteger(0); - final ThreadLocal tl = Utils.newThreadLocal(Product.class); - for (int i = 0; i < numberOfTests; i++) { - final int testNumber = i; - Thread t = new Thread(new Runnable() { - public void run() { - try { - int testCase = testNumber % 10; - test(testCase, tl.get()); - } catch (AssertionError e) { - failures.incrementAndGet(); - } catch (IciqlException e) { - failures.incrementAndGet(); - if (e.getIciqlCode() != IciqlException.CODE_UNMAPPED_FIELD) { - System.err.println("UNEXPECTED ERROR in testConcurrencyThreadLocal()"); - e.printStackTrace(); - } - } - } - }, "ICIQL-" + i); - t.start(); - threads.add(t); - } - - // wait till all threads complete - for (Thread t : threads) { - t.join(); - } - - assertEquals("ThreadLocal should never fail!", 0, failures.get()); - } - - private void test(int testCase, Product p) throws AssertionError { - Db db = IciqlSuite.openCurrentDb(); - try { - List list; - switch (testCase) { - case 0: - list = db.from(p).where(p.productName).is("Chai").select(); - assertEquals(1, list.size()); - assertEquals("Chai", list.get(0).productName); - break; - case 1: - list = db.from(p).where(p.category).is("Condiments").select(); - assertEquals(5, list.size()); - break; - case 3: - list = db.from(p).where(p.productName).is("Aniseed Syrup").select(); - assertEquals(1, list.size()); - assertEquals("Aniseed Syrup", list.get(0).productName); - break; - case 4: - list = db.from(p).where(p.productName).like("Chef%").select(); - assertEquals(2, list.size()); - assertTrue(list.get(0).productName.startsWith("Chef")); - assertTrue(list.get(1).productName.startsWith("Chef")); - break; - case 6: - list = db.from(p).where(p.unitsInStock).exceeds(0).select(); - assertEquals(9, list.size()); - break; - case 7: - list = db.from(p).where(p.unitsInStock).is(0).select(); - assertEquals(1, list.size()); - assertEquals("Chef Anton's Gumbo Mix", list.get(0).productName); - break; - case 9: - list = db.from(p).where(p.productId).is(7).select(); - assertEquals(1, list.size()); - assertTrue(7 == list.get(0).productId); - break; - default: - list = db.from(p).select(); - assertEquals(10, list.size()); - } - } finally { - db.close(); - } - } + private int numberOfTests = 800; + + @Before + public void setUp() { + Db db = IciqlSuite.openNewDb(); + db.insertAll(Product.getList()); + } + + @Test + public void testAliasSharing() throws Exception { + Db db = IciqlSuite.openCurrentDb(); + try { + // Single-threaded example of why aliases can NOT be shared. + Product p = new Product(); + Query query1 = db.from(p); + Query query2 = db.from(p); + + // if you could share alias instances both counts should be equal + long count1 = 0; + try { + count1 = query1.where(p.category).is("Beverages").selectCount(); + } catch (IciqlException e) { + assertEquals(IciqlException.CODE_UNMAPPED_FIELD, e.getIciqlCode()); + } + long count2 = query2.where(p.category).is("Beverages").selectCount(); + + // but they aren't + assertEquals(0, count1); + assertEquals(2, count2); + assertTrue(count1 != count2); + } finally { + db.close(); + } + } + + @Test + @Ignore + public void testConcurrencyFinal() throws Exception { + // Multi-threaded example of why aliases can NOT be shared. + // + // This test looks like it _could_ work and you may find that it _can_ + // work, but you should also find that it _will_ fail. + + List threads = Utils.newArrayList(); + final AtomicInteger failures = new AtomicInteger(0); + final Product p = new Product(); + for (int i = 0; i < numberOfTests; i++) { + final int testNumber = i; + Thread t = new Thread(new Runnable() { + public void run() { + try { + int testCase = testNumber % 10; + test(testCase, p); + } catch (AssertionError e) { + failures.incrementAndGet(); + } catch (IciqlException e) { + failures.incrementAndGet(); + if (e.getIciqlCode() != IciqlException.CODE_UNMAPPED_FIELD) { + System.err.println("UNEXPECTED ERROR in testConcurrencyFinal()"); + e.printStackTrace(); + } + } + } + }, "ICIQL-" + i); + t.start(); + threads.add(t); + } + + // wait till all threads complete + for (Thread t : threads) { + t.join(); + } + + assertTrue("This should fail. Try running a few more times.", failures.get() > 0); + } + + @Test + @Ignore + public void testConcurrencyThreadLocal() throws Exception { + List threads = Utils.newArrayList(); + final AtomicInteger failures = new AtomicInteger(0); + final ThreadLocal tl = Utils.newThreadLocal(Product.class); + for (int i = 0; i < numberOfTests; i++) { + final int testNumber = i; + Thread t = new Thread(new Runnable() { + public void run() { + try { + int testCase = testNumber % 10; + test(testCase, tl.get()); + } catch (AssertionError e) { + failures.incrementAndGet(); + } catch (IciqlException e) { + failures.incrementAndGet(); + if (e.getIciqlCode() != IciqlException.CODE_UNMAPPED_FIELD) { + System.err.println("UNEXPECTED ERROR in testConcurrencyThreadLocal()"); + e.printStackTrace(); + } + } + } + }, "ICIQL-" + i); + t.start(); + threads.add(t); + } + + // wait till all threads complete + for (Thread t : threads) { + t.join(); + } + + assertEquals("ThreadLocal should never fail!", 0, failures.get()); + } + + private void test(int testCase, Product p) throws AssertionError { + Db db = IciqlSuite.openCurrentDb(); + try { + List list; + switch (testCase) { + case 0: + list = db.from(p).where(p.productName).is("Chai").select(); + assertEquals(1, list.size()); + assertEquals("Chai", list.get(0).productName); + break; + case 1: + list = db.from(p).where(p.category).is("Condiments").select(); + assertEquals(5, list.size()); + break; + case 3: + list = db.from(p).where(p.productName).is("Aniseed Syrup").select(); + assertEquals(1, list.size()); + assertEquals("Aniseed Syrup", list.get(0).productName); + break; + case 4: + list = db.from(p).where(p.productName).like("Chef%").select(); + assertEquals(2, list.size()); + assertTrue(list.get(0).productName.startsWith("Chef")); + assertTrue(list.get(1).productName.startsWith("Chef")); + break; + case 6: + list = db.from(p).where(p.unitsInStock).exceeds(0).select(); + assertEquals(9, list.size()); + break; + case 7: + list = db.from(p).where(p.unitsInStock).is(0).select(); + assertEquals(1, list.size()); + assertEquals("Chef Anton's Gumbo Mix", list.get(0).productName); + break; + case 9: + list = db.from(p).where(p.productId).is(7).select(); + assertEquals(1, list.size()); + assertTrue(7 == list.get(0).productId); + break; + default: + list = db.from(p).select(); + assertEquals(10, list.size()); + } + } finally { + db.close(); + } + } } diff --git a/src/test/java/com/iciql/test/DataTypeAdapterTest.java b/src/test/java/com/iciql/test/DataTypeAdapterTest.java index 4d2350c..72de794 100644 --- a/src/test/java/com/iciql/test/DataTypeAdapterTest.java +++ b/src/test/java/com/iciql/test/DataTypeAdapterTest.java @@ -16,23 +16,22 @@ package com.iciql.test; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.util.Date; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - import com.iciql.Db; import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQTable; import com.iciql.Iciql.TypeAdapter; import com.iciql.adapter.JavaSerializationTypeAdapter; import com.iciql.test.models.SupportedTypes; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Date; /** * Tests insertion and retrieval of a custom data type that is automatically transformed @@ -40,66 +39,66 @@ import com.iciql.test.models.SupportedTypes; */ public class DataTypeAdapterTest extends Assert { - private Db db; + private Db db; - @Before - public void setUp() { - db = IciqlSuite.openNewDb(); - } + @Before + public void setUp() { + db = IciqlSuite.openNewDb(); + } - @After - public void tearDown() { - db.close(); - } + @After + public void tearDown() { + db.close(); + } - @Test - public void testSerializedObjectDataType() { + @Test + public void testSerializedObjectDataType() { - SerializedObjectTypeAdapterTest row = new SerializedObjectTypeAdapterTest(); - row.received = new Date(); - row.obj = SupportedTypes.createList().get(1); - db.insert(row); + SerializedObjectTypeAdapterTest row = new SerializedObjectTypeAdapterTest(); + row.received = new Date(); + row.obj = SupportedTypes.createList().get(1); + db.insert(row); - SerializedObjectTypeAdapterTest table = new SerializedObjectTypeAdapterTest(); - SerializedObjectTypeAdapterTest q1 = db.from(table).selectFirst(); + SerializedObjectTypeAdapterTest table = new SerializedObjectTypeAdapterTest(); + SerializedObjectTypeAdapterTest q1 = db.from(table).selectFirst(); - assertNotNull(q1); - assertTrue(row.obj.equivalentTo(q1.obj)); + assertNotNull(q1); + assertTrue(row.obj.equivalentTo(q1.obj)); - } + } - @IQTable(name="dataTypeAdapters") - public static class SerializedObjectTypeAdapterTest { + @IQTable(name = "dataTypeAdapters") + public static class SerializedObjectTypeAdapterTest { - @IQColumn(autoIncrement = true, primaryKey = true) - public long id; + @IQColumn(autoIncrement = true, primaryKey = true) + public long id; - @IQColumn - public java.util.Date received; + @IQColumn + public java.util.Date received; - @IQColumn - @SupportedTypesAdapter - public SupportedTypes obj; + @IQColumn + @SupportedTypesAdapter + public SupportedTypes obj; - } + } - /** - * Maps a SupportedType instance to a BLOB using Java Object serialization. - * - */ - public static class SupportedTypesAdapterImpl extends JavaSerializationTypeAdapter { + /** + * Maps a SupportedType instance to a BLOB using Java Object serialization. + */ + public static class SupportedTypesAdapterImpl extends JavaSerializationTypeAdapter { - @Override - public Class getJavaType() { - return SupportedTypes.class; - } + @Override + public Class getJavaType() { + return SupportedTypes.class; + } - } + } - @Retention(RetentionPolicy.RUNTIME) - @Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) - @TypeAdapter(SupportedTypesAdapterImpl.class) - public @interface SupportedTypesAdapter { } + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) + @TypeAdapter(SupportedTypesAdapterImpl.class) + public @interface SupportedTypesAdapter { + } } diff --git a/src/test/java/com/iciql/test/DefaultValuesTest.java b/src/test/java/com/iciql/test/DefaultValuesTest.java index 1374379..e1b75a3 100644 --- a/src/test/java/com/iciql/test/DefaultValuesTest.java +++ b/src/test/java/com/iciql/test/DefaultValuesTest.java @@ -16,46 +16,45 @@ package com.iciql.test; -import static org.junit.Assert.assertTrue; - -import java.util.List; - -import org.junit.Test; - import com.iciql.Db; import com.iciql.DbInspector; import com.iciql.ValidationRemark; import com.iciql.test.models.DefaultValuesModel; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertTrue; /** * Tests default object values. */ public class DefaultValuesTest { - @Test - public void testDefaultObjectValues() { - Db db = IciqlSuite.openNewDb(); - - // insert random model - DefaultValuesModel model = new DefaultValuesModel(); - db.insert(model); - - DefaultValuesModel v = new DefaultValuesModel(); - - // retrieve model and compare - DefaultValuesModel retrievedModel = db.from(v).selectFirst(); - assertTrue(model.myInteger.equals(retrievedModel.myInteger)); - assertTrue(model.myDate.equals(retrievedModel.myDate)); - assertTrue(model.myEnumIdTree.equals(retrievedModel.myEnumIdTree)); - assertTrue(model.myNameTree.equals(retrievedModel.myNameTree)); - assertTrue(model.myOrdinalTree.equals(retrievedModel.myOrdinalTree)); - assertTrue(retrievedModel.myNullTree == null); - - DbInspector inspector = new DbInspector(db); - List remarks = inspector.validateModel(model, false); - db.close(); - for (ValidationRemark remark : remarks) { - System.out.println(remark.toString()); - } - } + @Test + public void testDefaultObjectValues() { + Db db = IciqlSuite.openNewDb(); + + // insert random model + DefaultValuesModel model = new DefaultValuesModel(); + db.insert(model); + + DefaultValuesModel v = new DefaultValuesModel(); + + // retrieve model and compare + DefaultValuesModel retrievedModel = db.from(v).selectFirst(); + assertTrue(model.myInteger.equals(retrievedModel.myInteger)); + assertTrue(model.myDate.equals(retrievedModel.myDate)); + assertTrue(model.myEnumIdTree.equals(retrievedModel.myEnumIdTree)); + assertTrue(model.myNameTree.equals(retrievedModel.myNameTree)); + assertTrue(model.myOrdinalTree.equals(retrievedModel.myOrdinalTree)); + assertTrue(retrievedModel.myNullTree == null); + + DbInspector inspector = new DbInspector(db); + List remarks = inspector.validateModel(model, false); + db.close(); + for (ValidationRemark remark : remarks) { + System.out.println(remark.toString()); + } + } } diff --git a/src/test/java/com/iciql/test/EnumsTest.java b/src/test/java/com/iciql/test/EnumsTest.java index 8e1f90e..e0f307d 100644 --- a/src/test/java/com/iciql/test/EnumsTest.java +++ b/src/test/java/com/iciql/test/EnumsTest.java @@ -16,15 +16,6 @@ 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.IciqlException; import com.iciql.test.models.EnumModels; @@ -33,123 +24,131 @@ import com.iciql.test.models.EnumModels.EnumOrdinalModel; import com.iciql.test.models.EnumModels.EnumStringModel; import com.iciql.test.models.EnumModels.Genus; import com.iciql.test.models.EnumModels.Tree; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Tests enum support. */ public class EnumsTest { - private Db db; - - @Before - public void setUp() { - db = IciqlSuite.openNewDb(); - db.insertAll(EnumIdModel.createList()); - db.insertAll(EnumOrdinalModel.createList()); - db.insertAll(EnumStringModel.createList()); - } - - @After - public void tearDown() { - db.close(); - } - - @Test - public void testEnumQueries() { - testIntEnums(new EnumIdModel()); - testIntEnums(new EnumOrdinalModel()); - testStringEnums(new EnumStringModel()); - testStringEnumIds(new EnumStringModel()); - } - - private void testIntEnums(EnumModels e) { - // ensure all records inserted - long count = db.from(e).selectCount(); - assertEquals(5, count); - - // special case: - // value is first enum constant which is also the alias object. - // the first enum constant is used as the alias because we can not - // instantiate an enum reflectively. - EnumModels firstEnumValue = db.from(e).where(e.tree()).is(Tree.PINE).selectFirst(); - assertEquals(Tree.PINE, firstEnumValue.tree()); - - EnumModels model = db.from(e).where(e.tree()).is(Tree.WALNUT).selectFirst(); - - assertEquals(400, model.id.intValue()); - assertEquals(Tree.WALNUT, model.tree()); - - List list = db.from(e).where(e.tree()).atLeast(Tree.BIRCH).select(); - assertEquals(3, list.size()); - - // between is an int compare - list = db.from(e).where(e.tree()).between(Tree.BIRCH).and(Tree.WALNUT).select(); - assertEquals(2, list.size()); - - } - - private void testStringEnums(EnumModels e) { - // ensure all records inserted - long count = db.from(e).selectCount(); - assertEquals(5, count); - - // special case: - // value is first enum constant which is also the alias object. - // the first enum constant is used as the alias because we can not - // instantiate an enum reflectively. - EnumModels firstEnumValue = db.from(e).where(e.tree()).is(Tree.PINE).selectFirst(); - assertEquals(Tree.PINE, firstEnumValue.tree()); - - EnumModels model = db.from(e).where(e.tree()).is(Tree.WALNUT).selectFirst(); - - assertEquals(400, model.id.intValue()); - assertEquals(Tree.WALNUT, model.tree()); - - List list = db.from(e).where(e.tree()).isNot(Tree.BIRCH).select(); - assertEquals(count - 1, list.size()); - - // between is a string compare - list = db.from(e).where(e.tree()).between(Tree.MAPLE).and(Tree.PINE).select(); - assertEquals(3, list.size()); - } - - private void testStringEnumIds(EnumModels e) { - // ensure all records inserted - long count = db.from(e).selectCount(); - assertEquals(5, count); - - // special case: - // value is first enum constant which is also the alias object. - // the first enum constant is used as the alias because we can not - // instantiate an enum reflectively. - EnumModels firstEnumValue = db.from(e).where(e.genus()).is(Genus.PINUS).selectFirst(); - assertEquals(Tree.PINE, firstEnumValue.tree()); - assertEquals(Genus.PINUS, firstEnumValue.genus()); - - EnumModels model = db.from(e).where(e.genus()).is(Genus.JUGLANS).selectFirst(); - - assertEquals(400, model.id.intValue()); - assertEquals(Tree.WALNUT, model.tree()); - assertEquals(Genus.JUGLANS, model.genus()); - - List list = db.from(e).where(e.genus()).isNot(Genus.BETULA).select(); - assertEquals(count - 1, list.size()); - - } - - @Test - public void testMultipleEnumInstances() { - BadEnums b = new BadEnums(); - try { - db.from(b).where(b.tree1).is(Tree.BIRCH).and (b.tree2).is(Tree.MAPLE).getSQL(); - assertTrue("Failed to detect multiple Tree fields?!", false); - } catch (IciqlException e) { - assertTrue(e.getMessage(), e.getMessage().startsWith("Can not explicitly reference Tree")); - } - } - - public static class BadEnums { - Tree tree1 = Tree.BIRCH; - Tree tree2 = Tree.MAPLE; - } + private Db db; + + @Before + public void setUp() { + db = IciqlSuite.openNewDb(); + db.insertAll(EnumIdModel.createList()); + db.insertAll(EnumOrdinalModel.createList()); + db.insertAll(EnumStringModel.createList()); + } + + @After + public void tearDown() { + db.close(); + } + + @Test + public void testEnumQueries() { + testIntEnums(new EnumIdModel()); + testIntEnums(new EnumOrdinalModel()); + testStringEnums(new EnumStringModel()); + testStringEnumIds(new EnumStringModel()); + } + + private void testIntEnums(EnumModels e) { + // ensure all records inserted + long count = db.from(e).selectCount(); + assertEquals(5, count); + + // special case: + // value is first enum constant which is also the alias object. + // the first enum constant is used as the alias because we can not + // instantiate an enum reflectively. + EnumModels firstEnumValue = db.from(e).where(e.tree()).is(Tree.PINE).selectFirst(); + assertEquals(Tree.PINE, firstEnumValue.tree()); + + EnumModels model = db.from(e).where(e.tree()).is(Tree.WALNUT).selectFirst(); + + assertEquals(400, model.id.intValue()); + assertEquals(Tree.WALNUT, model.tree()); + + List list = db.from(e).where(e.tree()).atLeast(Tree.BIRCH).select(); + assertEquals(3, list.size()); + + // between is an int compare + list = db.from(e).where(e.tree()).between(Tree.BIRCH).and(Tree.WALNUT).select(); + assertEquals(2, list.size()); + + } + + private void testStringEnums(EnumModels e) { + // ensure all records inserted + long count = db.from(e).selectCount(); + assertEquals(5, count); + + // special case: + // value is first enum constant which is also the alias object. + // the first enum constant is used as the alias because we can not + // instantiate an enum reflectively. + EnumModels firstEnumValue = db.from(e).where(e.tree()).is(Tree.PINE).selectFirst(); + assertEquals(Tree.PINE, firstEnumValue.tree()); + + EnumModels model = db.from(e).where(e.tree()).is(Tree.WALNUT).selectFirst(); + + assertEquals(400, model.id.intValue()); + assertEquals(Tree.WALNUT, model.tree()); + + List list = db.from(e).where(e.tree()).isNot(Tree.BIRCH).select(); + assertEquals(count - 1, list.size()); + + // between is a string compare + list = db.from(e).where(e.tree()).between(Tree.MAPLE).and(Tree.PINE).select(); + assertEquals(3, list.size()); + } + + private void testStringEnumIds(EnumModels e) { + // ensure all records inserted + long count = db.from(e).selectCount(); + assertEquals(5, count); + + // special case: + // value is first enum constant which is also the alias object. + // the first enum constant is used as the alias because we can not + // instantiate an enum reflectively. + EnumModels firstEnumValue = db.from(e).where(e.genus()).is(Genus.PINUS).selectFirst(); + assertEquals(Tree.PINE, firstEnumValue.tree()); + assertEquals(Genus.PINUS, firstEnumValue.genus()); + + EnumModels model = db.from(e).where(e.genus()).is(Genus.JUGLANS).selectFirst(); + + assertEquals(400, model.id.intValue()); + assertEquals(Tree.WALNUT, model.tree()); + assertEquals(Genus.JUGLANS, model.genus()); + + List list = db.from(e).where(e.genus()).isNot(Genus.BETULA).select(); + assertEquals(count - 1, list.size()); + + } + + @Test + public void testMultipleEnumInstances() { + BadEnums b = new BadEnums(); + try { + db.from(b).where(b.tree1).is(Tree.BIRCH).and(b.tree2).is(Tree.MAPLE).getSQL(); + assertTrue("Failed to detect multiple Tree fields?!", false); + } catch (IciqlException e) { + assertTrue(e.getMessage(), e.getMessage().startsWith("Can not explicitly reference Tree")); + } + } + + public static class BadEnums { + Tree tree1 = Tree.BIRCH; + Tree tree2 = Tree.MAPLE; + } } diff --git a/src/test/java/com/iciql/test/ForeignKeyTest.java b/src/test/java/com/iciql/test/ForeignKeyTest.java index 12d2a07..e1c8ffa 100644 --- a/src/test/java/com/iciql/test/ForeignKeyTest.java +++ b/src/test/java/com/iciql/test/ForeignKeyTest.java @@ -16,68 +16,67 @@ */ package com.iciql.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - +import com.iciql.Db; +import com.iciql.IciqlException; +import com.iciql.test.models.CategoryAnnotationOnly; +import com.iciql.test.models.ProductAnnotationOnlyWithForeignKey; import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; -import com.iciql.Db; -import com.iciql.IciqlException; -import com.iciql.test.models.CategoryAnnotationOnly; -import com.iciql.test.models.ProductAnnotationOnlyWithForeignKey; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Tests of Foreign Keys. */ public class ForeignKeyTest { - /** - * This object represents a database (actually a connection to the - * database). - */ + /** + * This object represents a database (actually a connection to the + * database). + */ + + private Db db; + + @Before + public void setUp() { + db = IciqlSuite.openNewDb(); + db.insertAll(CategoryAnnotationOnly.getList()); + db.insertAll(ProductAnnotationOnlyWithForeignKey.getList()); + } + + @After + public void tearDown() { + db.dropTable(ProductAnnotationOnlyWithForeignKey.class); + db.dropTable(CategoryAnnotationOnly.class); + db.close(); + } + + @Test + public void testForeignKeyWithOnDeleteCascade() { + ProductAnnotationOnlyWithForeignKey p = new ProductAnnotationOnlyWithForeignKey(); + long count1 = db.from(p).selectCount(); - private Db db; + // should remove 2 associated products + CategoryAnnotationOnly c = new CategoryAnnotationOnly(); + db.from(c).where(c.categoryId).is(1L).delete(); - @Before - public void setUp() { - db = IciqlSuite.openNewDb(); - db.insertAll(CategoryAnnotationOnly.getList()); - db.insertAll(ProductAnnotationOnlyWithForeignKey.getList()); - } + long count2 = db.from(p).selectCount(); - @After - public void tearDown() { - db.dropTable(ProductAnnotationOnlyWithForeignKey.class); - db.dropTable(CategoryAnnotationOnly.class); - db.close(); - } + assertEquals(count1, count2 + 2L); + } - @Test - public void testForeignKeyWithOnDeleteCascade() { - ProductAnnotationOnlyWithForeignKey p = new ProductAnnotationOnlyWithForeignKey(); - long count1 = db.from(p).selectCount(); - - // should remove 2 associated products - CategoryAnnotationOnly c = new CategoryAnnotationOnly(); - db.from(c).where(c.categoryId).is(1L).delete(); - - long count2 = db.from(p).selectCount(); - - assertEquals(count1, count2 + 2L); - } - - @Test - @Ignore - public void testForeignKeyDropReferenceTable() { - try { - db.dropTable(CategoryAnnotationOnly.class); - assertTrue("Should not be able to drop reference table!", false); - } catch (IciqlException e) { - assertEquals(e.getMessage(), IciqlException.CODE_CONSTRAINT_VIOLATION, e.getIciqlCode()); - } - } + @Test + @Ignore + public void testForeignKeyDropReferenceTable() { + try { + db.dropTable(CategoryAnnotationOnly.class); + assertTrue("Should not be able to drop reference table!", false); + } catch (IciqlException e) { + assertEquals(e.getMessage(), IciqlException.CODE_CONSTRAINT_VIOLATION, e.getIciqlCode()); + } + } } diff --git a/src/test/java/com/iciql/test/IciqlSuite.java b/src/test/java/com/iciql/test/IciqlSuite.java index 37c534c..df5e4d6 100644 --- a/src/test/java/com/iciql/test/IciqlSuite.java +++ b/src/test/java/com/iciql/test/IciqlSuite.java @@ -16,33 +16,6 @@ */ package com.iciql.test; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.PrintStream; -import java.sql.SQLException; -import java.text.DecimalFormat; -import java.text.MessageFormat; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.apache.commons.dbcp.ConnectionFactory; -import org.apache.commons.dbcp.DriverManagerConnectionFactory; -import org.apache.commons.dbcp.PoolableConnectionFactory; -import org.apache.commons.dbcp.PoolingDataSource; -import org.apache.commons.pool.impl.GenericObjectPool; -import org.apache.derby.drda.NetworkServerControl; -import org.hsqldb.persist.HsqlProperties; -import org.junit.Assert; -import org.junit.runner.JUnitCore; -import org.junit.runner.Result; -import org.junit.runner.RunWith; -import org.junit.runner.notification.Failure; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.beust.jcommander.ParameterException; @@ -77,10 +50,36 @@ import com.iciql.util.IciqlLogger.IciqlListener; import com.iciql.util.IciqlLogger.StatementType; import com.iciql.util.StringUtils; import com.iciql.util.Utils; +import org.apache.commons.dbcp.ConnectionFactory; +import org.apache.commons.dbcp.DriverManagerConnectionFactory; +import org.apache.commons.dbcp.PoolableConnectionFactory; +import org.apache.commons.dbcp.PoolingDataSource; +import org.apache.commons.pool.impl.GenericObjectPool; +import org.apache.derby.drda.NetworkServerControl; +import org.hsqldb.persist.HsqlProperties; +import org.junit.Assert; +import org.junit.runner.JUnitCore; +import org.junit.runner.Result; +import org.junit.runner.RunWith; +import org.junit.runner.notification.Failure; +import org.junit.runners.Suite; +import org.junit.runners.Suite.SuiteClasses; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintStream; +import java.sql.SQLException; +import java.text.DecimalFormat; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; /** * JUnit 4 iciql test suite. - * + *

* By default this test suite will run against the H2 database. You can change * this by switching the DEFAULT_TEST_DB value. *

@@ -90,614 +89,613 @@ import com.iciql.util.Utils; * NOTE: If you want to test against MySQL or PostgreSQL you must create an * "iciql" database and allow user "sa" password "sa" complete control of that * database. - * */ @RunWith(Suite.class) -@SuiteClasses({ AliasMapTest.class, AnnotationsTest.class, BooleanModelTest.class, ClobTest.class, - ConcurrencyTest.class, EnumsTest.class, ModelsTest.class, PrimitivesTest.class, OneOfTest.class, - RuntimeQueryTest.class, SamplesTest.class, UpdateTest.class, UpgradesTest.class, JoinTest.class, - UUIDTest.class, ViewsTest.class, ForeignKeyTest.class, TransactionTest.class, NestedConditionsTest.class, - DataTypeAdapterTest.class, ProductDaoTest.class }) +@SuiteClasses({AliasMapTest.class, AnnotationsTest.class, BooleanModelTest.class, ClobTest.class, + ConcurrencyTest.class, EnumsTest.class, ModelsTest.class, PrimitivesTest.class, OneOfTest.class, + RuntimeQueryTest.class, SamplesTest.class, UpdateTest.class, UpgradesTest.class, JoinTest.class, + UUIDTest.class, ViewsTest.class, ForeignKeyTest.class, TransactionTest.class, NestedConditionsTest.class, + DataTypeAdapterTest.class, ProductDaoTest.class}) public class IciqlSuite { - private final static File baseFolder = new File(System.getProperty("user.dir"), "/testdbs"); - private static final TestDb[] TEST_DBS = { - new TestDb("H2", "memory", "jdbc:h2:mem:iciql"), - new TestDb("H2", "file", "jdbc:h2:file:" - + new File(baseFolder, "/h2/iciql").getAbsolutePath()), - new TestDb("H2", "tcp", "jdbc:h2:tcp://localhost/" - + new File(baseFolder, "/h2tcp/iciql").getAbsolutePath()), - new TestDb("HSQL", "memory", "jdbc:hsqldb:mem:iciql"), - new TestDb("HSQL", "file", "jdbc:hsqldb:file:testdbs/hsql/iciql"), - new TestDb("HSQL", "tcp", "jdbc:hsqldb:hsql://localhost/iciql"), - new TestDb("Derby", "memory", "jdbc:derby:memory:iciql;create=true"), - new TestDb("Derby", "file", "jdbc:derby:directory:testdbs/derby/iciql;create=true"), - new TestDb("Derby", "tcp", "jdbc:derby://localhost:1527/testdbs/derby/iciql;create=true", "sa", "sa"), - new TestDb("MySQL", "tcp", "jdbc:mysql://localhost:3306/iciql", "sa", "sa"), - new TestDb("PostgreSQL", "tcp", "jdbc:postgresql://localhost:5432/iciql", "sa", "sa"), - - // - // SQLite Memory - // - new TestDb("SQLite", "memory", "jdbc:sqlite:file::memory:?cache=shared&foreign_keys=ON"), - - // - // SQLite DELETE rollback journal (default) - // - new TestDb("SQLite", "delete,full_sync", "jdbc:sqlite:" - + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() - + "?foreign_keys=ON&journal_mode=DELETE&synchronous=FULL"), - - new TestDb("SQLite", "delete,norm_sync", "jdbc:sqlite:" - + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() - + "?foreign_keys=ON&journal_mode=DELETE&synchronous=NORMAL"), - - new TestDb("SQLite", "delete,no_sync", "jdbc:sqlite:" - + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() - + "?foreign_keys=ON&journal_mode=DELETE&synchronous=OFF"), - - // - // SQLite WAL - // - new TestDb("SQLite", "wal,full_sync", "jdbc:sqlite:" - + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() - + "?foreign_keys=ON&journal_mode=WAL&synchronous=FULL"), - - new TestDb("SQLite", "wal,norm_sync", "jdbc:sqlite:" - + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() - + "?foreign_keys=ON&journal_mode=WAL&synchronous=NORMAL"), - - new TestDb("SQLite", "wal,no_sync", "jdbc:sqlite:" - + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() - + "?foreign_keys=ON&journal_mode=WAL&synchronous=OFF"), - - }; - - private static final TestDb DEFAULT_TEST_DB = TEST_DBS[3]; - - private static final PrintStream ERR = System.err; - - private static PrintStream out = System.out; - - private static Map connectionFactories = Utils - .newSynchronizedHashMap(); - - private static Map dataSources = Utils.newSynchronizedHashMap(); - - public static void assertStartsWith(String value, String startsWith) { - Assert.assertTrue(MessageFormat.format("Expected \"{0}\", got: \"{1}\"", startsWith, value), - value.startsWith(startsWith)); - } - - public static void assertEqualsIgnoreCase(String expected, String actual) { - Assert.assertTrue(MessageFormat.format("Expected \"{0}\", got: \"{1}\"", expected, actual), - expected.equalsIgnoreCase(actual)); - } - - public static boolean equivalentTo(double expected, double actual) { - if (Double.compare(expected, actual) == 0) { - return true; - } - return Math.abs(expected - actual) <= 0.000001d; - } - - public static Db openNewDb() { - return openNewDb(Mode.PROD); - } - - /** - * Open a new Db object. All connections are cached and re-used to eliminate - * embedded database startup costs. - * - * @param mode - * @return a fresh Db object - */ - public static Db openNewDb(Mode mode) { - String testUrl = System.getProperty("iciql.url", DEFAULT_TEST_DB.url); - String testUser = System.getProperty("iciql.user", DEFAULT_TEST_DB.username); - String testPassword = System.getProperty("iciql.password", DEFAULT_TEST_DB.password); - - Db db = null; - PoolingDataSource dataSource = dataSources.get(testUrl); - if (dataSource == null) { - ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(testUrl, testUser, - testPassword); - GenericObjectPool pool = new GenericObjectPool(); - pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW); - PoolableConnectionFactory factory = new PoolableConnectionFactory(connectionFactory, pool, null, - null, false, true); - dataSource = new PoolingDataSource(pool); - dataSources.put(testUrl, dataSource); - connectionFactories.put(testUrl, factory); - } - db = Db.open(dataSource, mode); - - // drop views - db.dropView(ProductView.class); - db.dropView(ProductViewInherited.class); - db.dropView(ProductViewFromQuery.class); - db.dropView(ProductViewInheritedComplex.class); - - // drop tables - db.dropTable(BooleanModel.class); - db.dropTable(ComplexObject.class); - db.dropTable(Customer.class); - db.dropTable(DefaultValuesModel.class); - db.dropTable(EnumIdModel.class); - db.dropTable(EnumOrdinalModel.class); - db.dropTable(EnumStringModel.class); - db.dropTable(Order.class); - db.dropTable(PrimitivesModel.class); - db.dropTable(Product.class); - db.dropTable(ProductAnnotationOnly.class); - db.dropTable(ProductInheritedAnnotation.class); - db.dropTable(ProductMixedAnnotation.class); - db.dropTable(SupportedTypes.class); - db.dropTable(JoinTest.UserId.class); - db.dropTable(JoinTest.UserNote.class); - db.dropTable(EnumsTest.BadEnums.class); - db.dropTable(MultipleBoolsModel.class); - db.dropTable(ProductAnnotationOnlyWithForeignKey.class); - db.dropTable(CategoryAnnotationOnly.class); - db.dropTable(SerializedObjectTypeAdapterTest.class); - - return db; - } - - /** - * Open the current database. - * - * @return the current database - */ - public static Db openCurrentDb() { - String testUrl = System.getProperty("iciql.url", DEFAULT_TEST_DB.url); - String testUser = System.getProperty("iciql.user", DEFAULT_TEST_DB.username); - String testPassword = System.getProperty("iciql.password", DEFAULT_TEST_DB.password); - return Db.open(testUrl, testUser, testPassword); - } - - /** - * Returns the name of the underlying database engine for the Db object. - * - * @param db - * @return the database engine name - */ - public static String getDatabaseEngineName(Db db) { - String database = ""; - try { - database = db.getConnection().getMetaData().getDatabaseProductName(); - } catch (SQLException s) { - } - return database; - } - - /** - * Returns true if the underlying database engine is Derby. - * - * @param db - * @return true if underlying database engine is Derby - */ - public static boolean isDerby(Db db) { - return IciqlSuite.getDatabaseEngineName(db).equals("Apache Derby"); - } - - /** - * Returns true if the underlying database engine is H2. - * - * @param db - * @return true if underlying database engine is H2 - */ - public static boolean isH2(Db db) { - return IciqlSuite.getDatabaseEngineName(db).equals("H2"); - } - - /** - * Returns true if the underlying database engine is MySQL. - * - * @param db - * @return true if underlying database engine is MySQL - */ - public static boolean isMySQL(Db db) { - return IciqlSuite.getDatabaseEngineName(db).equals("MySQL"); - } - - /** - * Returns true if the underlying database engine is SQLite. - * - * @param db - * @return true if underlying database engine is SQLite - */ - public static boolean isSQLite(Db db) { - return IciqlSuite.getDatabaseEngineName(db).equals("SQLite"); - } - - /** - * Gets the default schema of the underlying database engine. - * - * @param db - * @return the default schema - */ - public static String getDefaultSchema(Db db) { - if (isDerby(db)) { - // Derby sets default schema name to username - return "SA"; - } else if (isMySQL(db)) { - // MySQL does not have schemas - return null; - } else if (isSQLite(db)) { - // SQLite does not have schemas - return null; - } - - return "PUBLIC"; - } - - /** - * Main entry point for the test suite. Executing this method will run the - * test suite on all registered databases. - * - * @param args - * @throws Exception - */ - public static void main(String... args) throws Exception { - Params params = new Params(); - JCommander jc = new JCommander(params); - try { - jc.parse(args); - } catch (ParameterException t) { - usage(jc, t); - } - - // Replace System.out with a file - if (!StringUtils.isNullOrEmpty(params.dbPerformanceFile)) { - out = new PrintStream(params.dbPerformanceFile); - System.setErr(out); - } - - deleteRecursively(baseFolder); - new File(baseFolder, "/sqlite").mkdirs(); - - // Start the HSQL, H2, and Derby servers in-process - org.hsqldb.Server hsql = startHSQL(); - org.h2.tools.Server h2 = startH2(); - NetworkServerControl derby = startDerby(); - - // Statement logging - final FileWriter statementWriter; - if (StringUtils.isNullOrEmpty(params.sqlStatementsFile)) { - statementWriter = null; - } else { - statementWriter = new FileWriter(params.sqlStatementsFile); - } - IciqlListener statementListener = new IciqlListener() { - @Override - public void logIciql(StatementType type, String statement) { - if (statementWriter == null) { - return; - } - try { - statementWriter.append(statement); - statementWriter.append('\n'); - } catch (IOException e) { - e.printStackTrace(); - } - } - }; - IciqlLogger.registerListener(statementListener); - - SuiteClasses suiteClasses = IciqlSuite.class.getAnnotation(SuiteClasses.class); - long quickestDatabase = Long.MAX_VALUE; - String dividerMajor = buildDivider('*', 79); - String dividerMinor = buildDivider('-', 79); - - // Header - out.println(dividerMajor); - out.println(MessageFormat.format("{0} {1} ({2}) testing {3} database configurations", Constants.NAME, - Constants.getVersion(), Constants.getBuildDate(), TEST_DBS.length)); - out.println(dividerMajor); - out.println(); - - showProperty("java.vendor"); - showProperty("java.runtime.version"); - showProperty("java.vm.name"); - showProperty("os.name"); - showProperty("os.version"); - showProperty("os.arch"); - showProperty("available processors", "" + Runtime.getRuntime().availableProcessors()); - showProperty( - "available memory", - MessageFormat.format("{0,number,0.0} GB", ((double) Runtime.getRuntime().maxMemory()) - / (1024 * 1024))); - out.println(); - - // Test a database - long lastCount = 0; - for (TestDb testDb : TEST_DBS) { - out.println(dividerMinor); - out.println("Testing " + testDb.describeDatabase()); - out.println(" " + testDb.url); - out.println(dividerMinor); - - // inject a database section delimiter in the statement log - if (statementWriter != null) { - statementWriter.append("\n\n"); - statementWriter.append("# ").append(dividerMinor).append('\n'); - statementWriter.append("# ").append("Testing " + testDb.describeDatabase()).append('\n'); - statementWriter.append("# ").append(dividerMinor).append('\n'); - statementWriter.append("\n\n"); - } - - if (testDb.getVersion().equals("OFFLINE")) { - // Database not available - out.println("Skipping. Could not find " + testDb.url); - out.println(); - } else { - // Setup system properties - System.setProperty("iciql.url", testDb.url); - System.setProperty("iciql.user", testDb.username); - System.setProperty("iciql.password", testDb.password); - - // Test database - Result result = JUnitCore.runClasses(suiteClasses.value()); - - // Report results - testDb.runtime = result.getRunTime(); - if (testDb.runtime < quickestDatabase) { - quickestDatabase = testDb.runtime; - } - testDb.statements = IciqlLogger.getTotalCount() - lastCount; - // reset total count for next database - lastCount = IciqlLogger.getTotalCount(); - - out.println(MessageFormat.format( - "{0} tests ({1} failures, {2} ignores) {3} statements in {4,number,0.000} secs", - result.getRunCount(), result.getFailureCount(), result.getIgnoreCount(), - testDb.statements, result.getRunTime() / 1000f)); - - if (result.getFailureCount() == 0) { - out.println(); - out.println(" 100% successful test suite run."); - out.println(); - } else { - for (Failure failure : result.getFailures()) { - out.println(MessageFormat.format("\n + {0}\n {1}", failure.getTestHeader(), - failure.getMessage())); - } - out.println(); - } - } - } - - // Display runtime results sorted by performance leader - out.println(); - out.println(dividerMajor); - out.println(MessageFormat.format("{0} {1} ({2}) test suite performance results", Constants.NAME, - Constants.getVersion(), Constants.getBuildDate())); - - StringBuilder compressedSystem = new StringBuilder(); - compressedSystem.append(" on "); - compressedSystem.append(System.getProperty("java.vendor")); - compressedSystem.append(' '); - compressedSystem.append(System.getProperty("java.runtime.version")); - compressedSystem.append(", "); - compressedSystem.append(System.getProperty("os.name")); - compressedSystem.append(' '); - compressedSystem.append(System.getProperty("os.version")); - compressedSystem.append(", "); - compressedSystem.append(System.getProperty("os.arch")); - out.println(compressedSystem.toString()); - - out.println(dividerMajor); - List dbs = Arrays.asList(TEST_DBS); - Collections.sort(dbs); - - out.println(MessageFormat.format("{0} {1} {2} {3} {4}", - StringUtils.pad("Name", 11, " ", true), - StringUtils.pad("Config", 16, " ", true), - StringUtils.pad("Version", 25, " ", true), - StringUtils.pad("Stats/sec", 10, " ", true), - "Runtime")); - out.println(dividerMinor); - for (TestDb testDb : dbs) { - DecimalFormat df = new DecimalFormat("0.0"); - out.println(MessageFormat.format("{0} {1} {2} {3} {4}s ({5,number,0.0}x)", - StringUtils.pad(testDb.name, 11, " ", true), - StringUtils.pad(testDb.config, 16, " ", true), - StringUtils.pad(testDb.getVersion(), 23, " ", true), - StringUtils.pad("" + testDb.getStatementRate(), 7, " ", false), - StringUtils.pad(df.format(testDb.getRuntime()), 8, " ", false), - ((double) testDb.runtime) / quickestDatabase)); - } - out.println(dividerMinor); - - // cleanup - for (PoolableConnectionFactory factory : connectionFactories.values()) { - factory.getPool().close(); - } - IciqlLogger.unregisterListener(statementListener); - out.close(); - System.setErr(ERR); - if (statementWriter != null) { - statementWriter.close(); - } - hsql.stop(); - h2.stop(); - derby.shutdown(); - System.exit(0); - } - - private static void showProperty(String name) { - showProperty(name, System.getProperty(name)); - } - - private static void showProperty(String name, String value) { - out.print(' '); - out.print(StringUtils.pad(name, 25, " ", true)); - out.println(value); - } - - private static void usage(JCommander jc, ParameterException t) { - System.out.println(Constants.NAME + " test suite v" + Constants.getVersion()); - System.out.println(); - if (t != null) { - System.out.println(t.getMessage()); - System.out.println(); - } - if (jc != null) { - jc.usage(); - } - System.exit(0); - } - - private static String buildDivider(char c, int length) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < length; i++) { - sb.append(c); - } - return sb.toString(); - } - - private static void deleteRecursively(File f) { - if (f.isDirectory()) { - for (File file : f.listFiles()) { - if (file.isDirectory()) { - deleteRecursively(file); - } - file.delete(); - } - } - f.delete(); - } - - /** - * Start an HSQL tcp server. - * - * @return an HSQL server instance - * @throws Exception - */ - private static org.hsqldb.Server startHSQL() throws Exception { - HsqlProperties p = new HsqlProperties(); - String db = new File(System.getProperty("user.dir")).getAbsolutePath() + "/testdbs/hsqltcp/iciql"; - p.setProperty("server.database.0", "file:" + db); - p.setProperty("server.dbname.0", "iciql"); - // set up the rest of properties - - // alternative to the above is - org.hsqldb.Server server = new org.hsqldb.Server(); - server.setProperties(p); - server.setLogWriter(null); - server.setErrWriter(null); - server.start(); - return server; - } - - /** - * Start the H2 tcp server. - * - * @return an H2 server instance - * @throws Exception - */ - private static org.h2.tools.Server startH2() throws Exception { - org.h2.tools.Server server = org.h2.tools.Server.createTcpServer(); - server.start(); - return server; - } - - /** - * Start the Derby tcp server. - * - * @return an Derby server instance - * @throws Exception - */ - private static NetworkServerControl startDerby() throws Exception { - NetworkServerControl serverControl = new NetworkServerControl(); - serverControl.start(null); - return serverControl; - } - - /** - * Represents a test database url. - */ - private static class TestDb implements Comparable { - final String name; - final String config; - final String url; - final String username; - final String password; - String version; - long runtime; - long statements; - - TestDb(String name, String config, String url) { - this(name, config, url, "sa", ""); - } - - TestDb(String name, String config, String url, String username, String password) { - this.name = name; - this.config = config; - this.url = url; - this.username = username; - this.password = password; - } - - double getRuntime() { - return runtime / 1000d; - } - - int getStatementRate() { - return Double.valueOf((statements) / (runtime / 1000d)).intValue(); - } - - String describeDatabase() { - StringBuilder sb = new StringBuilder(name); - sb.append(" "); - sb.append(getVersion()); - return sb.toString(); - } - - String getVersion() { - if (version == null) { - try { - Db db = Db.open(url, username, password); - version = db.getConnection().getMetaData().getDatabaseProductVersion(); - db.close(); - return version; - } catch (Throwable t) { - version = "OFFLINE"; - } - } - return version; - } - - @Override - public int compareTo(TestDb o) { - if (runtime == 0) { - return 1; - } - if (o.runtime == 0) { - return -1; - } - int r1 = getStatementRate(); - int r2 = o.getStatementRate(); - if (r1 == r2) { - return 0; - } - if (r1 < r2) { - return 1; - } - return -1; - } - } - - /** - * Command-line parameters for TestSuite. - */ - @Parameters(separators = " ") - private static class Params { - - @Parameter(names = { "--dbFile" }, description = "Database performance results text file", required = false) - public String dbPerformanceFile; - - @Parameter(names = { "--sqlFile" }, description = "SQL statements log file", required = false) - public String sqlStatementsFile; - } + private final static File baseFolder = new File(System.getProperty("user.dir"), "/testdbs"); + private static final TestDb[] TEST_DBS = { + new TestDb("H2", "memory", "jdbc:h2:mem:iciql"), + new TestDb("H2", "file", "jdbc:h2:file:" + + new File(baseFolder, "/h2/iciql").getAbsolutePath()), + new TestDb("H2", "tcp", "jdbc:h2:tcp://localhost/" + + new File(baseFolder, "/h2tcp/iciql").getAbsolutePath()), + new TestDb("HSQL", "memory", "jdbc:hsqldb:mem:iciql"), + new TestDb("HSQL", "file", "jdbc:hsqldb:file:testdbs/hsql/iciql"), + new TestDb("HSQL", "tcp", "jdbc:hsqldb:hsql://localhost/iciql"), + new TestDb("Derby", "memory", "jdbc:derby:memory:iciql;create=true"), + new TestDb("Derby", "file", "jdbc:derby:directory:testdbs/derby/iciql;create=true"), + new TestDb("Derby", "tcp", "jdbc:derby://localhost:1527/testdbs/derby/iciql;create=true", "sa", "sa"), + new TestDb("MySQL", "tcp", "jdbc:mysql://localhost:3306/iciql", "sa", "sa"), + new TestDb("PostgreSQL", "tcp", "jdbc:postgresql://localhost:5432/iciql", "sa", "sa"), + + // + // SQLite Memory + // + new TestDb("SQLite", "memory", "jdbc:sqlite:file::memory:?cache=shared&foreign_keys=ON"), + + // + // SQLite DELETE rollback journal (default) + // + new TestDb("SQLite", "delete,full_sync", "jdbc:sqlite:" + + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() + + "?foreign_keys=ON&journal_mode=DELETE&synchronous=FULL"), + + new TestDb("SQLite", "delete,norm_sync", "jdbc:sqlite:" + + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() + + "?foreign_keys=ON&journal_mode=DELETE&synchronous=NORMAL"), + + new TestDb("SQLite", "delete,no_sync", "jdbc:sqlite:" + + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() + + "?foreign_keys=ON&journal_mode=DELETE&synchronous=OFF"), + + // + // SQLite WAL + // + new TestDb("SQLite", "wal,full_sync", "jdbc:sqlite:" + + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() + + "?foreign_keys=ON&journal_mode=WAL&synchronous=FULL"), + + new TestDb("SQLite", "wal,norm_sync", "jdbc:sqlite:" + + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() + + "?foreign_keys=ON&journal_mode=WAL&synchronous=NORMAL"), + + new TestDb("SQLite", "wal,no_sync", "jdbc:sqlite:" + + new File(baseFolder, "/sqlite/iciql.db").getAbsolutePath() + + "?foreign_keys=ON&journal_mode=WAL&synchronous=OFF"), + + }; + + private static final TestDb DEFAULT_TEST_DB = TEST_DBS[3]; + + private static final PrintStream ERR = System.err; + + private static PrintStream out = System.out; + + private static Map connectionFactories = Utils + .newSynchronizedHashMap(); + + private static Map dataSources = Utils.newSynchronizedHashMap(); + + public static void assertStartsWith(String value, String startsWith) { + Assert.assertTrue(MessageFormat.format("Expected \"{0}\", got: \"{1}\"", startsWith, value), + value.startsWith(startsWith)); + } + + public static void assertEqualsIgnoreCase(String expected, String actual) { + Assert.assertTrue(MessageFormat.format("Expected \"{0}\", got: \"{1}\"", expected, actual), + expected.equalsIgnoreCase(actual)); + } + + public static boolean equivalentTo(double expected, double actual) { + if (Double.compare(expected, actual) == 0) { + return true; + } + return Math.abs(expected - actual) <= 0.000001d; + } + + public static Db openNewDb() { + return openNewDb(Mode.PROD); + } + + /** + * Open a new Db object. All connections are cached and re-used to eliminate + * embedded database startup costs. + * + * @param mode + * @return a fresh Db object + */ + public static Db openNewDb(Mode mode) { + String testUrl = System.getProperty("iciql.url", DEFAULT_TEST_DB.url); + String testUser = System.getProperty("iciql.user", DEFAULT_TEST_DB.username); + String testPassword = System.getProperty("iciql.password", DEFAULT_TEST_DB.password); + + Db db = null; + PoolingDataSource dataSource = dataSources.get(testUrl); + if (dataSource == null) { + ConnectionFactory connectionFactory = new DriverManagerConnectionFactory(testUrl, testUser, + testPassword); + GenericObjectPool pool = new GenericObjectPool(); + pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW); + PoolableConnectionFactory factory = new PoolableConnectionFactory(connectionFactory, pool, null, + null, false, true); + dataSource = new PoolingDataSource(pool); + dataSources.put(testUrl, dataSource); + connectionFactories.put(testUrl, factory); + } + db = Db.open(dataSource, mode); + + // drop views + db.dropView(ProductView.class); + db.dropView(ProductViewInherited.class); + db.dropView(ProductViewFromQuery.class); + db.dropView(ProductViewInheritedComplex.class); + + // drop tables + db.dropTable(BooleanModel.class); + db.dropTable(ComplexObject.class); + db.dropTable(Customer.class); + db.dropTable(DefaultValuesModel.class); + db.dropTable(EnumIdModel.class); + db.dropTable(EnumOrdinalModel.class); + db.dropTable(EnumStringModel.class); + db.dropTable(Order.class); + db.dropTable(PrimitivesModel.class); + db.dropTable(Product.class); + db.dropTable(ProductAnnotationOnly.class); + db.dropTable(ProductInheritedAnnotation.class); + db.dropTable(ProductMixedAnnotation.class); + db.dropTable(SupportedTypes.class); + db.dropTable(JoinTest.UserId.class); + db.dropTable(JoinTest.UserNote.class); + db.dropTable(EnumsTest.BadEnums.class); + db.dropTable(MultipleBoolsModel.class); + db.dropTable(ProductAnnotationOnlyWithForeignKey.class); + db.dropTable(CategoryAnnotationOnly.class); + db.dropTable(SerializedObjectTypeAdapterTest.class); + + return db; + } + + /** + * Open the current database. + * + * @return the current database + */ + public static Db openCurrentDb() { + String testUrl = System.getProperty("iciql.url", DEFAULT_TEST_DB.url); + String testUser = System.getProperty("iciql.user", DEFAULT_TEST_DB.username); + String testPassword = System.getProperty("iciql.password", DEFAULT_TEST_DB.password); + return Db.open(testUrl, testUser, testPassword); + } + + /** + * Returns the name of the underlying database engine for the Db object. + * + * @param db + * @return the database engine name + */ + public static String getDatabaseEngineName(Db db) { + String database = ""; + try { + database = db.getConnection().getMetaData().getDatabaseProductName(); + } catch (SQLException s) { + } + return database; + } + + /** + * Returns true if the underlying database engine is Derby. + * + * @param db + * @return true if underlying database engine is Derby + */ + public static boolean isDerby(Db db) { + return IciqlSuite.getDatabaseEngineName(db).equals("Apache Derby"); + } + + /** + * Returns true if the underlying database engine is H2. + * + * @param db + * @return true if underlying database engine is H2 + */ + public static boolean isH2(Db db) { + return IciqlSuite.getDatabaseEngineName(db).equals("H2"); + } + + /** + * Returns true if the underlying database engine is MySQL. + * + * @param db + * @return true if underlying database engine is MySQL + */ + public static boolean isMySQL(Db db) { + return IciqlSuite.getDatabaseEngineName(db).equals("MySQL"); + } + + /** + * Returns true if the underlying database engine is SQLite. + * + * @param db + * @return true if underlying database engine is SQLite + */ + public static boolean isSQLite(Db db) { + return IciqlSuite.getDatabaseEngineName(db).equals("SQLite"); + } + + /** + * Gets the default schema of the underlying database engine. + * + * @param db + * @return the default schema + */ + public static String getDefaultSchema(Db db) { + if (isDerby(db)) { + // Derby sets default schema name to username + return "SA"; + } else if (isMySQL(db)) { + // MySQL does not have schemas + return null; + } else if (isSQLite(db)) { + // SQLite does not have schemas + return null; + } + + return "PUBLIC"; + } + + /** + * Main entry point for the test suite. Executing this method will run the + * test suite on all registered databases. + * + * @param args + * @throws Exception + */ + public static void main(String... args) throws Exception { + Params params = new Params(); + JCommander jc = new JCommander(params); + try { + jc.parse(args); + } catch (ParameterException t) { + usage(jc, t); + } + + // Replace System.out with a file + if (!StringUtils.isNullOrEmpty(params.dbPerformanceFile)) { + out = new PrintStream(params.dbPerformanceFile); + System.setErr(out); + } + + deleteRecursively(baseFolder); + new File(baseFolder, "/sqlite").mkdirs(); + + // Start the HSQL, H2, and Derby servers in-process + org.hsqldb.Server hsql = startHSQL(); + org.h2.tools.Server h2 = startH2(); + NetworkServerControl derby = startDerby(); + + // Statement logging + final FileWriter statementWriter; + if (StringUtils.isNullOrEmpty(params.sqlStatementsFile)) { + statementWriter = null; + } else { + statementWriter = new FileWriter(params.sqlStatementsFile); + } + IciqlListener statementListener = new IciqlListener() { + @Override + public void logIciql(StatementType type, String statement) { + if (statementWriter == null) { + return; + } + try { + statementWriter.append(statement); + statementWriter.append('\n'); + } catch (IOException e) { + e.printStackTrace(); + } + } + }; + IciqlLogger.registerListener(statementListener); + + SuiteClasses suiteClasses = IciqlSuite.class.getAnnotation(SuiteClasses.class); + long quickestDatabase = Long.MAX_VALUE; + String dividerMajor = buildDivider('*', 79); + String dividerMinor = buildDivider('-', 79); + + // Header + out.println(dividerMajor); + out.println(MessageFormat.format("{0} {1} ({2}) testing {3} database configurations", Constants.NAME, + Constants.getVersion(), Constants.getBuildDate(), TEST_DBS.length)); + out.println(dividerMajor); + out.println(); + + showProperty("java.vendor"); + showProperty("java.runtime.version"); + showProperty("java.vm.name"); + showProperty("os.name"); + showProperty("os.version"); + showProperty("os.arch"); + showProperty("available processors", "" + Runtime.getRuntime().availableProcessors()); + showProperty( + "available memory", + MessageFormat.format("{0,number,0.0} GB", ((double) Runtime.getRuntime().maxMemory()) + / (1024 * 1024))); + out.println(); + + // Test a database + long lastCount = 0; + for (TestDb testDb : TEST_DBS) { + out.println(dividerMinor); + out.println("Testing " + testDb.describeDatabase()); + out.println(" " + testDb.url); + out.println(dividerMinor); + + // inject a database section delimiter in the statement log + if (statementWriter != null) { + statementWriter.append("\n\n"); + statementWriter.append("# ").append(dividerMinor).append('\n'); + statementWriter.append("# ").append("Testing " + testDb.describeDatabase()).append('\n'); + statementWriter.append("# ").append(dividerMinor).append('\n'); + statementWriter.append("\n\n"); + } + + if (testDb.getVersion().equals("OFFLINE")) { + // Database not available + out.println("Skipping. Could not find " + testDb.url); + out.println(); + } else { + // Setup system properties + System.setProperty("iciql.url", testDb.url); + System.setProperty("iciql.user", testDb.username); + System.setProperty("iciql.password", testDb.password); + + // Test database + Result result = JUnitCore.runClasses(suiteClasses.value()); + + // Report results + testDb.runtime = result.getRunTime(); + if (testDb.runtime < quickestDatabase) { + quickestDatabase = testDb.runtime; + } + testDb.statements = IciqlLogger.getTotalCount() - lastCount; + // reset total count for next database + lastCount = IciqlLogger.getTotalCount(); + + out.println(MessageFormat.format( + "{0} tests ({1} failures, {2} ignores) {3} statements in {4,number,0.000} secs", + result.getRunCount(), result.getFailureCount(), result.getIgnoreCount(), + testDb.statements, result.getRunTime() / 1000f)); + + if (result.getFailureCount() == 0) { + out.println(); + out.println(" 100% successful test suite run."); + out.println(); + } else { + for (Failure failure : result.getFailures()) { + out.println(MessageFormat.format("\n + {0}\n {1}", failure.getTestHeader(), + failure.getMessage())); + } + out.println(); + } + } + } + + // Display runtime results sorted by performance leader + out.println(); + out.println(dividerMajor); + out.println(MessageFormat.format("{0} {1} ({2}) test suite performance results", Constants.NAME, + Constants.getVersion(), Constants.getBuildDate())); + + StringBuilder compressedSystem = new StringBuilder(); + compressedSystem.append(" on "); + compressedSystem.append(System.getProperty("java.vendor")); + compressedSystem.append(' '); + compressedSystem.append(System.getProperty("java.runtime.version")); + compressedSystem.append(", "); + compressedSystem.append(System.getProperty("os.name")); + compressedSystem.append(' '); + compressedSystem.append(System.getProperty("os.version")); + compressedSystem.append(", "); + compressedSystem.append(System.getProperty("os.arch")); + out.println(compressedSystem.toString()); + + out.println(dividerMajor); + List dbs = Arrays.asList(TEST_DBS); + Collections.sort(dbs); + + out.println(MessageFormat.format("{0} {1} {2} {3} {4}", + StringUtils.pad("Name", 11, " ", true), + StringUtils.pad("Config", 16, " ", true), + StringUtils.pad("Version", 25, " ", true), + StringUtils.pad("Stats/sec", 10, " ", true), + "Runtime")); + out.println(dividerMinor); + for (TestDb testDb : dbs) { + DecimalFormat df = new DecimalFormat("0.0"); + out.println(MessageFormat.format("{0} {1} {2} {3} {4}s ({5,number,0.0}x)", + StringUtils.pad(testDb.name, 11, " ", true), + StringUtils.pad(testDb.config, 16, " ", true), + StringUtils.pad(testDb.getVersion(), 23, " ", true), + StringUtils.pad("" + testDb.getStatementRate(), 7, " ", false), + StringUtils.pad(df.format(testDb.getRuntime()), 8, " ", false), + ((double) testDb.runtime) / quickestDatabase)); + } + out.println(dividerMinor); + + // cleanup + for (PoolableConnectionFactory factory : connectionFactories.values()) { + factory.getPool().close(); + } + IciqlLogger.unregisterListener(statementListener); + out.close(); + System.setErr(ERR); + if (statementWriter != null) { + statementWriter.close(); + } + hsql.stop(); + h2.stop(); + derby.shutdown(); + System.exit(0); + } + + private static void showProperty(String name) { + showProperty(name, System.getProperty(name)); + } + + private static void showProperty(String name, String value) { + out.print(' '); + out.print(StringUtils.pad(name, 25, " ", true)); + out.println(value); + } + + private static void usage(JCommander jc, ParameterException t) { + System.out.println(Constants.NAME + " test suite v" + Constants.getVersion()); + System.out.println(); + if (t != null) { + System.out.println(t.getMessage()); + System.out.println(); + } + if (jc != null) { + jc.usage(); + } + System.exit(0); + } + + private static String buildDivider(char c, int length) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + sb.append(c); + } + return sb.toString(); + } + + private static void deleteRecursively(File f) { + if (f.isDirectory()) { + for (File file : f.listFiles()) { + if (file.isDirectory()) { + deleteRecursively(file); + } + file.delete(); + } + } + f.delete(); + } + + /** + * Start an HSQL tcp server. + * + * @return an HSQL server instance + * @throws Exception + */ + private static org.hsqldb.Server startHSQL() throws Exception { + HsqlProperties p = new HsqlProperties(); + String db = new File(System.getProperty("user.dir")).getAbsolutePath() + "/testdbs/hsqltcp/iciql"; + p.setProperty("server.database.0", "file:" + db); + p.setProperty("server.dbname.0", "iciql"); + // set up the rest of properties + + // alternative to the above is + org.hsqldb.Server server = new org.hsqldb.Server(); + server.setProperties(p); + server.setLogWriter(null); + server.setErrWriter(null); + server.start(); + return server; + } + + /** + * Start the H2 tcp server. + * + * @return an H2 server instance + * @throws Exception + */ + private static org.h2.tools.Server startH2() throws Exception { + org.h2.tools.Server server = org.h2.tools.Server.createTcpServer(); + server.start(); + return server; + } + + /** + * Start the Derby tcp server. + * + * @return an Derby server instance + * @throws Exception + */ + private static NetworkServerControl startDerby() throws Exception { + NetworkServerControl serverControl = new NetworkServerControl(); + serverControl.start(null); + return serverControl; + } + + /** + * Represents a test database url. + */ + private static class TestDb implements Comparable { + final String name; + final String config; + final String url; + final String username; + final String password; + String version; + long runtime; + long statements; + + TestDb(String name, String config, String url) { + this(name, config, url, "sa", ""); + } + + TestDb(String name, String config, String url, String username, String password) { + this.name = name; + this.config = config; + this.url = url; + this.username = username; + this.password = password; + } + + double getRuntime() { + return runtime / 1000d; + } + + int getStatementRate() { + return Double.valueOf((statements) / (runtime / 1000d)).intValue(); + } + + String describeDatabase() { + StringBuilder sb = new StringBuilder(name); + sb.append(" "); + sb.append(getVersion()); + return sb.toString(); + } + + String getVersion() { + if (version == null) { + try { + Db db = Db.open(url, username, password); + version = db.getConnection().getMetaData().getDatabaseProductVersion(); + db.close(); + return version; + } catch (Throwable t) { + version = "OFFLINE"; + } + } + return version; + } + + @Override + public int compareTo(TestDb o) { + if (runtime == 0) { + return 1; + } + if (o.runtime == 0) { + return -1; + } + int r1 = getStatementRate(); + int r2 = o.getStatementRate(); + if (r1 == r2) { + return 0; + } + if (r1 < r2) { + return 1; + } + return -1; + } + } + + /** + * Command-line parameters for TestSuite. + */ + @Parameters(separators = " ") + private static class Params { + + @Parameter(names = {"--dbFile"}, description = "Database performance results text file", required = false) + public String dbPerformanceFile; + + @Parameter(names = {"--sqlFile"}, description = "SQL statements log file", required = false) + public String sqlStatementsFile; + } } \ No newline at end of file diff --git a/src/test/java/com/iciql/test/JoinTest.java b/src/test/java/com/iciql/test/JoinTest.java index 0e5e39d..e3370b1 100644 --- a/src/test/java/com/iciql/test/JoinTest.java +++ b/src/test/java/com/iciql/test/JoinTest.java @@ -16,72 +16,70 @@ package com.iciql.test; -import static org.junit.Assert.assertEquals; - -import java.util.Arrays; -import java.util.List; - -import org.junit.After; -import org.junit.Assume; -import org.junit.Before; -import org.junit.Test; - import com.iciql.Db; import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQTable; import com.iciql.QueryWhere; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.assertEquals; /** * Tests of Joins. */ public class JoinTest { - Db db; - - @Before - public void setup() { - db = IciqlSuite.openNewDb(); - - db.insertAll(UserId.getList()); - db.insertAll(UserNote.getList()); - } - - @After - public void tearDown() { - db.close(); - } - - @Test - public void testPrimitiveJoin() throws Exception { - final UserId u = new UserId(); - final UserNote n = new UserNote(); - - List notes = db.from(u).innerJoin(n).on(u.id).is(n.userId).where(u.id).is(2) - .select(new UserNote() { - { - userId = n.userId; - noteId = n.noteId; - text = n.text; - } - }); - assertEquals(3, notes.size()); - } - - @Test - public void testJoin() throws Exception { - final UserId u = new UserId(); - final UserNote n = new UserNote(); - - // this query returns 1 UserId if the user has a note - // it's purpose is to confirm fluency/type-safety on a very simple - // join case where the main table is filtered/reduced by hits in a - // related table - - List users = db.from(u).innerJoin(n).on(u.id).is(n.userId).where(u.id).is(2).selectDistinct(); - - assertEquals(1, users.size()); - assertEquals(2, users.get(0).id); - } + Db db; + + @Before + public void setup() { + db = IciqlSuite.openNewDb(); + + db.insertAll(UserId.getList()); + db.insertAll(UserNote.getList()); + } + + @After + public void tearDown() { + db.close(); + } + + @Test + public void testPrimitiveJoin() throws Exception { + final UserId u = new UserId(); + final UserNote n = new UserNote(); + + List notes = db.from(u).innerJoin(n).on(u.id).is(n.userId).where(u.id).is(2) + .select(new UserNote() { + { + userId = n.userId; + noteId = n.noteId; + text = n.text; + } + }); + assertEquals(3, notes.size()); + } + + @Test + public void testJoin() throws Exception { + final UserId u = new UserId(); + final UserNote n = new UserNote(); + + // this query returns 1 UserId if the user has a note + // it's purpose is to confirm fluency/type-safety on a very simple + // join case where the main table is filtered/reduced by hits in a + // related table + + List users = db.from(u).innerJoin(n).on(u.id).is(n.userId).where(u.id).is(2).selectDistinct(); + + assertEquals(1, users.size()); + assertEquals(2, users.get(0).id); + } @Test public void testLeftJoin() throws Exception { @@ -103,71 +101,71 @@ public class JoinTest { assertEquals(3, notes.size()); // do not test MySQL on this statement because the databases - if (IciqlSuite.isMySQL(db)) { - assertEquals("SELECT * FROM UserId WHERE `id` in (SELECT `userId` FROM UserNote WHERE `userId` > 0 )", q.toSQL()); - } else { - assertEquals("SELECT * FROM UserId WHERE id in (SELECT userId FROM UserNote WHERE userId > 0 )", q.toSQL()); - } + if (IciqlSuite.isMySQL(db)) { + assertEquals("SELECT * FROM UserId WHERE `id` in (SELECT `userId` FROM UserNote WHERE `userId` > 0 )", q.toSQL()); + } else { + assertEquals("SELECT * FROM UserId WHERE id in (SELECT userId FROM UserNote WHERE userId > 0 )", q.toSQL()); + } + } + + @IQTable + public static class UserId { + + @IQColumn(primaryKey = true) + public int id; + + @IQColumn(length = 10) + public String name; + + public UserId() { + // public constructor + } + + public UserId(int id, String name) { + this.id = id; + this.name = name; + } + + public String toString() { + return name + " (" + id + ")"; + } + + public static List getList() { + UserId[] list = {new UserId(1, "Tom"), new UserId(2, "Dick"), new UserId(3, "Harry"), new UserId(4, "Jack")}; + return Arrays.asList(list); + } + } + + @IQTable + public static class UserNote { + + @IQColumn(autoIncrement = true, primaryKey = true) + public int noteId; + + @IQColumn + public int userId; + + @IQColumn(length = 10) + public String text; + + public UserNote() { + // public constructor + } + + public UserNote(int userId, String text) { + this.userId = userId; + this.text = text; + } + + public String toString() { + return text; + } + + public static List getList() { + UserNote[] list = {new UserNote(1, "A"), new UserNote(2, "B"), new UserNote(3, "C"), + new UserNote(1, "D"), new UserNote(2, "E"), new UserNote(3, "F"), new UserNote(1, "G"), + new UserNote(2, "H"), new UserNote(3, "I"),}; + return Arrays.asList(list); + } } - - @IQTable - public static class UserId { - - @IQColumn(primaryKey = true) - public int id; - - @IQColumn(length = 10) - public String name; - - public UserId() { - // public constructor - } - - public UserId(int id, String name) { - this.id = id; - this.name = name; - } - - public String toString() { - return name + " (" + id + ")"; - } - - public static List getList() { - UserId[] list = { new UserId(1, "Tom"), new UserId(2, "Dick"), new UserId(3, "Harry"), new UserId(4, "Jack") }; - return Arrays.asList(list); - } - } - - @IQTable - public static class UserNote { - - @IQColumn(autoIncrement = true, primaryKey = true) - public int noteId; - - @IQColumn - public int userId; - - @IQColumn(length = 10) - public String text; - - public UserNote() { - // public constructor - } - - public UserNote(int userId, String text) { - this.userId = userId; - this.text = text; - } - - public String toString() { - return text; - } - - public static List getList() { - UserNote[] list = { new UserNote(1, "A"), new UserNote(2, "B"), new UserNote(3, "C"), - new UserNote(1, "D"), new UserNote(2, "E"), new UserNote(3, "F"), new UserNote(1, "G"), - new UserNote(2, "H"), new UserNote(3, "I"), }; - return Arrays.asList(list); - } - } } diff --git a/src/test/java/com/iciql/test/ModelsTest.java b/src/test/java/com/iciql/test/ModelsTest.java index 0b43c57..742ed53 100644 --- a/src/test/java/com/iciql/test/ModelsTest.java +++ b/src/test/java/com/iciql/test/ModelsTest.java @@ -17,20 +17,6 @@ package com.iciql.test; -import static com.iciql.test.IciqlSuite.assertEqualsIgnoreCase; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.sql.SQLException; -import java.text.MessageFormat; -import java.util.List; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ErrorCollector; - import com.iciql.Db; import com.iciql.DbInspector; import com.iciql.ValidationRemark; @@ -39,140 +25,153 @@ import com.iciql.test.models.ProductAnnotationOnly; import com.iciql.test.models.ProductMixedAnnotation; import com.iciql.test.models.SupportedTypes; import com.iciql.util.StringUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ErrorCollector; + +import java.sql.SQLException; +import java.text.MessageFormat; +import java.util.List; + +import static com.iciql.test.IciqlSuite.assertEqualsIgnoreCase; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Test that the mapping between classes and tables is done correctly. */ public class ModelsTest { - /* - * The ErrorCollector Rule allows execution of a test to continue after the - * first problem is found and report them all at once - */ - @Rule - public ErrorCollector errorCollector = new ErrorCollector(); - - private Db db; - - @Before - public void setUp() { - db = IciqlSuite.openNewDb(); - db.insertAll(Product.getList()); - db.insertAll(ProductAnnotationOnly.getList()); - db.insertAll(ProductMixedAnnotation.getList()); - } - - @After - public void tearDown() { - db.close(); - } - - @Test - public void testValidateModels() { - // SQLite metadata mapping in the JDBC driver needs improvement - String schemaName = IciqlSuite.getDefaultSchema(db); - DbInspector inspector = new DbInspector(db); - validateModel(inspector, schemaName, new ProductAnnotationOnly(), IciqlSuite.isSQLite(db) ? 5 : 2); - validateModel(inspector, schemaName, new ProductMixedAnnotation(), IciqlSuite.isSQLite(db) ? 6 : 4); - } - - private void validateModel(DbInspector inspector, String schemaName, Object o, int expected) { - List remarks = inspector.validateModel(o, false); - assertTrue("validation remarks are null for " + o.getClass().getName(), remarks != null); - StringBuilder sb = new StringBuilder(); - sb.append("validation remarks for " + o.getClass().getName()); - sb.append('\n'); - for (ValidationRemark remark : remarks) { - sb.append(remark.toString()); - sb.append('\n'); - if (remark.isError()) { - errorCollector.addError(new SQLException(remark.toString())); - } - } - - if (IciqlSuite.isSQLite(db)) { - assertEquals(sb.toString(), expected, remarks.size()); - } else if (StringUtils.isNullOrEmpty(schemaName)) { - // no schema expected - assertEquals(sb.toString(), expected - 1, remarks.size()); - } else { - assertEquals(sb.toString(), expected, remarks.size()); - assertEqualsIgnoreCase(MessageFormat.format("@IQSchema(\"{0}\")", schemaName), - remarks.get(0).message); - } - } - - @Test - public void testSupportedTypes() { - List original = SupportedTypes.createList(); - db.insertAll(original); - List retrieved = db.from(SupportedTypes.SAMPLE).select(); - assertEquals(original.size(), retrieved.size()); - for (int i = 0; i < original.size(); i++) { - SupportedTypes o = original.get(i); - SupportedTypes r = retrieved.get(i); - assertTrue(o.equivalentTo(r)); - } - } - - @Test - public void testModelGeneration() { - List original = SupportedTypes.createList(); - db.insertAll(original); - DbInspector inspector = new DbInspector(db); - List models = inspector.generateModel(null, "SupportedTypes", "com.iciql.test.models", true, - true); - assertEquals(1, models.size()); - // a poor test, but a start - String dbName = IciqlSuite.getDatabaseEngineName(db); - if (dbName.equals("H2")) { - assertEquals(1587, models.get(0).length()); - } else if (dbName.startsWith("HSQL")) { - // HSQL uses Double instead of Float - assertEquals(1591, models.get(0).length()); - } else if (dbName.equals("Apache Derby")) { - // Derby uses java.sql.Timestamp not java.util.Date - // Derby uses username as schema name - assertEquals(1601, models.get(0).length()); - } else if (dbName.equals("PostgreSQL")) { - assertEquals(1643, models.get(0).length()); - } else if (dbName.equals("MySQL")) { - // MySQL uses timestamp default values like - // 0000-00-00 00:00:00 and CURRENT_TIMESTAMP - assertEquals(1673, models.get(0).length()); - } else if (dbName.equals("SQLite")) { - assertEquals(1566, models.get(0).length()); - } else { - // unknown database - assertEquals(0, models.get(0).length()); - } - } - - @Test - public void testDiscreteUpdateStringTrimming() { - List original = SupportedTypes.createList(); - db.insertAll(original); - SupportedTypes s1 = db.from(SupportedTypes.SAMPLE).where(SupportedTypes.SAMPLE.id).is(1).selectFirst(); - db.from(SupportedTypes.SAMPLE) - .set(SupportedTypes.SAMPLE.myString) - .to(s1.myString + s1.myString + s1.myString + s1.myString) - .update(); - SupportedTypes s2 = db.from(SupportedTypes.SAMPLE).where(SupportedTypes.SAMPLE.id).is(1).selectFirst(); - assertEquals(40, s2.myString.length()); - } - - @Test - public void testColumnSelection() { - List original = SupportedTypes.createList(); - db.insertAll(original); - List myStrings = db.from(SupportedTypes.SAMPLE) - .select(SupportedTypes.SAMPLE.myString); - assertEquals(10, myStrings.size()); - - List ids = db.from(SupportedTypes.SAMPLE) - .orderByDesc(SupportedTypes.SAMPLE.myInteger) - .selectDistinct(SupportedTypes.SAMPLE.myInteger); - assertEquals(10, ids.size()); - assertEquals("[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]", ids.toString()); - } + /* + * The ErrorCollector Rule allows execution of a test to continue after the + * first problem is found and report them all at once + */ + @Rule + public ErrorCollector errorCollector = new ErrorCollector(); + + private Db db; + + @Before + public void setUp() { + db = IciqlSuite.openNewDb(); + db.insertAll(Product.getList()); + db.insertAll(ProductAnnotationOnly.getList()); + db.insertAll(ProductMixedAnnotation.getList()); + } + + @After + public void tearDown() { + db.close(); + } + + @Test + public void testValidateModels() { + // SQLite metadata mapping in the JDBC driver needs improvement + String schemaName = IciqlSuite.getDefaultSchema(db); + DbInspector inspector = new DbInspector(db); + validateModel(inspector, schemaName, new ProductAnnotationOnly(), IciqlSuite.isSQLite(db) ? 5 : 2); + validateModel(inspector, schemaName, new ProductMixedAnnotation(), IciqlSuite.isSQLite(db) ? 6 : 4); + } + + private void validateModel(DbInspector inspector, String schemaName, Object o, int expected) { + List remarks = inspector.validateModel(o, false); + assertTrue("validation remarks are null for " + o.getClass().getName(), remarks != null); + StringBuilder sb = new StringBuilder(); + sb.append("validation remarks for " + o.getClass().getName()); + sb.append('\n'); + for (ValidationRemark remark : remarks) { + sb.append(remark.toString()); + sb.append('\n'); + if (remark.isError()) { + errorCollector.addError(new SQLException(remark.toString())); + } + } + + if (IciqlSuite.isSQLite(db)) { + assertEquals(sb.toString(), expected, remarks.size()); + } else if (StringUtils.isNullOrEmpty(schemaName)) { + // no schema expected + assertEquals(sb.toString(), expected - 1, remarks.size()); + } else { + assertEquals(sb.toString(), expected, remarks.size()); + assertEqualsIgnoreCase(MessageFormat.format("@IQSchema(\"{0}\")", schemaName), + remarks.get(0).message); + } + } + + @Test + public void testSupportedTypes() { + List original = SupportedTypes.createList(); + db.insertAll(original); + List retrieved = db.from(SupportedTypes.SAMPLE).select(); + assertEquals(original.size(), retrieved.size()); + for (int i = 0; i < original.size(); i++) { + SupportedTypes o = original.get(i); + SupportedTypes r = retrieved.get(i); + assertTrue(o.equivalentTo(r)); + } + } + + @Test + public void testModelGeneration() { + List original = SupportedTypes.createList(); + db.insertAll(original); + DbInspector inspector = new DbInspector(db); + List models = inspector.generateModel(null, "SupportedTypes", "com.iciql.test.models", true, + true); + assertEquals(1, models.size()); + // a poor test, but a start + String dbName = IciqlSuite.getDatabaseEngineName(db); + if (dbName.equals("H2")) { + assertEquals(1587, models.get(0).length()); + } else if (dbName.startsWith("HSQL")) { + // HSQL uses Double instead of Float + assertEquals(1591, models.get(0).length()); + } else if (dbName.equals("Apache Derby")) { + // Derby uses java.sql.Timestamp not java.util.Date + // Derby uses username as schema name + assertEquals(1601, models.get(0).length()); + } else if (dbName.equals("PostgreSQL")) { + assertEquals(1643, models.get(0).length()); + } else if (dbName.equals("MySQL")) { + // MySQL uses timestamp default values like + // 0000-00-00 00:00:00 and CURRENT_TIMESTAMP + assertEquals(1673, models.get(0).length()); + } else if (dbName.equals("SQLite")) { + assertEquals(1566, models.get(0).length()); + } else { + // unknown database + assertEquals(0, models.get(0).length()); + } + } + + @Test + public void testDiscreteUpdateStringTrimming() { + List original = SupportedTypes.createList(); + db.insertAll(original); + SupportedTypes s1 = db.from(SupportedTypes.SAMPLE).where(SupportedTypes.SAMPLE.id).is(1).selectFirst(); + db.from(SupportedTypes.SAMPLE) + .set(SupportedTypes.SAMPLE.myString) + .to(s1.myString + s1.myString + s1.myString + s1.myString) + .update(); + SupportedTypes s2 = db.from(SupportedTypes.SAMPLE).where(SupportedTypes.SAMPLE.id).is(1).selectFirst(); + assertEquals(40, s2.myString.length()); + } + + @Test + public void testColumnSelection() { + List original = SupportedTypes.createList(); + db.insertAll(original); + List myStrings = db.from(SupportedTypes.SAMPLE) + .select(SupportedTypes.SAMPLE.myString); + assertEquals(10, myStrings.size()); + + List ids = db.from(SupportedTypes.SAMPLE) + .orderByDesc(SupportedTypes.SAMPLE.myInteger) + .selectDistinct(SupportedTypes.SAMPLE.myInteger); + assertEquals(10, ids.size()); + assertEquals("[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]", ids.toString()); + } } diff --git a/src/test/java/com/iciql/test/NestedConditionsTest.java b/src/test/java/com/iciql/test/NestedConditionsTest.java index 6676c9e..008a8eb 100644 --- a/src/test/java/com/iciql/test/NestedConditionsTest.java +++ b/src/test/java/com/iciql/test/NestedConditionsTest.java @@ -17,261 +17,257 @@ package com.iciql.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.List; -import java.util.Set; -import java.util.TreeSet; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - import com.iciql.Db; import com.iciql.IciqlException; import com.iciql.NestedConditions.And; import com.iciql.NestedConditions.Or; import com.iciql.QueryWhere; import com.iciql.test.models.Customer; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class NestedConditionsTest { - enum Region { - JP, FR - } - - private Db db; - - @Before - public void setUp() { - db = IciqlSuite.openNewDb(); - db.insertAll(Customer.getList()); - } - - @After - public void tearDown() { - db.close(); - } - - private String search(Region region, String... customerIds) { - Customer model; - QueryWhere query; - - model = new Customer(); - query = db.from(model).whereTrue(); - - if (customerIds != null && customerIds.length > 0) { - query.andOpen(); - for (String value : customerIds) { - query.or(model.customerId).is(value); - } - query.close(); - } - - if (region != null) { - query.and(model.region).is(region.name()); - } - return query.toSQL(); - } - - @Test - public void andOrSyntaxTest() { - String Customer = db.getDialect().prepareTableName(null, "Customer"); - String customerId = db.getDialect().prepareColumnName("customerId"); - String region = db.getDialect().prepareColumnName("region"); - String trueValue = db.getDialect().prepareStringParameter(true); - - assertEquals( - String.format("SELECT * FROM %s WHERE (%s)", Customer, trueValue), - search(null, (String[]) null)); - assertEquals( - String.format("SELECT * FROM %s WHERE (%s)", Customer, trueValue), - search(null, new String[0])); - assertEquals( - String.format("SELECT * FROM %s WHERE (%s) AND ( %s = '0001' )", - Customer, trueValue, customerId), - search(null, "0001")); - assertEquals( - String.format("SELECT * FROM %s WHERE (%s) AND ( %s = '0001' OR %s = '0002' )", - Customer, trueValue, customerId, customerId), - search(null, "0001", "0002")); - assertEquals( - String.format("SELECT * FROM %s WHERE (%s) AND %s = 'JP'", - Customer, trueValue, region), - search(Region.JP, (String[]) null)); - assertEquals( - String.format("SELECT * FROM %s WHERE (%s) AND %s = 'JP'", - Customer, trueValue, region), - search(Region.JP, new String[0])); - assertEquals( - String.format("SELECT * FROM %s WHERE (%s) AND ( %s = '0001' ) AND %s = 'JP'", - Customer, trueValue, customerId, region), - search(Region.JP, "0001")); - assertEquals( - String.format("SELECT * FROM %s WHERE (%s) AND ( %s = '0001' OR %s = '0002' ) AND %s = 'JP'", - Customer, trueValue, customerId, customerId, region), - search(Region.JP, "0001", "0002")); - } - - @Test - public void errorTest() { - Customer model; - - model = new Customer(); - try { - db.from(model) - .where(model.customerId).is("0001") - .andOpen() - .or(model.region).is("FR") - .or(model.region).is("JP") - .close() - .toSQL(); - assertTrue(true); - } - catch (IciqlException error) { - assertTrue(false); - } - - try { - db.from(model) - .where(model.customerId).is("0001") - .andOpen() - .or(model.region).is("FR") - .or(model.region).is("JP") - .toSQL(); - assertTrue(false); - } - catch (IciqlException error) { - assertTrue(true); - } - - try { - db.from(model) - .where(model.customerId).is("0001") - .andOpen() - .or(model.region).is("FR") - .or(model.region).is("JP") - .close() - .close(); - assertTrue(false); - } - catch (IciqlException error) { - assertTrue(true); - } - } - - @Test - public void fluentSyntaxTest() { - String Customer = db.getDialect().prepareTableName(null, "Customer"); - String customerId = db.getDialect().prepareColumnName("customerId"); - String region = db.getDialect().prepareColumnName("region"); - String trueValue = db.getDialect().prepareStringParameter(true); - String falseValue = db.getDialect().prepareStringParameter(false); - - final Customer model = new Customer(); - - assertEquals( - String.format("SELECT * FROM %s WHERE (%s) AND %s = '0001' AND ( %s = 'CA' OR %s = 'LA' )", - Customer, trueValue, customerId, region, region), - - db.from(model).where(new And(db, model) {{ - - and(model.customerId).is("0001"); - and(new Or(db, model) {{ - or(model.region).is("CA"); - or(model.region).is("LA"); - }}); - - }}) - - .toSQL()); - - assertEquals( - String.format("SELECT * FROM %s WHERE (%s) OR %s = '0001' OR ( %s = '0002' AND %s = 'LA' )", - Customer, falseValue, customerId, customerId, region), - - db.from(model).where(new Or(db, model) {{ - - or(model.customerId).is("0001"); - - or(new And(db, model) {{ - and(model.customerId).is("0002"); - and(model.region).is("LA"); - }}); - - }}) - - .toSQL()); - - assertEquals( - String.format("SELECT * FROM %s WHERE (%s) OR ( %s = '0001' AND %s = 'WA' ) OR ( %s = '0002' AND %s = 'LA' )", - Customer, falseValue, customerId, region, customerId, region), - - db.from(model).where(new Or(db, model) {{ - - or(new And(db, model) {{ - and(model.customerId).is("0001"); - and(model.region).is("WA"); - }}); - - or(new And(db, model) {{ - and(model.customerId).is("0002"); - and(model.region).is("LA"); - }}); - - }}) - - .toSQL()); - - assertEquals( - String.format("SELECT * FROM %s WHERE %s = '0001' OR ( %s = '0002' AND %s = 'LA' )", - Customer, customerId, customerId, region), - - db.from(model).where(model.customerId).is("0001") - - .or(new And(db, model) {{ - and(model.customerId).is("0002"); - and(model.region).is("LA"); - }}) - - .toSQL()); - - - assertEquals( - String.format("SELECT * FROM %s WHERE %s IS NOT NULL AND ( %s = 'LA' OR %s = 'CA' OR %s = 'WA' )", - Customer, customerId, region, region, region), - db.from(model) - .where(model.customerId).isNotNull() + enum Region { + JP, FR + } + + private Db db; + + @Before + public void setUp() { + db = IciqlSuite.openNewDb(); + db.insertAll(Customer.getList()); + } + + @After + public void tearDown() { + db.close(); + } + + private String search(Region region, String... customerIds) { + Customer model; + QueryWhere query; + + model = new Customer(); + query = db.from(model).whereTrue(); + + if (customerIds != null && customerIds.length > 0) { + query.andOpen(); + for (String value : customerIds) { + query.or(model.customerId).is(value); + } + query.close(); + } + + if (region != null) { + query.and(model.region).is(region.name()); + } + return query.toSQL(); + } + + @Test + public void andOrSyntaxTest() { + String Customer = db.getDialect().prepareTableName(null, "Customer"); + String customerId = db.getDialect().prepareColumnName("customerId"); + String region = db.getDialect().prepareColumnName("region"); + String trueValue = db.getDialect().prepareStringParameter(true); + + assertEquals( + String.format("SELECT * FROM %s WHERE (%s)", Customer, trueValue), + search(null, (String[]) null)); + assertEquals( + String.format("SELECT * FROM %s WHERE (%s)", Customer, trueValue), + search(null, new String[0])); + assertEquals( + String.format("SELECT * FROM %s WHERE (%s) AND ( %s = '0001' )", + Customer, trueValue, customerId), + search(null, "0001")); + assertEquals( + String.format("SELECT * FROM %s WHERE (%s) AND ( %s = '0001' OR %s = '0002' )", + Customer, trueValue, customerId, customerId), + search(null, "0001", "0002")); + assertEquals( + String.format("SELECT * FROM %s WHERE (%s) AND %s = 'JP'", + Customer, trueValue, region), + search(Region.JP, (String[]) null)); + assertEquals( + String.format("SELECT * FROM %s WHERE (%s) AND %s = 'JP'", + Customer, trueValue, region), + search(Region.JP, new String[0])); + assertEquals( + String.format("SELECT * FROM %s WHERE (%s) AND ( %s = '0001' ) AND %s = 'JP'", + Customer, trueValue, customerId, region), + search(Region.JP, "0001")); + assertEquals( + String.format("SELECT * FROM %s WHERE (%s) AND ( %s = '0001' OR %s = '0002' ) AND %s = 'JP'", + Customer, trueValue, customerId, customerId, region), + search(Region.JP, "0001", "0002")); + } + + @Test + public void errorTest() { + Customer model; + + model = new Customer(); + try { + db.from(model) + .where(model.customerId).is("0001") + .andOpen() + .or(model.region).is("FR") + .or(model.region).is("JP") + .close() + .toSQL(); + assertTrue(true); + } catch (IciqlException error) { + assertTrue(false); + } + + try { + db.from(model) + .where(model.customerId).is("0001") + .andOpen() + .or(model.region).is("FR") + .or(model.region).is("JP") + .toSQL(); + assertTrue(false); + } catch (IciqlException error) { + assertTrue(true); + } + + try { + db.from(model) + .where(model.customerId).is("0001") + .andOpen() + .or(model.region).is("FR") + .or(model.region).is("JP") + .close() + .close(); + assertTrue(false); + } catch (IciqlException error) { + assertTrue(true); + } + } + + @Test + public void fluentSyntaxTest() { + String Customer = db.getDialect().prepareTableName(null, "Customer"); + String customerId = db.getDialect().prepareColumnName("customerId"); + String region = db.getDialect().prepareColumnName("region"); + String trueValue = db.getDialect().prepareStringParameter(true); + String falseValue = db.getDialect().prepareStringParameter(false); + + final Customer model = new Customer(); + + assertEquals( + String.format("SELECT * FROM %s WHERE (%s) AND %s = '0001' AND ( %s = 'CA' OR %s = 'LA' )", + Customer, trueValue, customerId, region, region), + + db.from(model).where(new And(db, model) {{ + + and(model.customerId).is("0001"); + and(new Or(db, model) {{ + or(model.region).is("CA"); + or(model.region).is("LA"); + }}); + + }}) + + .toSQL()); + + assertEquals( + String.format("SELECT * FROM %s WHERE (%s) OR %s = '0001' OR ( %s = '0002' AND %s = 'LA' )", + Customer, falseValue, customerId, customerId, region), + + db.from(model).where(new Or(db, model) {{ + + or(model.customerId).is("0001"); + + or(new And(db, model) {{ + and(model.customerId).is("0002"); + and(model.region).is("LA"); + }}); + + }}) + + .toSQL()); + + assertEquals( + String.format("SELECT * FROM %s WHERE (%s) OR ( %s = '0001' AND %s = 'WA' ) OR ( %s = '0002' AND %s = 'LA' )", + Customer, falseValue, customerId, region, customerId, region), + + db.from(model).where(new Or(db, model) {{ + + or(new And(db, model) {{ + and(model.customerId).is("0001"); + and(model.region).is("WA"); + }}); + + or(new And(db, model) {{ + and(model.customerId).is("0002"); + and(model.region).is("LA"); + }}); + + }}) + + .toSQL()); + + assertEquals( + String.format("SELECT * FROM %s WHERE %s = '0001' OR ( %s = '0002' AND %s = 'LA' )", + Customer, customerId, customerId, region), + + db.from(model).where(model.customerId).is("0001") + + .or(new And(db, model) {{ + and(model.customerId).is("0002"); + and(model.region).is("LA"); + }}) + + .toSQL()); + + + assertEquals( + String.format("SELECT * FROM %s WHERE %s IS NOT NULL AND ( %s = 'LA' OR %s = 'CA' OR %s = 'WA' )", + Customer, customerId, region, region, region), + db.from(model) + .where(model.customerId).isNotNull() - .and(new Or(db, model) {{ - or(model.region).is("LA"); - or(model.region).is("CA"); - or(model.region).is("WA"); - }}) + .and(new Or(db, model) {{ + or(model.region).is("LA"); + or(model.region).is("CA"); + or(model.region).is("WA"); + }}) - .toSQL()); - } + .toSQL()); + } - @Test - public void compoundConditionsTest() { - final Customer c = new Customer(); - List matches = db.from(c) - .where(c.customerId).like("A%") - .and(c.region).isNotNull() - .and(new Or(db, c) {{ - or(c.region).is("LA"); - or(c.region).is("CA"); - }}).select(); + @Test + public void compoundConditionsTest() { + final Customer c = new Customer(); + List matches = db.from(c) + .where(c.customerId).like("A%") + .and(c.region).isNotNull() + .and(new Or(db, c) {{ + or(c.region).is("LA"); + or(c.region).is("CA"); + }}).select(); - assertEquals(2, matches.size()); + assertEquals(2, matches.size()); - Set ids = new TreeSet(); - for (Customer customer : matches) { - ids.add(customer.customerId); - } - assertEquals("[ANTON, ASLAN]", ids.toString()); + Set ids = new TreeSet(); + for (Customer customer : matches) { + ids.add(customer.customerId); + } + assertEquals("[ANTON, ASLAN]", ids.toString()); - } + } } diff --git a/src/test/java/com/iciql/test/OneOfTest.java b/src/test/java/com/iciql/test/OneOfTest.java index c4aa90b..c5d92f9 100644 --- a/src/test/java/com/iciql/test/OneOfTest.java +++ b/src/test/java/com/iciql/test/OneOfTest.java @@ -17,132 +17,131 @@ package com.iciql.test; -import static org.junit.Assert.assertEquals; +import com.iciql.Db; +import com.iciql.test.models.Customer; +import com.iciql.test.models.PrimitivesModel; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.TreeSet; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.iciql.Db; -import com.iciql.test.models.Customer; -import com.iciql.test.models.PrimitivesModel; +import static org.junit.Assert.assertEquals; public class OneOfTest { - private Db db; - - @Before - public void setUp() { - db = IciqlSuite.openNewDb(); - db.insertAll(Customer.getList()); - } - - @After - public void tearDown() { - db.close(); - } - - @SuppressWarnings("serial") - @Test - public void oneOfSyntaxTest() { - String PrimitivesTest = db.getDialect().prepareTableName(null, "PrimitivesTest"); - String Customer = db.getDialect().prepareTableName(null, "Customer"); - String myInteger = db.getDialect().prepareColumnName("myInteger"); - String customerId = db.getDialect().prepareColumnName("customerId"); - - PrimitivesModel p = new PrimitivesModel(); - assertEquals( - String.format("SELECT * FROM %s WHERE %s IN(0)", PrimitivesTest, myInteger), - db.from(p) - .where(p.myInteger).oneOf(0) - .toSQL()); - assertEquals( - String.format("SELECT * FROM %s WHERE %s IN(0, 1)", PrimitivesTest, myInteger), - db.from(p) - .where(p.myInteger).oneOf(0, 1) - .toSQL()); - Customer c = new Customer(); - assertEquals( - String.format("SELECT * FROM %s WHERE %s IN('a')", Customer, customerId), - db.from(c) - .where(c.customerId).oneOf(new ArrayList() {{ - this.add("a"); - }}) - .toSQL()); - assertEquals( - String.format("SELECT * FROM %s WHERE %s IN('a', 'b')", Customer, customerId), - db.from(c) - .where(c.customerId).oneOf(new ArrayList() {{ - this.add("a"); - this.add("b"); - }}) - .toSQL()); - } - - @SuppressWarnings("serial") - @Test - public void noneOfSyntaxTest() { - String PrimitivesTest = db.getDialect().prepareTableName(null, "PrimitivesTest"); - String Customer = db.getDialect().prepareTableName(null, "Customer"); - String myInteger = db.getDialect().prepareColumnName("myInteger"); - String customerId = db.getDialect().prepareColumnName("customerId"); - - PrimitivesModel p = new PrimitivesModel(); - assertEquals( - String.format("SELECT * FROM %s WHERE %s NOT IN(0)", PrimitivesTest, myInteger), - db.from(p) - .where(p.myInteger).noneOf(0) - .toSQL()); - assertEquals( - String.format("SELECT * FROM %s WHERE %s NOT IN(0, 1)", PrimitivesTest, myInteger), - db.from(p) - .where(p.myInteger).noneOf(0, 1) - .toSQL()); - Customer c = new Customer(); - assertEquals( - String.format("SELECT * FROM %s WHERE %s NOT IN('a')", Customer, customerId), - db.from(c) - .where(c.customerId).noneOf(new ArrayList() {{ - this.add("a"); - }}) - .toSQL()); - assertEquals( - String.format("SELECT * FROM %s WHERE %s NOT IN('a', 'b')", Customer, customerId), - db.from(c) - .where(c.customerId).noneOf(new ArrayList() {{ - this.add("a"); - this.add("b"); - }}) - .toSQL()); - } - - public void noneOfTest() { - Customer c = new Customer(); - List meAndny = db.from(c).where(c.region).noneOf("WA", "CA", "LA").select(); - assertEquals(2, meAndny.size()); - - Set regions = new TreeSet(); - for (Customer customer : meAndny) { - regions.add(customer.region); - } - assertEquals("[ME, NY]", regions.toString()); - } - - public void oneOfTest() { - Customer c = new Customer(); - List meAndny = db.from(c).where(c.region).oneOf("ME", "NY").select(); - assertEquals(2, meAndny.size()); - - Set regions = new TreeSet(); - for (Customer customer : meAndny) { - regions.add(customer.region); - } - assertEquals("[ME, NY]", regions.toString()); - } + private Db db; + + @Before + public void setUp() { + db = IciqlSuite.openNewDb(); + db.insertAll(Customer.getList()); + } + + @After + public void tearDown() { + db.close(); + } + + @SuppressWarnings("serial") + @Test + public void oneOfSyntaxTest() { + String PrimitivesTest = db.getDialect().prepareTableName(null, "PrimitivesTest"); + String Customer = db.getDialect().prepareTableName(null, "Customer"); + String myInteger = db.getDialect().prepareColumnName("myInteger"); + String customerId = db.getDialect().prepareColumnName("customerId"); + + PrimitivesModel p = new PrimitivesModel(); + assertEquals( + String.format("SELECT * FROM %s WHERE %s IN(0)", PrimitivesTest, myInteger), + db.from(p) + .where(p.myInteger).oneOf(0) + .toSQL()); + assertEquals( + String.format("SELECT * FROM %s WHERE %s IN(0, 1)", PrimitivesTest, myInteger), + db.from(p) + .where(p.myInteger).oneOf(0, 1) + .toSQL()); + Customer c = new Customer(); + assertEquals( + String.format("SELECT * FROM %s WHERE %s IN('a')", Customer, customerId), + db.from(c) + .where(c.customerId).oneOf(new ArrayList() {{ + this.add("a"); + }}) + .toSQL()); + assertEquals( + String.format("SELECT * FROM %s WHERE %s IN('a', 'b')", Customer, customerId), + db.from(c) + .where(c.customerId).oneOf(new ArrayList() {{ + this.add("a"); + this.add("b"); + }}) + .toSQL()); + } + + @SuppressWarnings("serial") + @Test + public void noneOfSyntaxTest() { + String PrimitivesTest = db.getDialect().prepareTableName(null, "PrimitivesTest"); + String Customer = db.getDialect().prepareTableName(null, "Customer"); + String myInteger = db.getDialect().prepareColumnName("myInteger"); + String customerId = db.getDialect().prepareColumnName("customerId"); + + PrimitivesModel p = new PrimitivesModel(); + assertEquals( + String.format("SELECT * FROM %s WHERE %s NOT IN(0)", PrimitivesTest, myInteger), + db.from(p) + .where(p.myInteger).noneOf(0) + .toSQL()); + assertEquals( + String.format("SELECT * FROM %s WHERE %s NOT IN(0, 1)", PrimitivesTest, myInteger), + db.from(p) + .where(p.myInteger).noneOf(0, 1) + .toSQL()); + Customer c = new Customer(); + assertEquals( + String.format("SELECT * FROM %s WHERE %s NOT IN('a')", Customer, customerId), + db.from(c) + .where(c.customerId).noneOf(new ArrayList() {{ + this.add("a"); + }}) + .toSQL()); + assertEquals( + String.format("SELECT * FROM %s WHERE %s NOT IN('a', 'b')", Customer, customerId), + db.from(c) + .where(c.customerId).noneOf(new ArrayList() {{ + this.add("a"); + this.add("b"); + }}) + .toSQL()); + } + + public void noneOfTest() { + Customer c = new Customer(); + List meAndny = db.from(c).where(c.region).noneOf("WA", "CA", "LA").select(); + assertEquals(2, meAndny.size()); + + Set regions = new TreeSet(); + for (Customer customer : meAndny) { + regions.add(customer.region); + } + assertEquals("[ME, NY]", regions.toString()); + } + + public void oneOfTest() { + Customer c = new Customer(); + List meAndny = db.from(c).where(c.region).oneOf("ME", "NY").select(); + assertEquals(2, meAndny.size()); + + Set regions = new TreeSet(); + for (Customer customer : meAndny) { + regions.add(customer.region); + } + assertEquals("[ME, NY]", regions.toString()); + } } diff --git a/src/test/java/com/iciql/test/PrimitivesTest.java b/src/test/java/com/iciql/test/PrimitivesTest.java index 6250649..49595a2 100644 --- a/src/test/java/com/iciql/test/PrimitivesTest.java +++ b/src/test/java/com/iciql/test/PrimitivesTest.java @@ -16,103 +16,102 @@ package com.iciql.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.Collections; -import java.util.List; - -import org.junit.Test; - import com.iciql.Db; import com.iciql.IciqlException; import com.iciql.test.models.MultipleBoolsModel; import com.iciql.test.models.PrimitivesModel; +import org.junit.Test; + +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Tests primitives with autoboxing within the framework. */ public class PrimitivesTest { - @Test - public void testPrimitives() { - Db db = IciqlSuite.openNewDb(); - - // insert random models in reverse order - List models = PrimitivesModel.getList(); - PrimitivesModel model = models.get(0); - Collections.reverse(models); - // insert them in reverse order - db.insertAll(models); - - PrimitivesModel p = new PrimitivesModel(); - - // retrieve model and compare - PrimitivesModel retrievedModel = db.from(p).orderBy(p.myLong).selectFirst(); - assertTrue(model.equivalentTo(retrievedModel)); - - retrievedModel = db.from(p).where("mylong = ? and myinteger = ?", model.myLong, model.myInteger) - .selectFirst(); - assertTrue(model.equivalentTo(retrievedModel)); - - // retrieve with conditions and compare - retrievedModel = db.from(p).where(p.myLong).is(model.myLong).and(p.myInteger).is(model.myInteger) - .selectFirst(); - assertTrue(model.equivalentTo(retrievedModel)); - - // set myInteger & myDouble - db.from(p).set(p.myInteger).to(10).set(p.myDouble).to(3.0d).where(p.myLong).is(model.myLong).update(); - retrievedModel = db.from(p).orderBy(p.myLong).selectFirst(); - - assertEquals(10, retrievedModel.myInteger); - assertEquals(3d, retrievedModel.myDouble, 0.001d); - - // increment my double by pi - db.from(p).increment(p.myDouble).by(3.14d).update(); - retrievedModel = db.from(p).orderBy(p.myLong).selectFirst(); - assertEquals(6.14d, retrievedModel.myDouble, 0.001d); - - // test order by - List list = db.from(p).orderBy(p.myLong).select(); - assertEquals(models.size(), list.size()); - assertEquals("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", list.toString()); - - // test model update - retrievedModel.myInteger = 1337; - assertTrue(db.update(retrievedModel)); - assertTrue(db.delete(retrievedModel)); - - db.close(); - } - - @Test - public void testMultipleBooleans() { - Db db = IciqlSuite.openNewDb(); - db.insertAll(MultipleBoolsModel.getList()); - - MultipleBoolsModel m = new MultipleBoolsModel(); - try { - db.from(m).where(m.a).is(true).select(); - assertTrue(false); - } catch (IciqlException e) { - assertTrue(true); - } - db.close(); - } - - @Test - public void testPrimitiveColumnSelection() { - Db db = IciqlSuite.openNewDb(); - - // insert random models in reverse order - List models = PrimitivesModel.getList(); - Collections.reverse(models); - // insert them in reverse order - db.insertAll(models); - - PrimitivesModel p = new PrimitivesModel(); - List list = db.from(p).orderByDesc(p.myLong).select(p.myLong); - assertEquals(models.size(), list.size()); - assertEquals("[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]", list.toString()); - } + @Test + public void testPrimitives() { + Db db = IciqlSuite.openNewDb(); + + // insert random models in reverse order + List models = PrimitivesModel.getList(); + PrimitivesModel model = models.get(0); + Collections.reverse(models); + // insert them in reverse order + db.insertAll(models); + + PrimitivesModel p = new PrimitivesModel(); + + // retrieve model and compare + PrimitivesModel retrievedModel = db.from(p).orderBy(p.myLong).selectFirst(); + assertTrue(model.equivalentTo(retrievedModel)); + + retrievedModel = db.from(p).where("mylong = ? and myinteger = ?", model.myLong, model.myInteger) + .selectFirst(); + assertTrue(model.equivalentTo(retrievedModel)); + + // retrieve with conditions and compare + retrievedModel = db.from(p).where(p.myLong).is(model.myLong).and(p.myInteger).is(model.myInteger) + .selectFirst(); + assertTrue(model.equivalentTo(retrievedModel)); + + // set myInteger & myDouble + db.from(p).set(p.myInteger).to(10).set(p.myDouble).to(3.0d).where(p.myLong).is(model.myLong).update(); + retrievedModel = db.from(p).orderBy(p.myLong).selectFirst(); + + assertEquals(10, retrievedModel.myInteger); + assertEquals(3d, retrievedModel.myDouble, 0.001d); + + // increment my double by pi + db.from(p).increment(p.myDouble).by(3.14d).update(); + retrievedModel = db.from(p).orderBy(p.myLong).selectFirst(); + assertEquals(6.14d, retrievedModel.myDouble, 0.001d); + + // test order by + List list = db.from(p).orderBy(p.myLong).select(); + assertEquals(models.size(), list.size()); + assertEquals("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", list.toString()); + + // test model update + retrievedModel.myInteger = 1337; + assertTrue(db.update(retrievedModel)); + assertTrue(db.delete(retrievedModel)); + + db.close(); + } + + @Test + public void testMultipleBooleans() { + Db db = IciqlSuite.openNewDb(); + db.insertAll(MultipleBoolsModel.getList()); + + MultipleBoolsModel m = new MultipleBoolsModel(); + try { + db.from(m).where(m.a).is(true).select(); + assertTrue(false); + } catch (IciqlException e) { + assertTrue(true); + } + db.close(); + } + + @Test + public void testPrimitiveColumnSelection() { + Db db = IciqlSuite.openNewDb(); + + // insert random models in reverse order + List models = PrimitivesModel.getList(); + Collections.reverse(models); + // insert them in reverse order + db.insertAll(models); + + PrimitivesModel p = new PrimitivesModel(); + List list = db.from(p).orderByDesc(p.myLong).select(p.myLong); + assertEquals(models.size(), list.size()); + assertEquals("[10, 9, 8, 7, 6, 5, 4, 3, 2, 1]", list.toString()); + } } diff --git a/src/test/java/com/iciql/test/ProductDaoTest.java b/src/test/java/com/iciql/test/ProductDaoTest.java index d8eac72..301753a 100644 --- a/src/test/java/com/iciql/test/ProductDaoTest.java +++ b/src/test/java/com/iciql/test/ProductDaoTest.java @@ -16,15 +16,6 @@ package com.iciql.test; -import java.sql.Date; -import java.util.Arrays; -import java.util.List; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - import com.iciql.Dao; import com.iciql.DaoClasspathStatementProvider; import com.iciql.Db; @@ -35,6 +26,14 @@ import com.iciql.test.DataTypeAdapterTest.SupportedTypesAdapter; import com.iciql.test.models.Order; import com.iciql.test.models.Product; import com.iciql.test.models.SupportedTypes; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.sql.Date; +import java.util.Arrays; +import java.util.List; /** * Tests DAO dynamic proxy mechanism. @@ -43,373 +42,373 @@ import com.iciql.test.models.SupportedTypes; */ public class ProductDaoTest extends Assert { - private Db db; + private Db db; - @Before - public void setUp() throws Exception { - db = IciqlSuite.openNewDb(); - db.insertAll(Product.getList()); - db.insertAll(Order.getList()); - db.setDaoStatementProvider(new DaoClasspathStatementProvider()); - } + @Before + public void setUp() throws Exception { + db = IciqlSuite.openNewDb(); + db.insertAll(Product.getList()); + db.insertAll(Order.getList()); + db.setDaoStatementProvider(new DaoClasspathStatementProvider()); + } - @After - public void tearDown() { - db.close(); - } + @After + public void tearDown() { + db.close(); + } - @Test - public void testQueryVoidReturnType() { + @Test + public void testQueryVoidReturnType() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - try { - dao.getWithIllegalVoid(); - assertTrue("void return type on a query should fail", false); - } catch (IciqlException e) { - assertTrue(true); - } - } + try { + dao.getWithIllegalVoid(); + assertTrue("void return type on a query should fail", false); + } catch (IciqlException e) { + assertTrue(true); + } + } - @Test - public void testQueryCollectionReturnType() { + @Test + public void testQueryCollectionReturnType() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - try { - dao.getWithIllegalCollection(); - assertTrue("collection return types on a query should fail", false); - } catch (IciqlException e) { - assertTrue(true); - } - } + try { + dao.getWithIllegalCollection(); + assertTrue("collection return types on a query should fail", false); + } catch (IciqlException e) { + assertTrue(true); + } + } - @Test - public void testQueryIgnoreDoubleDelimiter() { + @Test + public void testQueryIgnoreDoubleDelimiter() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - try { - dao.getWithDoubleDelimiter(); - assertTrue("the double delimiter should have been ignored", false); - } catch (IciqlException e) { - assertTrue(true); - } + try { + dao.getWithDoubleDelimiter(); + assertTrue("the double delimiter should have been ignored", false); + } catch (IciqlException e) { + assertTrue(true); + } - } + } - @Test - public void testQueryReturnModels() { + @Test + public void testQueryReturnModels() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - Product[] products = dao.getAllProducts(); - assertEquals(10, products.length); - } + Product[] products = dao.getAllProducts(); + assertEquals(10, products.length); + } - @Test - public void testQueryNamedOrIndexedParameterBinding() { + @Test + public void testQueryNamedOrIndexedParameterBinding() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - Product p2 = dao.getProduct(2); - assertEquals("Chang", p2.productName); + Product p2 = dao.getProduct(2); + assertEquals("Chang", p2.productName); - Product p3 = dao.getProductWithUnusedBoundParameters(true, 3, "test"); - assertEquals("Aniseed Syrup", p3.productName); + Product p3 = dao.getProductWithUnusedBoundParameters(true, 3, "test"); + assertEquals("Aniseed Syrup", p3.productName); - Product p4 = dao.getProductWithUnboundParameters(true, 4, "test"); - assertEquals("Chef Anton's Cajun Seasoning", p4.productName); + Product p4 = dao.getProductWithUnboundParameters(true, 4, "test"); + assertEquals("Chef Anton's Cajun Seasoning", p4.productName); - Product p5 = dao.getProductWithUnboundParameters(true, 5, "test"); - assertEquals("Chef Anton's Gumbo Mix", p5.productName); + Product p5 = dao.getProductWithUnboundParameters(true, 5, "test"); + assertEquals("Chef Anton's Gumbo Mix", p5.productName); - // test re-use of IndexedSql (manual check with debugger) - Product p6 = dao.getProduct(6); - assertEquals("Grandma's Boysenberry Spread", p6.productName); + // test re-use of IndexedSql (manual check with debugger) + Product p6 = dao.getProduct(6); + assertEquals("Grandma's Boysenberry Spread", p6.productName); - } + } - @Test - public void testJDBCPlaceholderParameterBinding() { + @Test + public void testJDBCPlaceholderParameterBinding() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - Product p2 = dao.getProductWithJDBCPlaceholders(2); - assertEquals("Chang", p2.productName); + Product p2 = dao.getProductWithJDBCPlaceholders(2); + assertEquals("Chang", p2.productName); - } + } - @Test - public void testQueryBeanBinding() { + @Test + public void testQueryBeanBinding() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - Product p4 = dao.getProduct(4); + Product p4 = dao.getProduct(4); - long [] products = dao.getSimilarInStockItemIds(p4); + long[] products = dao.getSimilarInStockItemIds(p4); - assertEquals("[6]", Arrays.toString(products)); + assertEquals("[6]", Arrays.toString(products)); - } + } - @Test - public void testQueryReturnField() { + @Test + public void testQueryReturnField() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - String n5 = dao.getProductName(5); - assertEquals("Chef Anton's Gumbo Mix", n5); + String n5 = dao.getProductName(5); + assertEquals("Chef Anton's Gumbo Mix", n5); - int u4 = dao.getUnitsInStock(4); - assertEquals(53, u4); + int u4 = dao.getUnitsInStock(4); + assertEquals(53, u4); - } + } - @Test - public void testQueryReturnFields() { + @Test + public void testQueryReturnFields() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - long [] ids = dao.getProductIdsForCategory("Condiments"); - assertEquals("[3, 4, 5, 6, 8]", Arrays.toString(ids)); + long[] ids = dao.getProductIdsForCategory("Condiments"); + assertEquals("[3, 4, 5, 6, 8]", Arrays.toString(ids)); - Date date = dao.getMostRecentOrder(); - assertEquals("2007-04-11", date.toString()); + Date date = dao.getMostRecentOrder(); + assertEquals("2007-04-11", date.toString()); - } + } - @Test - public void testUpdateIllegalReturnType() { + @Test + public void testUpdateIllegalReturnType() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - try { - dao.setWithIllegalReturnType(); - assertTrue("this should have been an illegal return type", false); - } catch (IciqlException e) { - assertTrue(true); - } + try { + dao.setWithIllegalReturnType(); + assertTrue("this should have been an illegal return type", false); + } catch (IciqlException e) { + assertTrue(true); + } - } + } - @Test - public void testUpdateStatements() { + @Test + public void testUpdateStatements() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - Product p1 = dao.getProduct(1); - assertEquals("Chai", p1.productName); + Product p1 = dao.getProduct(1); + assertEquals("Chai", p1.productName); - String name = "Tea"; - dao.setProductName(1, name); + String name = "Tea"; + dao.setProductName(1, name); - Product p2 = dao.getProduct(1); + Product p2 = dao.getProduct(1); - assertEquals(name, p2.productName); + assertEquals(name, p2.productName); - } + } - @Test - public void testUpdateStatementsReturnsSuccess() { + @Test + public void testUpdateStatementsReturnsSuccess() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - boolean success = dao.setProductNameReturnsSuccess(1, "Tea"); - assertTrue(success); + boolean success = dao.setProductNameReturnsSuccess(1, "Tea"); + assertTrue(success); - } + } - @Test - public void testUpdateStatementsReturnsCount() { + @Test + public void testUpdateStatementsReturnsCount() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - int rows = dao.renameProductCategoryReturnsCount("Condiments", "Garnishes"); - assertEquals(5, rows); + int rows = dao.renameProductCategoryReturnsCount("Condiments", "Garnishes"); + assertEquals(5, rows); - } + } - @Test - public void testQueryWithDataTypeAdapter() { + @Test + public void testQueryWithDataTypeAdapter() { - final SupportedTypes obj0 = SupportedTypes.createList().get(1); + final SupportedTypes obj0 = SupportedTypes.createList().get(1); - // insert our custom serialized object - SerializedObjectTypeAdapterTest row = new SerializedObjectTypeAdapterTest(); - row.received = new java.util.Date(); - row.obj = obj0; - final long id = db.insertAndGetKey(row); + // insert our custom serialized object + SerializedObjectTypeAdapterTest row = new SerializedObjectTypeAdapterTest(); + row.received = new java.util.Date(); + row.obj = obj0; + final long id = db.insertAndGetKey(row); - SerializedObjectTypeAdapterTest row1 = db.from(row).selectFirst(); + SerializedObjectTypeAdapterTest row1 = db.from(row).selectFirst(); - // validate retrieved object - final SupportedTypes obj1a = row1.obj; - assertNotNull(obj1a); - assertTrue(obj0.equivalentTo(obj1a)); + // validate retrieved object + final SupportedTypes obj1a = row1.obj; + assertNotNull(obj1a); + assertTrue(obj0.equivalentTo(obj1a)); - // validate the primary keys match - assertEquals("The returned primary key should match the object primary key", id, row1.id); + // validate the primary keys match + assertEquals("The returned primary key should match the object primary key", id, row1.id); - // retrieve our object with automatic data type conversion - ProductDao dao = db.open(ProductDao.class); - final SupportedTypes obj1 = dao.getCustomDataType(id); - assertNotNull(obj1); - assertTrue(obj0.equivalentTo(obj1)); - } + // retrieve our object with automatic data type conversion + ProductDao dao = db.open(ProductDao.class); + final SupportedTypes obj1 = dao.getCustomDataType(id); + assertNotNull(obj1); + assertTrue(obj0.equivalentTo(obj1)); + } - @Test - public void testUpdateWithDataTypeAdapter() { + @Test + public void testUpdateWithDataTypeAdapter() { - final SupportedTypes obj0 = SupportedTypes.createList().get(1); + final SupportedTypes obj0 = SupportedTypes.createList().get(1); - // insert our custom serialized object - SerializedObjectTypeAdapterTest row = new SerializedObjectTypeAdapterTest(); - row.received = new java.util.Date(); - row.obj = obj0; + // insert our custom serialized object + SerializedObjectTypeAdapterTest row = new SerializedObjectTypeAdapterTest(); + row.received = new java.util.Date(); + row.obj = obj0; - final long id = db.insertAndGetKey(row); + final long id = db.insertAndGetKey(row); - SerializedObjectTypeAdapterTest row1 = db.from(row).selectFirst(); + SerializedObjectTypeAdapterTest row1 = db.from(row).selectFirst(); - // validate retrieved object - final SupportedTypes obj1a = row1.obj; - assertNotNull(obj1a); - assertTrue(obj0.equivalentTo(obj1a)); + // validate retrieved object + final SupportedTypes obj1a = row1.obj; + assertNotNull(obj1a); + assertTrue(obj0.equivalentTo(obj1a)); - // validate the primary keys match - assertEquals("The returned primary key should match the object primary key", id, row1.id); + // validate the primary keys match + assertEquals("The returned primary key should match the object primary key", id, row1.id); - // validate the alternate retrieved object is equivalent - ProductDao dao = db.open(ProductDao.class); - final SupportedTypes obj1b = dao.getCustomDataType(id); - assertNotNull(obj1b); - assertTrue(obj1a.equivalentTo(obj1b)); + // validate the alternate retrieved object is equivalent + ProductDao dao = db.open(ProductDao.class); + final SupportedTypes obj1b = dao.getCustomDataType(id); + assertNotNull(obj1b); + assertTrue(obj1a.equivalentTo(obj1b)); - // update the stored object - obj1b.myString = "dta update successful"; - dao.setSupportedTypes(id, obj1b); + // update the stored object + obj1b.myString = "dta update successful"; + dao.setSupportedTypes(id, obj1b); - // confirm the update took place - final SupportedTypes obj2 = dao.getCustomDataType(id); - assertNotNull(obj2); - assertEquals("dta update successful", obj2.myString); - assertTrue(obj1b.equivalentTo(obj2)); - } + // confirm the update took place + final SupportedTypes obj2 = dao.getCustomDataType(id); + assertNotNull(obj2); + assertEquals("dta update successful", obj2.myString); + assertTrue(obj1b.equivalentTo(obj2)); + } - @Test - public void testDefaultProdResourceQueryReturnModels() { + @Test + public void testDefaultProdResourceQueryReturnModels() { - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - Product[] products = dao.getProductsFromResourceQuery(); - assertEquals(10, products.length); - } + Product[] products = dao.getProductsFromResourceQuery(); + assertEquals(10, products.length); + } - @Test - public void testDevResourceQueryReturnModels() { + @Test + public void testDevResourceQueryReturnModels() { - Db db = IciqlSuite.openNewDb(Mode.DEV); - db.insertAll(Product.getList()); - db.insertAll(Order.getList()); - db.setDaoStatementProvider(new DaoClasspathStatementProvider()); + Db db = IciqlSuite.openNewDb(Mode.DEV); + db.insertAll(Product.getList()); + db.insertAll(Order.getList()); + db.setDaoStatementProvider(new DaoClasspathStatementProvider()); - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - Product[] products = dao.getProductsFromResourceQuery(); - assertEquals(5, products.length); + Product[] products = dao.getProductsFromResourceQuery(); + assertEquals(5, products.length); - db.close(); - } + db.close(); + } - @Test - public void testTestResourceQueryReturnModels() { + @Test + public void testTestResourceQueryReturnModels() { - Db db = IciqlSuite.openNewDb(Mode.TEST); - db.insertAll(Product.getList()); - db.insertAll(Order.getList()); - db.setDaoStatementProvider(new DaoClasspathStatementProvider()); + Db db = IciqlSuite.openNewDb(Mode.TEST); + db.insertAll(Product.getList()); + db.insertAll(Order.getList()); + db.setDaoStatementProvider(new DaoClasspathStatementProvider()); - ProductDao dao = db.open(ProductDao.class); + ProductDao dao = db.open(ProductDao.class); - Product[] products = dao.getProductsFromResourceQuery(); - assertEquals(2, products.length); + Product[] products = dao.getProductsFromResourceQuery(); + assertEquals(2, products.length); - db.close(); - } + db.close(); + } - /** - * Define the Product DAO interface. - */ - public interface ProductDao extends Dao { + /** + * Define the Product DAO interface. + */ + public interface ProductDao extends Dao { - @SqlQuery("select * from Product") - void getWithIllegalVoid(); + @SqlQuery("select * from Product") + void getWithIllegalVoid(); - @SqlQuery("select * from Product") - List getWithIllegalCollection(); + @SqlQuery("select * from Product") + List getWithIllegalCollection(); - @SqlQuery("select * from Product where ::id = 1") - Product getWithDoubleDelimiter(); + @SqlQuery("select * from Product where ::id = 1") + Product getWithDoubleDelimiter(); - @SqlQuery("select * from Product") - Product[] getAllProducts(); + @SqlQuery("select * from Product") + Product[] getAllProducts(); - @SqlQuery("select * from Product where productId = :id") - Product getProduct(@Bind("id") long id); + @SqlQuery("select * from Product where productId = :id") + Product getProduct(@Bind("id") long id); - @SqlQuery("select * from Product where productId = :id") - Product getProductWithUnusedBoundParameters( - @Bind("irrelevant") boolean whocares, - @Bind("id") long id, - @Bind("dontmatter") String something); + @SqlQuery("select * from Product where productId = :id") + Product getProductWithUnusedBoundParameters( + @Bind("irrelevant") boolean whocares, + @Bind("id") long id, + @Bind("dontmatter") String something); - @SqlQuery("select * from Product where productId = :arg1") - Product getProductWithUnboundParameters( - boolean whocares, - long id, - String something); + @SqlQuery("select * from Product where productId = :arg1") + Product getProductWithUnboundParameters( + boolean whocares, + long id, + String something); - @SqlQuery("select * from Product where productId = :?") - Product getProductWithJDBCPlaceholders(long id); + @SqlQuery("select * from Product where productId = :?") + Product getProductWithJDBCPlaceholders(long id); - @SqlQuery("select productId from Product where unitsInStock > :p.unitsInStock and category = :p.category") - long[] getSimilarInStockItemIds(@BindBean("p") Product p); + @SqlQuery("select productId from Product where unitsInStock > :p.unitsInStock and category = :p.category") + long[] getSimilarInStockItemIds(@BindBean("p") Product p); - @SqlQuery("select productName from Product where productId = :?") - String getProductName(long id); + @SqlQuery("select productName from Product where productId = :?") + String getProductName(long id); - @SqlQuery("select unitsInStock from Product where productId = :?") - int getUnitsInStock(long id); + @SqlQuery("select unitsInStock from Product where productId = :?") + int getUnitsInStock(long id); - @SqlQuery("select productId from Product where category = :category") - long[] getProductIdsForCategory(@Bind("category") String cat); + @SqlQuery("select productId from Product where category = :category") + long[] getProductIdsForCategory(@Bind("category") String cat); - // will break ResultSet iteration after retrieving first value - @SqlQuery("select orderDate from Orders order by orderDate desc") - Date getMostRecentOrder(); + // will break ResultSet iteration after retrieving first value + @SqlQuery("select orderDate from Orders order by orderDate desc") + Date getMostRecentOrder(); - @SqlStatement("update Product set productName = 'test' where productId = 1") - String setWithIllegalReturnType(); + @SqlStatement("update Product set productName = 'test' where productId = 1") + String setWithIllegalReturnType(); - @SqlStatement("update Product set productName = :name where productId = :id") - void setProductName(@Bind("id") long id, @Bind("name") String name); + @SqlStatement("update Product set productName = :name where productId = :id") + void setProductName(@Bind("id") long id, @Bind("name") String name); - @SqlStatement("update Product set productName = :name where productId = :id") - boolean setProductNameReturnsSuccess(@Bind("id") long id, @Bind("name") String name); + @SqlStatement("update Product set productName = :name where productId = :id") + boolean setProductNameReturnsSuccess(@Bind("id") long id, @Bind("name") String name); - @SqlStatement("update Product set category = :newCategory where category = :oldCategory") - int renameProductCategoryReturnsCount(@Bind("oldCategory") String oldCategory, @Bind("newCategory") String newCategory); + @SqlStatement("update Product set category = :newCategory where category = :oldCategory") + int renameProductCategoryReturnsCount(@Bind("oldCategory") String oldCategory, @Bind("newCategory") String newCategory); - @SqlQuery("select obj from dataTypeAdapters where id=:1") - @SupportedTypesAdapter - SupportedTypes getCustomDataType(long id); + @SqlQuery("select obj from dataTypeAdapters where id=:1") + @SupportedTypesAdapter + SupportedTypes getCustomDataType(long id); - @SqlStatement("update dataTypeAdapters set obj=:2 where id=:1") - boolean setSupportedTypes(long id, @SupportedTypesAdapter SupportedTypes obj); + @SqlStatement("update dataTypeAdapters set obj=:2 where id=:1") + boolean setSupportedTypes(long id, @SupportedTypesAdapter SupportedTypes obj); - @SqlQuery("get.products") - Product[] getProductsFromResourceQuery(); + @SqlQuery("get.products") + Product[] getProductsFromResourceQuery(); - } + } } diff --git a/src/test/java/com/iciql/test/RuntimeQueryTest.java b/src/test/java/com/iciql/test/RuntimeQueryTest.java index 8220d7f..8a5bc5b 100644 --- a/src/test/java/com/iciql/test/RuntimeQueryTest.java +++ b/src/test/java/com/iciql/test/RuntimeQueryTest.java @@ -15,17 +15,6 @@ */ package com.iciql.test; -import static org.junit.Assert.assertEquals; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.text.MessageFormat; -import java.text.SimpleDateFormat; -import java.util.List; - -import org.junit.Assume; -import org.junit.Test; - import com.iciql.Db; import com.iciql.QueryWhere; import com.iciql.test.models.EnumModels.Tree; @@ -33,182 +22,192 @@ import com.iciql.test.models.Product; import com.iciql.test.models.StaticQueries; import com.iciql.util.JdbcUtils; import com.iciql.util.Utils; +import org.junit.Assume; +import org.junit.Test; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.List; + +import static org.junit.Assert.assertEquals; /** * Tests the runtime dynamic query function. */ public class RuntimeQueryTest { - @Test - public void testParameters() { - Db db = IciqlSuite.openNewDb(); + @Test + public void testParameters() { + Db db = IciqlSuite.openNewDb(); - // do not test non-H2 databases because dialects will get in the way - // e.g. column quoting, etc - Assume.assumeTrue(IciqlSuite.isH2(db)); + // do not test non-H2 databases because dialects will get in the way + // e.g. column quoting, etc + Assume.assumeTrue(IciqlSuite.isH2(db)); - Product p = new Product(); - String q1 = db.from(p).where(p.unitsInStock).isParameter().and(p.productName).likeParameter().orderBy(p.productId).toSQL(); - String q2 = db.from(p).where(p.unitsInStock).lessThan(100).and(p.productName).like("test").or(p.productName).likeParameter().orderBy(p.productId).toSQL(); + Product p = new Product(); + String q1 = db.from(p).where(p.unitsInStock).isParameter().and(p.productName).likeParameter().orderBy(p.productId).toSQL(); + String q2 = db.from(p).where(p.unitsInStock).lessThan(100).and(p.productName).like("test").or(p.productName).likeParameter().orderBy(p.productId).toSQL(); - StaticQueries.StaticModel1 m1 = new StaticQueries.StaticModel1(); - String q3 = db.from(m1).where(m1.myTree).is(Tree.MAPLE).and(m1.myTree).isParameter().toSQL(); + StaticQueries.StaticModel1 m1 = new StaticQueries.StaticModel1(); + String q3 = db.from(m1).where(m1.myTree).is(Tree.MAPLE).and(m1.myTree).isParameter().toSQL(); - StaticQueries.StaticModel2 m2 = new StaticQueries.StaticModel2(); - String q4 = db.from(m2).where(m2.myTree).is(Tree.MAPLE).and(m2.myTree).isParameter().toSQL(); + StaticQueries.StaticModel2 m2 = new StaticQueries.StaticModel2(); + String q4 = db.from(m2).where(m2.myTree).is(Tree.MAPLE).and(m2.myTree).isParameter().toSQL(); - StaticQueries.StaticModel3 m3 = new StaticQueries.StaticModel3(); - String q5 = db.from(m3).where(m3.myTree).is(Tree.MAPLE).and(m3.myTree).isParameter().toSQL(); + StaticQueries.StaticModel3 m3 = new StaticQueries.StaticModel3(); + String q5 = db.from(m3).where(m3.myTree).is(Tree.MAPLE).and(m3.myTree).isParameter().toSQL(); - long now = System.currentTimeMillis(); - java.sql.Date aDate = new java.sql.Date(now); - java.sql.Time aTime = new java.sql.Time(now); - java.sql.Timestamp aTimestamp = new java.sql.Timestamp(now); + long now = System.currentTimeMillis(); + java.sql.Date aDate = new java.sql.Date(now); + java.sql.Time aTime = new java.sql.Time(now); + java.sql.Timestamp aTimestamp = new java.sql.Timestamp(now); - String q6 = db.from(m1).where(m1.myDate).is(aDate).and(m1.myDate).isParameter().toSQL(); - String q7 = db.from(m1).where(m1.myTime).is(aTime).and(m1.myTime).isParameter().toSQL(); - String q8 = db.from(m1).where(m1.myTimestamp).is(aTimestamp).and(m1.myTimestamp).isParameter().toSQL(); + String q6 = db.from(m1).where(m1.myDate).is(aDate).and(m1.myDate).isParameter().toSQL(); + String q7 = db.from(m1).where(m1.myTime).is(aTime).and(m1.myTime).isParameter().toSQL(); + String q8 = db.from(m1).where(m1.myTimestamp).is(aTimestamp).and(m1.myTimestamp).isParameter().toSQL(); - db.close(); - assertEquals("SELECT * FROM Product WHERE unitsInStock = ? AND productName LIKE ? ORDER BY productId", q1); - assertEquals("SELECT * FROM Product WHERE unitsInStock < 100 AND productName LIKE 'test' OR productName LIKE ? ORDER BY productId", q2); + db.close(); + assertEquals("SELECT * FROM Product WHERE unitsInStock = ? AND productName LIKE ? ORDER BY productId", q1); + assertEquals("SELECT * FROM Product WHERE unitsInStock < 100 AND productName LIKE 'test' OR productName LIKE ? ORDER BY productId", q2); - assertEquals("SELECT * FROM StaticQueryTest1 WHERE myTree = 'MAPLE' AND myTree = ?", q3); - assertEquals("SELECT * FROM StaticQueryTest2 WHERE myTree = 50 AND myTree = ?", q4); - assertEquals("SELECT * FROM StaticQueryTest3 WHERE myTree = 4 AND myTree = ?", q5); + assertEquals("SELECT * FROM StaticQueryTest1 WHERE myTree = 'MAPLE' AND myTree = ?", q3); + assertEquals("SELECT * FROM StaticQueryTest2 WHERE myTree = 50 AND myTree = ?", q4); + assertEquals("SELECT * FROM StaticQueryTest3 WHERE myTree = 4 AND myTree = ?", q5); - java.util.Date refDate = new java.util.Date(now); - assertEquals("SELECT * FROM StaticQueryTest1 WHERE myDate = '" + new SimpleDateFormat("yyyy-MM-dd").format(refDate) + "' AND myDate = ?", q6); - assertEquals("SELECT * FROM StaticQueryTest1 WHERE myTime = '" + new SimpleDateFormat("HH:mm:ss").format(refDate) + "' AND myTime = ?", q7); - assertEquals("SELECT * FROM StaticQueryTest1 WHERE myTimestamp = '" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(refDate) + "' AND myTimestamp = ?", q8); - } + java.util.Date refDate = new java.util.Date(now); + assertEquals("SELECT * FROM StaticQueryTest1 WHERE myDate = '" + new SimpleDateFormat("yyyy-MM-dd").format(refDate) + "' AND myDate = ?", q6); + assertEquals("SELECT * FROM StaticQueryTest1 WHERE myTime = '" + new SimpleDateFormat("HH:mm:ss").format(refDate) + "' AND myTime = ?", q7); + assertEquals("SELECT * FROM StaticQueryTest1 WHERE myTimestamp = '" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(refDate) + "' AND myTimestamp = ?", q8); + } - @Test - public void testRuntimeSet() { - Db db = IciqlSuite.openNewDb(); + @Test + public void testRuntimeSet() { + Db db = IciqlSuite.openNewDb(); - // do not test non-H2 databases because dialects will get in the way - // e.g. column quoting, etc - Assume.assumeTrue(IciqlSuite.isH2(db)); + // do not test non-H2 databases because dialects will get in the way + // e.g. column quoting, etc + Assume.assumeTrue(IciqlSuite.isH2(db)); - StaticQueries.StaticModel1 m = new StaticQueries.StaticModel1(); - String q = db.from(m).set(m.myTimestamp).toParameter().where(m.id).isParameter().toSQL(); - db.close(); + StaticQueries.StaticModel1 m = new StaticQueries.StaticModel1(); + String q = db.from(m).set(m.myTimestamp).toParameter().where(m.id).isParameter().toSQL(); + db.close(); - assertEquals("UPDATE StaticQueryTest1 SET myTimestamp = ? WHERE id = ?", q); - } + assertEquals("UPDATE StaticQueryTest1 SET myTimestamp = ? WHERE id = ?", q); + } - @Test - public void testRuntimeSelectWildcards() { - Db db = IciqlSuite.openNewDb(); + @Test + public void testRuntimeSelectWildcards() { + Db db = IciqlSuite.openNewDb(); - // do not test non-H2 databases because dialects will get in the way - // e.g. column quoting, etc - Assume.assumeTrue(IciqlSuite.isH2(db)); + // do not test non-H2 databases because dialects will get in the way + // e.g. column quoting, etc + Assume.assumeTrue(IciqlSuite.isH2(db)); - StaticQueries.StaticModel1 m1 = new StaticQueries.StaticModel1(); - StaticQueries.StaticModel2 m2 = new StaticQueries.StaticModel2(); - StaticQueries.StaticModel2 m3 = new StaticQueries.StaticModel2(); + StaticQueries.StaticModel1 m1 = new StaticQueries.StaticModel1(); + StaticQueries.StaticModel2 m2 = new StaticQueries.StaticModel2(); + StaticQueries.StaticModel2 m3 = new StaticQueries.StaticModel2(); - int t0 = Utils.AS_COUNTER.get() + 1; - int t1 = t0 + 1; + int t0 = Utils.AS_COUNTER.get() + 1; + int t1 = t0 + 1; - QueryWhere where = db.from(m1).innerJoin(m2).on(m1.id).is(m2.id).where(m2.myTree).is(Tree.MAPLE); - String q1 = where.toSQL(false); - String q2 = where.toSQL(true); - String q3 = where.toSQL(false, m1); - String q4 = where.toSQL(true, m1); - String q5 = where.toSQL(false, m2); - String q6 = where.toSQL(true, m2); + QueryWhere where = db.from(m1).innerJoin(m2).on(m1.id).is(m2.id).where(m2.myTree).is(Tree.MAPLE); + String q1 = where.toSQL(false); + String q2 = where.toSQL(true); + String q3 = where.toSQL(false, m1); + String q4 = where.toSQL(true, m1); + String q5 = where.toSQL(false, m2); + String q6 = where.toSQL(true, m2); - // test unused alias - String q7 = where.toSQL(true, m3); + // test unused alias + String q7 = where.toSQL(true, m3); - db.close(); + db.close(); - assertEquals(MessageFormat.format("SELECT * FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q1); - assertEquals(MessageFormat.format("SELECT DISTINCT * FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q2); + assertEquals(MessageFormat.format("SELECT * FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q1); + assertEquals(MessageFormat.format("SELECT DISTINCT * FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q2); - assertEquals(MessageFormat.format("SELECT T{0,number,0}.* FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q3); - assertEquals(MessageFormat.format("SELECT DISTINCT T{0,number,0}.* FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q4); + assertEquals(MessageFormat.format("SELECT T{0,number,0}.* FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q3); + assertEquals(MessageFormat.format("SELECT DISTINCT T{0,number,0}.* FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q4); - assertEquals(MessageFormat.format("SELECT T{1,number,0}.* FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q5); - assertEquals(MessageFormat.format("SELECT DISTINCT T{1,number,0}.* FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q6); + assertEquals(MessageFormat.format("SELECT T{1,number,0}.* FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q5); + assertEquals(MessageFormat.format("SELECT DISTINCT T{1,number,0}.* FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q6); - assertEquals(MessageFormat.format("SELECT DISTINCT * FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q7); - } + assertEquals(MessageFormat.format("SELECT DISTINCT * FROM StaticQueryTest1 AS T{0,number,0} INNER JOIN StaticQueryTest2 AS T{1,number,0} ON T{0,number,0}.id = T{1,number,0}.id WHERE T{1,number,0}.myTree = 50", t0, t1), q7); + } - @Test - public void testRuntimeQuery() { - Db db = IciqlSuite.openNewDb(); - db.insertAll(Product.getList()); + @Test + public void testRuntimeQuery() { + Db db = IciqlSuite.openNewDb(); + db.insertAll(Product.getList()); - String unitsInStock = db.getDialect().prepareColumnName("unitsInStock"); - String productName = db.getDialect().prepareColumnName("productName"); - String productId = db.getDialect().prepareColumnName("productId"); + String unitsInStock = db.getDialect().prepareColumnName("unitsInStock"); + String productName = db.getDialect().prepareColumnName("productName"); + String productId = db.getDialect().prepareColumnName("productId"); - Product p = new Product(); - List products = db.from(p).where(unitsInStock + "=?", 120).orderBy(p.productId).select(); - assertEquals(1, products.size()); + Product p = new Product(); + List products = db.from(p).where(unitsInStock + "=?", 120).orderBy(p.productId).select(); + assertEquals(1, products.size()); - products = db.from(p).where(String.format("%s=? and productName like ? order by productId", - unitsInStock, productName, productId), 0, "Chef%") - .select(); - assertEquals(1, products.size()); - - db.close(); - } - - @Test - public void testExecuteQuery() throws SQLException { - Db db = IciqlSuite.openNewDb(); - db.insertAll(Product.getList()); - - String product = db.getDialect().prepareTableName(null, "Product"); - String unitsInStock = db.getDialect().prepareColumnName("unitsInStock"); - - // test plain statement - List products = db.executeQuery(Product.class, - String.format("select * from %s where %s=120", - product, unitsInStock)); - assertEquals(1, products.size()); - assertEquals("Condiments", products.get(0).category); - - // test prepared statement - products = db.executeQuery(Product.class, String.format("select * from %s where %s=?", - product, unitsInStock), 120); - assertEquals(1, products.size()); - assertEquals("Condiments", products.get(0).category); - - db.close(); - } + products = db.from(p).where(String.format("%s=? and productName like ? order by productId", + unitsInStock, productName, productId), 0, "Chef%") + .select(); + assertEquals(1, products.size()); + + db.close(); + } + + @Test + public void testExecuteQuery() throws SQLException { + Db db = IciqlSuite.openNewDb(); + db.insertAll(Product.getList()); + + String product = db.getDialect().prepareTableName(null, "Product"); + String unitsInStock = db.getDialect().prepareColumnName("unitsInStock"); + + // test plain statement + List products = db.executeQuery(Product.class, + String.format("select * from %s where %s=120", + product, unitsInStock)); + assertEquals(1, products.size()); + assertEquals("Condiments", products.get(0).category); + + // test prepared statement + products = db.executeQuery(Product.class, String.format("select * from %s where %s=?", + product, unitsInStock), 120); + assertEquals(1, products.size()); + assertEquals("Condiments", products.get(0).category); + + db.close(); + } - @Test - public void testBuildObjects() throws SQLException { - Db db = IciqlSuite.openNewDb(); - db.insertAll(Product.getList()); - - String product = db.getDialect().prepareTableName(null, "Product"); - String unitsInStock = db.getDialect().prepareColumnName("unitsInStock"); + @Test + public void testBuildObjects() throws SQLException { + Db db = IciqlSuite.openNewDb(); + db.insertAll(Product.getList()); + + String product = db.getDialect().prepareTableName(null, "Product"); + String unitsInStock = db.getDialect().prepareColumnName("unitsInStock"); - // test plain statement - ResultSet rs = db.executeQuery(String.format("select * from %s where %s=120", - product, unitsInStock)); - List products = db.buildObjects(Product.class, rs); - JdbcUtils.closeSilently(rs, true); + // test plain statement + ResultSet rs = db.executeQuery(String.format("select * from %s where %s=120", + product, unitsInStock)); + List products = db.buildObjects(Product.class, rs); + JdbcUtils.closeSilently(rs, true); - assertEquals(1, products.size()); - assertEquals("Condiments", products.get(0).category); - - // test prepared statement - rs = db.executeQuery(String.format("select * from %s where %s=?", - product, unitsInStock), 120); - products = db.buildObjects(Product.class, rs); - JdbcUtils.closeSilently(rs, true); + assertEquals(1, products.size()); + assertEquals("Condiments", products.get(0).category); + + // test prepared statement + rs = db.executeQuery(String.format("select * from %s where %s=?", + product, unitsInStock), 120); + products = db.buildObjects(Product.class, rs); + JdbcUtils.closeSilently(rs, true); - assertEquals(1, products.size()); - assertEquals("Condiments", products.get(0).category); + assertEquals(1, products.size()); + assertEquals("Condiments", products.get(0).category); - db.close(); - } + db.close(); + } } diff --git a/src/test/java/com/iciql/test/SamplesTest.java b/src/test/java/com/iciql/test/SamplesTest.java index dbbf97f..e3b3ea5 100644 --- a/src/test/java/com/iciql/test/SamplesTest.java +++ b/src/test/java/com/iciql/test/SamplesTest.java @@ -17,27 +17,6 @@ package com.iciql.test; -import static com.iciql.Function.count; -import static com.iciql.Function.isNull; -import static com.iciql.Function.length; -import static com.iciql.Function.max; -import static com.iciql.Function.min; -import static com.iciql.Function.not; -import static com.iciql.Function.sum; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.math.BigDecimal; -import java.text.DecimalFormat; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - import com.iciql.Db; import com.iciql.Filter; import com.iciql.Iciql.IQColumn; @@ -47,6 +26,20 @@ import com.iciql.test.models.Customer; import com.iciql.test.models.Order; import com.iciql.test.models.Product; import com.iciql.test.models.SupportedTypes; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static com.iciql.Function.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * This is the implementation of the 101 LINQ Samples as described in @@ -54,399 +47,399 @@ import com.iciql.test.models.SupportedTypes; */ public class SamplesTest { - /** - * This object represents a database (actually a connection to the - * database). - */ - - Db db; - - @Before - public void setUp() { - db = IciqlSuite.openNewDb(); - db.insertAll(Product.getList()); - db.insertAll(Customer.getList()); - db.insertAll(Order.getList()); - db.insertAll(ComplexObject.getList()); - } - - @After - public void tearDown() { - db.close(); - } - - /** - * A simple test table. The columns are in a different order than in the - * database. - */ - public static class TestReverse { - public String name; - public Integer id; - } - - @Test - public void testReverseColumns() { - try { - db.executeUpdate("DROP TABLE TestReverse"); - } catch (IciqlException e) { - } - db.executeUpdate("create table TestReverse(id int, name varchar(10), additional varchar(10))"); - TestReverse t = new TestReverse(); - t.id = 10; - t.name = "Hello"; - db.insert(t); - TestReverse check = db.from(new TestReverse()).selectFirst(); - assertEquals(t.name, check.name); - assertEquals(t.id, check.id); - db.executeUpdate("DROP TABLE TestReverse"); - } - - @Test - public void testWhereSimple2() { - - // var soldOutProducts = - // from p in products - // where p.UnitsInStock == 0 - // select p; - - Product p = new Product(); - List soldOutProducts = db.from(p).where(p.unitsInStock).is(0).orderBy(p.productId).select(); - List soldOutProducts2 = db.from(p).where(p.unitsInStock).is(0).orderBy(p.productId).select(p); - - assertEquals("[Chef Anton's Gumbo Mix: 0]", soldOutProducts.toString()); - assertEquals(soldOutProducts.toString(), soldOutProducts2.toString()); - } - - @Test - public void testWhereSimple3() { - - // var expensiveInStockProducts = - // from p in products - // where p.UnitsInStock > 0 - // && p.UnitPrice > 3.00M - // select p; - - Product p = new Product(); - List expensiveInStockProducts = db.from(p).where(p.unitsInStock).exceeds(0).and(p.unitPrice) - .exceeds(30.0).orderBy(p.productId).select(); - - assertEquals("[Northwoods Cranberry Sauce: 6, Mishi Kobe Niku: 29, Ikura: 31]", - expensiveInStockProducts.toString()); - } - - @Test - public void testWhereSimple4() { - - // var waCustomers = - // from c in customers - // where c.Region == "WA" - // select c; - - Customer c = new Customer(); - List waCustomers = db.from(c).where(c.region).is("WA").select(); - - assertEquals("[ALFKI, ANATR]", waCustomers.toString()); - } - - @Test - public void testSelectSimple2() { - - // var productNames = - // from p in products - // select p.ProductName; - - Product p = new Product(); - List productNames = db.from(p).orderBy(p.productId).select(p.productName); - - List products = Product.getList(); - for (int i = 0; i < products.size(); i++) { - assertEquals(products.get(i).productName, productNames.get(i)); - } - } - - /** - * A result set class containing the product name and price. - */ - public static class ProductPrice { - public String productName; - public String category; - @IQColumn(name = "unitPrice") - public Double price; - } - - @Test - public void testAnonymousTypes3() { - - // var productInfos = - // from p in products - // select new { - // p.ProductName, - // p.Category, - // Price = p.UnitPrice - // }; - - final Product p = new Product(); - List productInfos = db.from(p).orderBy(p.productId).select(new ProductPrice() { - { - productName = p.productName; - category = p.category; - price = p.unitPrice; - } - }); - - List products = Product.getList(); - assertEquals(products.size(), productInfos.size()); - for (int i = 0; i < products.size(); i++) { - ProductPrice pr = productInfos.get(i); - Product p2 = products.get(i); - assertEquals(p2.productName, pr.productName); - assertEquals(p2.category, pr.category); - assertEquals(p2.unitPrice, pr.price); - } - } - - /** - * A result set class containing customer data and the order total. - */ - public static class CustOrder { - public String customerId; - public Integer orderId; - public BigDecimal total; - - @Override - public String toString() { - return customerId + ":" + orderId + ":" + new DecimalFormat("##.00").format(total); - } - } - - @Test - public void testSelectManyCompoundFrom2() { - - // var orders = - // from c in customers, - // o in c.Orders - // where o.Total < 500.00M - // select new { - // c.CustomerID, - // o.OrderID, - // o.Total - // }; - - final Customer c = new Customer(); - final Order o = new Order(); - List orders = db.from(c).innerJoin(o).on(c.customerId).is(o.customerId).where(o.total) - .lessThan(new BigDecimal("100.00")).orderBy(c.customerId).select(new CustOrder() { - { - customerId = c.customerId; - orderId = o.orderId; - total = o.total; - } - }); - - assertEquals("[ANATR:10308:88.80]", orders.toString()); - } - - @Test - public void testIsNull() { - Product p = new Product(); - String sql = db.from(p).whereTrue(isNull(p.productName)).getSQL(); - assertEquals("SELECT * FROM Product WHERE (" + db.getDialect().prepareColumnName("productName") - + " IS NULL)", sql); - } - - @Test - public void testDelete() { - Product p = new Product(); - int deleted = db.from(p).where(p.productName).like("A%").delete(); - assertEquals(1, deleted); - deleted = db.from(p).delete(); - assertEquals(9, deleted); - db.insertAll(Product.getList()); - db.deleteAll(Product.getList()); - assertEquals(0, db.from(p).selectCount()); - db.insertAll(Product.getList()); - } - - @Test - public void testOrAndNot() { - Product p = new Product(); - String sql = db.from(p).whereTrue(not(isNull(p.productName))).getSQL(); - String productName = db.getDialect().prepareColumnName("productName"); - assertEquals("SELECT * FROM Product WHERE (NOT " + productName + " IS NULL)", sql); - sql = db.from(p).whereTrue(not(isNull(p.productName))).getSQL(); - assertEquals("SELECT * FROM Product WHERE (NOT " + productName + " IS NULL)", sql); - sql = db.from(p).whereTrue(db.test(p.productId).is(1)).getSQL(); - String productId = db.getDialect().prepareColumnName("productId"); - assertEquals("SELECT * FROM Product WHERE ((" + productId + " = ?))", sql); - } - - @Test - public void testLength() { - Product p = new Product(); - List lengths = db.from(p).where(length(p.productName)).lessThan(10) - .selectDistinct(length(p.productName)); - // Formerly used orderBy(1) here, but that is not portable across DBs - Collections.sort(lengths); - assertEquals("[4, 5]", lengths.toString()); - } - - @Test - public void testSum() { - Product p = new Product(); - Number sum = db.from(p).selectFirst(sum(p.unitsInStock)); - assertEquals(323, sum.intValue()); - Double sumPrice = db.from(p).selectFirst(sum(p.unitPrice)); - assertEquals(313.35, sumPrice.doubleValue(), 0.001); - } - - @Test - public void testMinMax() { - Product p = new Product(); - Integer min = db.from(p).selectFirst(min(p.unitsInStock)); - assertEquals(0, min.intValue()); - String minName = db.from(p).selectFirst(min(p.productName)); - assertEquals("Aniseed Syrup", minName); - Double max = db.from(p).selectFirst(max(p.unitPrice)); - assertEquals(97.0, max.doubleValue(), 0.001); - } - - @Test - public void testLike() { - Product p = new Product(); - List aList = db.from(p).where(p.productName).like("Cha%").orderBy(p.productName).select(); - assertEquals("[Chai: 39, Chang: 17]", aList.toString()); - } - - @Test - public void testCount() { - long count = db.from(new Product()).selectCount(); - assertEquals(10, count); - } - - @Test - public void testComplexObject() { - ComplexObject co = new ComplexObject(); - String sql = db.from(co).where(co.id).is(1).and(co.amount).is(1L).and(co.birthday) - .lessThan(new java.util.Date()).and(co.created) - .lessThan(java.sql.Timestamp.valueOf("2005-05-05 05:05:05")).and(co.name).is("hello") - .and(co.time).lessThan(java.sql.Time.valueOf("23:23:23")).and(co.value) - .is(new BigDecimal("1")).getSQL(); - - StringBuilder sb = new StringBuilder(); - sb.append("SELECT * FROM ComplexObject WHERE "); - sb.append(db.getDialect().prepareColumnName("id")); - sb.append(" = ? AND "); - sb.append(db.getDialect().prepareColumnName("amount")); - sb.append(" = ? AND "); - sb.append(db.getDialect().prepareColumnName("birthday")); - sb.append(" < ? AND "); - sb.append(db.getDialect().prepareColumnName("created")); - sb.append(" < ? AND "); - sb.append(db.getDialect().prepareColumnName("name")); - sb.append(" = ? AND "); - sb.append(db.getDialect().prepareColumnName("time")); - sb.append(" < ? AND "); - sb.append(db.getDialect().prepareColumnName("value")); - sb.append(" = ?"); - assertEquals(sb.toString(), sql); - - long count = db.from(co).where(co.id).is(1).and(co.amount).is(1L).and(co.birthday) - .lessThan(new java.util.Date()).and(co.created) - .lessThan(java.sql.Timestamp.valueOf("2005-05-05 05:05:05")).and(co.name).is("hello") - .and(co.time).lessThan(java.sql.Time.valueOf("23:23:23")).and(co.value) - .is(new BigDecimal("1")).selectCount(); - assertEquals(1, count); - } - - @Test - public void testComplexObject2() { - testComplexObject2(1, "hello"); - } - - private void testComplexObject2(final int x, final String name) { - final ComplexObject co = new ComplexObject(); - - String sql = db.from(co).where(new Filter() { - @Override - public boolean where() { - return co.id == x && co.name.equals(name) && co.name.equals("hello"); - } - }).getSQL(); - StringBuilder sb = new StringBuilder(); - sb.append("SELECT * FROM ComplexObject WHERE "); - sb.append(db.getDialect().prepareColumnName("id")); - sb.append("=? AND ?="); - sb.append(db.getDialect().prepareColumnName("name")); - sb.append(" AND 'hello'="); - sb.append(db.getDialect().prepareColumnName("name")); - assertEquals(sb.toString(), sql); - - long count = db.from(co).where(new Filter() { - @Override - public boolean where() { - return co.id == x && co.name.equals(name) && co.name.equals("hello"); - } - }).selectCount(); - - assertEquals(1, count); - } - - @Test - public void testLimitOffset() { - Set ids = new HashSet(); - Product p = new Product(); - for (int i = 0; i < 5; i++) { - List products = db.from(p).limit(2).offset(2 * i).select(); - assertTrue(products.size() == 2); - for (Product prod : products) { - assertTrue("Failed to add product id. Duplicate?", ids.add(prod.productId)); - } - } - } - - @Test - public void testKeyRetrieval() { - List list = SupportedTypes.createList(); - List keys = db.insertAllAndGetKeys(list); - Set uniqueKeys = new HashSet(); - for (Long l : keys) { - assertTrue("Failed to add key. Duplicate?", uniqueKeys.add(l)); - } - } - - /** - * A result set class containing product groups. - */ - public static class ProductGroup { - public String category; - public Long productCount; - - @Override - public String toString() { - return category + ":" + productCount; - } - } - - @Test - public void testGroup() { - - // var orderGroups = - // from p in products - // group p by p.Category into g - // select new { - // Category = g.Key, - // Products = g - // }; - - final Product p = new Product(); - List list = db.from(p).groupBy(p.category).orderBy(p.category) - .select(new ProductGroup() { - { - category = p.category; - productCount = count(); - } - }); - assertEquals("[Beverages:2, Condiments:5, Meat/Poultry:1, Produce:1, Seafood:1]", list.toString()); - } + /** + * This object represents a database (actually a connection to the + * database). + */ + + Db db; + + @Before + public void setUp() { + db = IciqlSuite.openNewDb(); + db.insertAll(Product.getList()); + db.insertAll(Customer.getList()); + db.insertAll(Order.getList()); + db.insertAll(ComplexObject.getList()); + } + + @After + public void tearDown() { + db.close(); + } + + /** + * A simple test table. The columns are in a different order than in the + * database. + */ + public static class TestReverse { + public String name; + public Integer id; + } + + @Test + public void testReverseColumns() { + try { + db.executeUpdate("DROP TABLE TestReverse"); + } catch (IciqlException e) { + } + db.executeUpdate("create table TestReverse(id int, name varchar(10), additional varchar(10))"); + TestReverse t = new TestReverse(); + t.id = 10; + t.name = "Hello"; + db.insert(t); + TestReverse check = db.from(new TestReverse()).selectFirst(); + assertEquals(t.name, check.name); + assertEquals(t.id, check.id); + db.executeUpdate("DROP TABLE TestReverse"); + } + + @Test + public void testWhereSimple2() { + + // var soldOutProducts = + // from p in products + // where p.UnitsInStock == 0 + // select p; + + Product p = new Product(); + List soldOutProducts = db.from(p).where(p.unitsInStock).is(0).orderBy(p.productId).select(); + List soldOutProducts2 = db.from(p).where(p.unitsInStock).is(0).orderBy(p.productId).select(p); + + assertEquals("[Chef Anton's Gumbo Mix: 0]", soldOutProducts.toString()); + assertEquals(soldOutProducts.toString(), soldOutProducts2.toString()); + } + + @Test + public void testWhereSimple3() { + + // var expensiveInStockProducts = + // from p in products + // where p.UnitsInStock > 0 + // && p.UnitPrice > 3.00M + // select p; + + Product p = new Product(); + List expensiveInStockProducts = db.from(p).where(p.unitsInStock).exceeds(0).and(p.unitPrice) + .exceeds(30.0).orderBy(p.productId).select(); + + assertEquals("[Northwoods Cranberry Sauce: 6, Mishi Kobe Niku: 29, Ikura: 31]", + expensiveInStockProducts.toString()); + } + + @Test + public void testWhereSimple4() { + + // var waCustomers = + // from c in customers + // where c.Region == "WA" + // select c; + + Customer c = new Customer(); + List waCustomers = db.from(c).where(c.region).is("WA").select(); + + assertEquals("[ALFKI, ANATR]", waCustomers.toString()); + } + + @Test + public void testSelectSimple2() { + + // var productNames = + // from p in products + // select p.ProductName; + + Product p = new Product(); + List productNames = db.from(p).orderBy(p.productId).select(p.productName); + + List products = Product.getList(); + for (int i = 0; i < products.size(); i++) { + assertEquals(products.get(i).productName, productNames.get(i)); + } + } + + /** + * A result set class containing the product name and price. + */ + public static class ProductPrice { + public String productName; + public String category; + @IQColumn(name = "unitPrice") + public Double price; + } + + @Test + public void testAnonymousTypes3() { + + // var productInfos = + // from p in products + // select new { + // p.ProductName, + // p.Category, + // Price = p.UnitPrice + // }; + + final Product p = new Product(); + List productInfos = db.from(p).orderBy(p.productId).select(new ProductPrice() { + { + productName = p.productName; + category = p.category; + price = p.unitPrice; + } + }); + + List products = Product.getList(); + assertEquals(products.size(), productInfos.size()); + for (int i = 0; i < products.size(); i++) { + ProductPrice pr = productInfos.get(i); + Product p2 = products.get(i); + assertEquals(p2.productName, pr.productName); + assertEquals(p2.category, pr.category); + assertEquals(p2.unitPrice, pr.price); + } + } + + /** + * A result set class containing customer data and the order total. + */ + public static class CustOrder { + public String customerId; + public Integer orderId; + public BigDecimal total; + + @Override + public String toString() { + return customerId + ":" + orderId + ":" + new DecimalFormat("##.00").format(total); + } + } + + @Test + public void testSelectManyCompoundFrom2() { + + // var orders = + // from c in customers, + // o in c.Orders + // where o.Total < 500.00M + // select new { + // c.CustomerID, + // o.OrderID, + // o.Total + // }; + + final Customer c = new Customer(); + final Order o = new Order(); + List orders = db.from(c).innerJoin(o).on(c.customerId).is(o.customerId).where(o.total) + .lessThan(new BigDecimal("100.00")).orderBy(c.customerId).select(new CustOrder() { + { + customerId = c.customerId; + orderId = o.orderId; + total = o.total; + } + }); + + assertEquals("[ANATR:10308:88.80]", orders.toString()); + } + + @Test + public void testIsNull() { + Product p = new Product(); + String sql = db.from(p).whereTrue(isNull(p.productName)).getSQL(); + assertEquals("SELECT * FROM Product WHERE (" + db.getDialect().prepareColumnName("productName") + + " IS NULL)", sql); + } + + @Test + public void testDelete() { + Product p = new Product(); + int deleted = db.from(p).where(p.productName).like("A%").delete(); + assertEquals(1, deleted); + deleted = db.from(p).delete(); + assertEquals(9, deleted); + db.insertAll(Product.getList()); + db.deleteAll(Product.getList()); + assertEquals(0, db.from(p).selectCount()); + db.insertAll(Product.getList()); + } + + @Test + public void testOrAndNot() { + Product p = new Product(); + String sql = db.from(p).whereTrue(not(isNull(p.productName))).getSQL(); + String productName = db.getDialect().prepareColumnName("productName"); + assertEquals("SELECT * FROM Product WHERE (NOT " + productName + " IS NULL)", sql); + sql = db.from(p).whereTrue(not(isNull(p.productName))).getSQL(); + assertEquals("SELECT * FROM Product WHERE (NOT " + productName + " IS NULL)", sql); + sql = db.from(p).whereTrue(db.test(p.productId).is(1)).getSQL(); + String productId = db.getDialect().prepareColumnName("productId"); + assertEquals("SELECT * FROM Product WHERE ((" + productId + " = ?))", sql); + } + + @Test + public void testLength() { + Product p = new Product(); + List lengths = db.from(p).where(length(p.productName)).lessThan(10) + .selectDistinct(length(p.productName)); + // Formerly used orderBy(1) here, but that is not portable across DBs + Collections.sort(lengths); + assertEquals("[4, 5]", lengths.toString()); + } + + @Test + public void testSum() { + Product p = new Product(); + Number sum = db.from(p).selectFirst(sum(p.unitsInStock)); + assertEquals(323, sum.intValue()); + Double sumPrice = db.from(p).selectFirst(sum(p.unitPrice)); + assertEquals(313.35, sumPrice.doubleValue(), 0.001); + } + + @Test + public void testMinMax() { + Product p = new Product(); + Integer min = db.from(p).selectFirst(min(p.unitsInStock)); + assertEquals(0, min.intValue()); + String minName = db.from(p).selectFirst(min(p.productName)); + assertEquals("Aniseed Syrup", minName); + Double max = db.from(p).selectFirst(max(p.unitPrice)); + assertEquals(97.0, max.doubleValue(), 0.001); + } + + @Test + public void testLike() { + Product p = new Product(); + List aList = db.from(p).where(p.productName).like("Cha%").orderBy(p.productName).select(); + assertEquals("[Chai: 39, Chang: 17]", aList.toString()); + } + + @Test + public void testCount() { + long count = db.from(new Product()).selectCount(); + assertEquals(10, count); + } + + @Test + public void testComplexObject() { + ComplexObject co = new ComplexObject(); + String sql = db.from(co).where(co.id).is(1).and(co.amount).is(1L).and(co.birthday) + .lessThan(new java.util.Date()).and(co.created) + .lessThan(java.sql.Timestamp.valueOf("2005-05-05 05:05:05")).and(co.name).is("hello") + .and(co.time).lessThan(java.sql.Time.valueOf("23:23:23")).and(co.value) + .is(new BigDecimal("1")).getSQL(); + + StringBuilder sb = new StringBuilder(); + sb.append("SELECT * FROM ComplexObject WHERE "); + sb.append(db.getDialect().prepareColumnName("id")); + sb.append(" = ? AND "); + sb.append(db.getDialect().prepareColumnName("amount")); + sb.append(" = ? AND "); + sb.append(db.getDialect().prepareColumnName("birthday")); + sb.append(" < ? AND "); + sb.append(db.getDialect().prepareColumnName("created")); + sb.append(" < ? AND "); + sb.append(db.getDialect().prepareColumnName("name")); + sb.append(" = ? AND "); + sb.append(db.getDialect().prepareColumnName("time")); + sb.append(" < ? AND "); + sb.append(db.getDialect().prepareColumnName("value")); + sb.append(" = ?"); + assertEquals(sb.toString(), sql); + + long count = db.from(co).where(co.id).is(1).and(co.amount).is(1L).and(co.birthday) + .lessThan(new java.util.Date()).and(co.created) + .lessThan(java.sql.Timestamp.valueOf("2005-05-05 05:05:05")).and(co.name).is("hello") + .and(co.time).lessThan(java.sql.Time.valueOf("23:23:23")).and(co.value) + .is(new BigDecimal("1")).selectCount(); + assertEquals(1, count); + } + + @Test + public void testComplexObject2() { + testComplexObject2(1, "hello"); + } + + private void testComplexObject2(final int x, final String name) { + final ComplexObject co = new ComplexObject(); + + String sql = db.from(co).where(new Filter() { + @Override + public boolean where() { + return co.id == x && co.name.equals(name) && co.name.equals("hello"); + } + }).getSQL(); + StringBuilder sb = new StringBuilder(); + sb.append("SELECT * FROM ComplexObject WHERE "); + sb.append(db.getDialect().prepareColumnName("id")); + sb.append("=? AND ?="); + sb.append(db.getDialect().prepareColumnName("name")); + sb.append(" AND 'hello'="); + sb.append(db.getDialect().prepareColumnName("name")); + assertEquals(sb.toString(), sql); + + long count = db.from(co).where(new Filter() { + @Override + public boolean where() { + return co.id == x && co.name.equals(name) && co.name.equals("hello"); + } + }).selectCount(); + + assertEquals(1, count); + } + + @Test + public void testLimitOffset() { + Set ids = new HashSet(); + Product p = new Product(); + for (int i = 0; i < 5; i++) { + List products = db.from(p).limit(2).offset(2 * i).select(); + assertTrue(products.size() == 2); + for (Product prod : products) { + assertTrue("Failed to add product id. Duplicate?", ids.add(prod.productId)); + } + } + } + + @Test + public void testKeyRetrieval() { + List list = SupportedTypes.createList(); + List keys = db.insertAllAndGetKeys(list); + Set uniqueKeys = new HashSet(); + for (Long l : keys) { + assertTrue("Failed to add key. Duplicate?", uniqueKeys.add(l)); + } + } + + /** + * A result set class containing product groups. + */ + public static class ProductGroup { + public String category; + public Long productCount; + + @Override + public String toString() { + return category + ":" + productCount; + } + } + + @Test + public void testGroup() { + + // var orderGroups = + // from p in products + // group p by p.Category into g + // select new { + // Category = g.Key, + // Products = g + // }; + + final Product p = new Product(); + List list = db.from(p).groupBy(p.category).orderBy(p.category) + .select(new ProductGroup() { + { + category = p.category; + productCount = count(); + } + }); + assertEquals("[Beverages:2, Condiments:5, Meat/Poultry:1, Produce:1, Seafood:1]", list.toString()); + } } diff --git a/src/test/java/com/iciql/test/TransactionTest.java b/src/test/java/com/iciql/test/TransactionTest.java index 026366e..5c84072 100644 --- a/src/test/java/com/iciql/test/TransactionTest.java +++ b/src/test/java/com/iciql/test/TransactionTest.java @@ -15,105 +15,104 @@ */ package com.iciql.test; -import static org.junit.Assert.assertEquals; - -import java.sql.SQLException; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - import com.iciql.Db; import com.iciql.IciqlException; import com.iciql.test.models.CategoryAnnotationOnly; import com.iciql.test.models.ProductAnnotationOnlyWithForeignKey; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.sql.SQLException; + +import static org.junit.Assert.assertEquals; /** * Tests of transactions. */ public class TransactionTest { - /** - * This object represents a database (actually a connection to the - * database). - */ - - private Db db; - - @Before - public void setUp() { - db = IciqlSuite.openNewDb(); - - // tables creation - db.from(new CategoryAnnotationOnly()); - db.from(new ProductAnnotationOnlyWithForeignKey()); - - startTransactionMode(); - } - - @After - public void tearDown() { - - endTransactionMode(); - - db.dropTable(ProductAnnotationOnlyWithForeignKey.class); - db.dropTable(CategoryAnnotationOnly.class); - db.close(); - } - - @Test - public void testTransaction() { - - // insert in 2 tables inside a transaction - - // insertAll don't use save point in this transaction - db.insertAll(CategoryAnnotationOnly.getList()); - db.insertAll(ProductAnnotationOnlyWithForeignKey.getList()); - - // don't commit changes - try { - db.getConnection().rollback(); - } catch (SQLException e) { - throw new IciqlException(e, "Can't rollback"); - } - - ProductAnnotationOnlyWithForeignKey p = new ProductAnnotationOnlyWithForeignKey(); - long count1 = db.from(p).selectCount(); - - CategoryAnnotationOnly c = new CategoryAnnotationOnly(); - long count2 = db.from(c).selectCount(); - - // verify changes aren't committed - assertEquals(count1, 0L); - assertEquals(count2, 0L); - } - - /** - * Helper to set transaction mode - */ - private void startTransactionMode() { - db.setSkipCreate(true); - db.setAutoSavePoint(false); - - try { - db.getConnection().setAutoCommit(false); - } catch (SQLException e) { - throw new IciqlException(e, "Could not change auto-commit mode"); - } - } - - /** - * Helper to return to initial mode - */ - private void endTransactionMode() { - try { - db.getConnection().setAutoCommit(true); - } catch (SQLException e) { - throw new IciqlException(e, "Could not change auto-commit mode"); - } - // returns to initial states - db.setSkipCreate(false); - db.setAutoSavePoint(true); - } - + /** + * This object represents a database (actually a connection to the + * database). + */ + + private Db db; + + @Before + public void setUp() { + db = IciqlSuite.openNewDb(); + + // tables creation + db.from(new CategoryAnnotationOnly()); + db.from(new ProductAnnotationOnlyWithForeignKey()); + + startTransactionMode(); + } + + @After + public void tearDown() { + + endTransactionMode(); + + db.dropTable(ProductAnnotationOnlyWithForeignKey.class); + db.dropTable(CategoryAnnotationOnly.class); + db.close(); + } + + @Test + public void testTransaction() { + + // insert in 2 tables inside a transaction + + // insertAll don't use save point in this transaction + db.insertAll(CategoryAnnotationOnly.getList()); + db.insertAll(ProductAnnotationOnlyWithForeignKey.getList()); + + // don't commit changes + try { + db.getConnection().rollback(); + } catch (SQLException e) { + throw new IciqlException(e, "Can't rollback"); + } + + ProductAnnotationOnlyWithForeignKey p = new ProductAnnotationOnlyWithForeignKey(); + long count1 = db.from(p).selectCount(); + + CategoryAnnotationOnly c = new CategoryAnnotationOnly(); + long count2 = db.from(c).selectCount(); + + // verify changes aren't committed + assertEquals(count1, 0L); + assertEquals(count2, 0L); + } + + /** + * Helper to set transaction mode + */ + private void startTransactionMode() { + db.setSkipCreate(true); + db.setAutoSavePoint(false); + + try { + db.getConnection().setAutoCommit(false); + } catch (SQLException e) { + throw new IciqlException(e, "Could not change auto-commit mode"); + } + } + + /** + * Helper to return to initial mode + */ + private void endTransactionMode() { + try { + db.getConnection().setAutoCommit(true); + } catch (SQLException e) { + throw new IciqlException(e, "Could not change auto-commit mode"); + } + // returns to initial states + db.setSkipCreate(false); + db.setAutoSavePoint(true); + } + } diff --git a/src/test/java/com/iciql/test/UUIDTest.java b/src/test/java/com/iciql/test/UUIDTest.java index bb09c9f..14db69c 100644 --- a/src/test/java/com/iciql/test/UUIDTest.java +++ b/src/test/java/com/iciql/test/UUIDTest.java @@ -16,21 +16,20 @@ package com.iciql.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - +import com.iciql.Db; +import com.iciql.Iciql.IQColumn; +import com.iciql.Iciql.IQTable; import org.junit.After; import org.junit.Assume; import org.junit.Before; import org.junit.Test; -import com.iciql.Db; -import com.iciql.Iciql.IQColumn; -import com.iciql.Iciql.IQTable; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Tests of UUID type. @@ -39,77 +38,77 @@ import com.iciql.Iciql.IQTable; */ public class UUIDTest { - Db db; - - @Before - public void setup() { - db = IciqlSuite.openNewDb(); - } - - @After - public void tearDown() { - db.close(); - } - - @Test - public void testUUIDs() throws Exception { - // do not test non-H2 databases - Assume.assumeTrue(IciqlSuite.isH2(db)); - - List originals = UUIDRecord.getList(); - db.insertAll(originals); - UUIDRecord u = new UUIDRecord(); - List retrieved = db.from(u).orderBy(u.id).select(); - assertEquals(originals.size(), retrieved.size()); - for (int i = 0; i < originals.size(); i++) { - UUIDRecord a = originals.get(i); - UUIDRecord b = retrieved.get(i); - assertTrue(a.equivalentTo(b)); - } - - UUIDRecord second = db.from(u).where(u.uuid).is(originals.get(1).uuid).selectFirst(); - assertTrue(originals.get(1).equivalentTo(second)); - db.dropTable(UUIDRecord.class); - } - - /** - * A simple class used in this test. - */ - @IQTable(name = "UUID_TEST") - public static class UUIDRecord { - - @IQColumn(primaryKey = true) - public Integer id; - - @IQColumn() - public UUID uuid; - - public UUIDRecord() { - // public constructor - } - - private UUIDRecord(int id) { - this.id = id; - this.uuid = UUID.randomUUID(); - } - - public boolean equivalentTo(UUIDRecord b) { - boolean same = true; - same &= id == b.id; - same &= uuid.equals(b.uuid); - return same; - } - - public String toString() { - return id + ": " + uuid; - } - - public static List getList() { - List list = new ArrayList(); - for (int i = 0; i < 10; i++) { - list.add(new UUIDRecord(i + 1)); - } - return list; - } - } + Db db; + + @Before + public void setup() { + db = IciqlSuite.openNewDb(); + } + + @After + public void tearDown() { + db.close(); + } + + @Test + public void testUUIDs() throws Exception { + // do not test non-H2 databases + Assume.assumeTrue(IciqlSuite.isH2(db)); + + List originals = UUIDRecord.getList(); + db.insertAll(originals); + UUIDRecord u = new UUIDRecord(); + List retrieved = db.from(u).orderBy(u.id).select(); + assertEquals(originals.size(), retrieved.size()); + for (int i = 0; i < originals.size(); i++) { + UUIDRecord a = originals.get(i); + UUIDRecord b = retrieved.get(i); + assertTrue(a.equivalentTo(b)); + } + + UUIDRecord second = db.from(u).where(u.uuid).is(originals.get(1).uuid).selectFirst(); + assertTrue(originals.get(1).equivalentTo(second)); + db.dropTable(UUIDRecord.class); + } + + /** + * A simple class used in this test. + */ + @IQTable(name = "UUID_TEST") + public static class UUIDRecord { + + @IQColumn(primaryKey = true) + public Integer id; + + @IQColumn() + public UUID uuid; + + public UUIDRecord() { + // public constructor + } + + private UUIDRecord(int id) { + this.id = id; + this.uuid = UUID.randomUUID(); + } + + public boolean equivalentTo(UUIDRecord b) { + boolean same = true; + same &= id == b.id; + same &= uuid.equals(b.uuid); + return same; + } + + public String toString() { + return id + ": " + uuid; + } + + public static List getList() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + list.add(new UUIDRecord(i + 1)); + } + return list; + } + } } diff --git a/src/test/java/com/iciql/test/UpdateTest.java b/src/test/java/com/iciql/test/UpdateTest.java index b0981a9..9f1bf70 100644 --- a/src/test/java/com/iciql/test/UpdateTest.java +++ b/src/test/java/com/iciql/test/UpdateTest.java @@ -17,166 +17,163 @@ package com.iciql.test; -import static java.sql.Date.valueOf; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - import com.iciql.Db; import com.iciql.test.models.Customer; import com.iciql.test.models.Order; import com.iciql.test.models.Product; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static java.sql.Date.valueOf; +import static org.junit.Assert.*; /** * Tests the Db.update() function. - * + * * @author dmoebius at scoop dash gmbh dot de */ public class UpdateTest { - private Db db; - - @Before - public void setUp() throws Exception { - db = IciqlSuite.openNewDb(); - db.insertAll(Product.getList()); - db.insertAll(Customer.getList()); - db.insertAll(Order.getList()); - } - - @After - public void tearDown() { - db.close(); - } - - @Test - public void testSimpleUpdate() { - Product p = new Product(); - Product pChang = db.from(p).where(p.productName).is("Chang").selectFirst(); - // update unitPrice from 19.0 to 19.5 - pChang.unitPrice = 19.5; - // update unitsInStock from 17 to 16 - pChang.unitsInStock = 16; - db.update(pChang); - - Product p2 = new Product(); - Product pChang2 = db.from(p2).where(p2.productName).is("Chang").selectFirst(); - assertEquals(19.5, pChang2.unitPrice.doubleValue(), 0.001); - assertEquals(16, pChang2.unitsInStock.intValue()); - - // undo update - pChang.unitPrice = 19.0; - pChang.unitsInStock = 17; - db.update(pChang); - } - - @Test - public void testSimpleUpdateWithCombinedPrimaryKey() { - Order o = new Order(); - Order ourOrder = db.from(o).where(o.orderDate).is(valueOf("2007-01-02")).selectFirst(); - ourOrder.orderDate = valueOf("2007-01-03"); - db.update(ourOrder); - - Order ourUpdatedOrder = db.from(o).where(o.orderDate).is(valueOf("2007-01-03")).selectFirst(); - assertTrue("updated order not found", ourUpdatedOrder != null); - - // undo update - ourOrder.orderDate = valueOf("2007-01-02"); - db.update(ourOrder); - } - - @Test - public void testSimpleMerge() { - Product p = new Product(); - Product pChang = db.from(p).where(p.productName).is("Chang").selectFirst(); - // update unitPrice from 19.0 to 19.5 - pChang.unitPrice = 19.5; - // update unitsInStock from 17 to 16 - pChang.unitsInStock = 16; - db.merge(pChang); - - Product p2 = new Product(); - Product pChang2 = db.from(p2).where(p2.productName).is("Chang").selectFirst(); - assertEquals(19.5, pChang2.unitPrice, 0.001); - assertEquals(16, pChang2.unitsInStock.intValue()); - - // undo update - pChang.unitPrice = 19.0; - pChang.unitsInStock = 17; - db.merge(pChang); - } - - @Test - public void testSimpleMergeWithCombinedPrimaryKey() { - Order o = new Order(); - Order ourOrder = db.from(o).where(o.orderDate).is(valueOf("2007-01-02")).selectFirst(); - ourOrder.orderDate = valueOf("2007-01-03"); - db.merge(ourOrder); - - Order ourUpdatedOrder = db.from(o).where(o.orderDate).is(valueOf("2007-01-03")).selectFirst(); - assertTrue("updated order not found", ourUpdatedOrder != null); - - // undo update - ourOrder.orderDate = valueOf("2007-01-02"); - db.merge(ourOrder); - } - - @Test - public void testSetColumns() { - Product p = new Product(); - Product original = db.from(p).where(p.productId).is(1).selectFirst(); - - // update string and double columns - db.from(p).set(p.productName).to("updated").increment(p.unitPrice).by(3.14).increment(p.unitsInStock) - .by(2).where(p.productId).is(1).update(); - - // confirm the data was properly updated - Product revised = db.from(p).where(p.productId).is(1).selectFirst(); - assertEquals("updated", revised.productName); - assertEquals(original.unitPrice + 3.14, revised.unitPrice, 0.001); - assertEquals(original.unitsInStock + 2, revised.unitsInStock.intValue()); - - // restore the data - db.from(p).set(p.productName).to(original.productName).set(p.unitPrice).to(original.unitPrice) - .increment(p.unitsInStock).by(-2).where(p.productId).is(1).update(); - - // confirm the data was properly restored - Product restored = db.from(p).where(p.productId).is(1).selectFirst(); - assertEquals(original.productName, restored.productName); - assertEquals(original.unitPrice, restored.unitPrice); - assertEquals(original.unitsInStock, restored.unitsInStock); - - double unitPriceOld = db.from(p).where(p.productId).is(1).selectFirst().unitPrice; - // double the unit price - db.from(p).increment(p.unitPrice).by(p.unitPrice).where(p.productId).is(1).update(); - double unitPriceNew = db.from(p).where(p.productId).is(1).selectFirst().unitPrice; - assertEquals(unitPriceOld * 2, unitPriceNew, 0.001); - - } - - @Test - public void testSetNull() { - Product p = new Product(); - Product original = db.from(p).where(p.productId).is(1).selectFirst(); - - String originalName = original.productName; - db.from(p).setNull(p.productName).update(); - - // confirm the data was properly updated - Product revised = db.from(p).where(p.productId).is(1).selectFirst(); - assertNull(revised.productName); - - // restore the data - db.from(p).set(p.productName).to(originalName).update(); - - // confirm the data was properly restored - Product restored = db.from(p).where(p.productId).is(1).selectFirst(); - assertEquals(originalName, restored.productName); - - } + private Db db; + + @Before + public void setUp() throws Exception { + db = IciqlSuite.openNewDb(); + db.insertAll(Product.getList()); + db.insertAll(Customer.getList()); + db.insertAll(Order.getList()); + } + + @After + public void tearDown() { + db.close(); + } + + @Test + public void testSimpleUpdate() { + Product p = new Product(); + Product pChang = db.from(p).where(p.productName).is("Chang").selectFirst(); + // update unitPrice from 19.0 to 19.5 + pChang.unitPrice = 19.5; + // update unitsInStock from 17 to 16 + pChang.unitsInStock = 16; + db.update(pChang); + + Product p2 = new Product(); + Product pChang2 = db.from(p2).where(p2.productName).is("Chang").selectFirst(); + assertEquals(19.5, pChang2.unitPrice.doubleValue(), 0.001); + assertEquals(16, pChang2.unitsInStock.intValue()); + + // undo update + pChang.unitPrice = 19.0; + pChang.unitsInStock = 17; + db.update(pChang); + } + + @Test + public void testSimpleUpdateWithCombinedPrimaryKey() { + Order o = new Order(); + Order ourOrder = db.from(o).where(o.orderDate).is(valueOf("2007-01-02")).selectFirst(); + ourOrder.orderDate = valueOf("2007-01-03"); + db.update(ourOrder); + + Order ourUpdatedOrder = db.from(o).where(o.orderDate).is(valueOf("2007-01-03")).selectFirst(); + assertTrue("updated order not found", ourUpdatedOrder != null); + + // undo update + ourOrder.orderDate = valueOf("2007-01-02"); + db.update(ourOrder); + } + + @Test + public void testSimpleMerge() { + Product p = new Product(); + Product pChang = db.from(p).where(p.productName).is("Chang").selectFirst(); + // update unitPrice from 19.0 to 19.5 + pChang.unitPrice = 19.5; + // update unitsInStock from 17 to 16 + pChang.unitsInStock = 16; + db.merge(pChang); + + Product p2 = new Product(); + Product pChang2 = db.from(p2).where(p2.productName).is("Chang").selectFirst(); + assertEquals(19.5, pChang2.unitPrice, 0.001); + assertEquals(16, pChang2.unitsInStock.intValue()); + + // undo update + pChang.unitPrice = 19.0; + pChang.unitsInStock = 17; + db.merge(pChang); + } + + @Test + public void testSimpleMergeWithCombinedPrimaryKey() { + Order o = new Order(); + Order ourOrder = db.from(o).where(o.orderDate).is(valueOf("2007-01-02")).selectFirst(); + ourOrder.orderDate = valueOf("2007-01-03"); + db.merge(ourOrder); + + Order ourUpdatedOrder = db.from(o).where(o.orderDate).is(valueOf("2007-01-03")).selectFirst(); + assertTrue("updated order not found", ourUpdatedOrder != null); + + // undo update + ourOrder.orderDate = valueOf("2007-01-02"); + db.merge(ourOrder); + } + + @Test + public void testSetColumns() { + Product p = new Product(); + Product original = db.from(p).where(p.productId).is(1).selectFirst(); + + // update string and double columns + db.from(p).set(p.productName).to("updated").increment(p.unitPrice).by(3.14).increment(p.unitsInStock) + .by(2).where(p.productId).is(1).update(); + + // confirm the data was properly updated + Product revised = db.from(p).where(p.productId).is(1).selectFirst(); + assertEquals("updated", revised.productName); + assertEquals(original.unitPrice + 3.14, revised.unitPrice, 0.001); + assertEquals(original.unitsInStock + 2, revised.unitsInStock.intValue()); + + // restore the data + db.from(p).set(p.productName).to(original.productName).set(p.unitPrice).to(original.unitPrice) + .increment(p.unitsInStock).by(-2).where(p.productId).is(1).update(); + + // confirm the data was properly restored + Product restored = db.from(p).where(p.productId).is(1).selectFirst(); + assertEquals(original.productName, restored.productName); + assertEquals(original.unitPrice, restored.unitPrice); + assertEquals(original.unitsInStock, restored.unitsInStock); + + double unitPriceOld = db.from(p).where(p.productId).is(1).selectFirst().unitPrice; + // double the unit price + db.from(p).increment(p.unitPrice).by(p.unitPrice).where(p.productId).is(1).update(); + double unitPriceNew = db.from(p).where(p.productId).is(1).selectFirst().unitPrice; + assertEquals(unitPriceOld * 2, unitPriceNew, 0.001); + + } + + @Test + public void testSetNull() { + Product p = new Product(); + Product original = db.from(p).where(p.productId).is(1).selectFirst(); + + String originalName = original.productName; + db.from(p).setNull(p.productName).update(); + + // confirm the data was properly updated + Product revised = db.from(p).where(p.productId).is(1).selectFirst(); + assertNull(revised.productName); + + // restore the data + db.from(p).set(p.productName).to(originalName).update(); + + // confirm the data was properly restored + Product restored = db.from(p).where(p.productId).is(1).selectFirst(); + assertEquals(originalName, restored.productName); + + } } diff --git a/src/test/java/com/iciql/test/UpgradesTest.java b/src/test/java/com/iciql/test/UpgradesTest.java index 4ab1ff1..82ba372 100644 --- a/src/test/java/com/iciql/test/UpgradesTest.java +++ b/src/test/java/com/iciql/test/UpgradesTest.java @@ -16,167 +16,165 @@ package com.iciql.test; -import static org.junit.Assert.assertEquals; - -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -import org.junit.Test; - import com.iciql.Db; import com.iciql.DbUpgrader; import com.iciql.Iciql.IQVersion; import com.iciql.test.models.Product; import com.iciql.test.models.SupportedTypes; import com.iciql.test.models.SupportedTypes.SupportedTypes2; +import org.junit.Test; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.assertEquals; /** * Tests the database and table upgrade functions. - * */ public class UpgradesTest { - @Test - public void testDatabaseUpgrade() { - Db db = IciqlSuite.openNewDb(); - - List products = Product.getList(); - - // set the v1 upgrader and insert a record. - // this will trigger the upgrade. - V1DbUpgrader v1 = new V1DbUpgrader(); - db.setDbUpgrader(v1); - db.insert(products.get(0)); - - // confirm that upgrade occurred - assertEquals(0, v1.oldVersion.get()); - assertEquals(1, v1.newVersion.get()); - - // open a second connection to the database - // and then apply the v2 upgrade. - // For an in-memory db its important to keep the first connection - // alive so that the database is not destroyed. - Db db2 = IciqlSuite.openCurrentDb(); - - // set the v2 upgrader and insert a record. - // this will trigger the upgrade. - V2DbUpgrader v2 = new V2DbUpgrader(); - db2.setDbUpgrader(v2); - db2.insert(products.get(1)); - - // confirm that upgrade occurred - assertEquals(1, v2.oldVersion.get()); - assertEquals(2, v2.newVersion.get()); - - db.executeUpdate("DROP TABLE iq_versions"); - db.close(); - db2.close(); - } - - @Test - public void testDatabaseInheritedUpgrade() { - Db db = IciqlSuite.openNewDb(); - - List products = Product.getList(); - - // set the v1 upgrader and insert a record. - // this will trigger the upgrade. - V1DbUpgrader v1 = new V1DbUpgrader(); - db.setDbUpgrader(v1); - db.insert(products.get(0)); - - // confirm that upgrade occurred - assertEquals(0, v1.oldVersion.get()); - assertEquals(1, v1.newVersion.get()); - - // open a second connection to the database - // and then apply the v2 upgrade. - // For an in-memory db its important to keep the first connection - // alive so that the database is not destroyed. - Db db2 = IciqlSuite.openCurrentDb(); - - // set the v2 upgrader and insert a record. - // this will trigger the upgrade. - V2DbUpgraderImpl v2 = new V2DbUpgraderImpl(); - db2.setDbUpgrader(v2); - db2.insert(products.get(1)); - - // confirm that upgrade occurred - assertEquals(1, v2.oldVersion.get()); - assertEquals(2, v2.newVersion.get()); - - db.executeUpdate("DROP TABLE iq_versions"); - db.close(); - db2.close(); - } - - @Test - public void testTableUpgrade() { - Db db = IciqlSuite.openNewDb(); - - // insert first, this will create version record automatically - List original = SupportedTypes.createList(); - db.insertAll(original); - - // reset the dbUpgrader (clears the update check cache) - V2DbUpgrader dbUpgrader = new V2DbUpgrader(); - db.setDbUpgrader(dbUpgrader); - - SupportedTypes2 s2 = new SupportedTypes2(); - - List types = db.from(s2).select(); - assertEquals(10, types.size()); - assertEquals(1, dbUpgrader.oldVersion.get()); - assertEquals(2, dbUpgrader.newVersion.get()); - db.executeUpdate("DROP TABLE iq_versions"); - db.close(); - } - - /** - * A sample database upgrader class. - */ - class BaseDbUpgrader implements DbUpgrader { - final AtomicInteger oldVersion = new AtomicInteger(0); - final AtomicInteger newVersion = new AtomicInteger(0); - - @Override - public boolean upgradeTable(Db db, String schema, String table, int fromVersion, int toVersion) { - // just claims success on upgrade request - oldVersion.set(fromVersion); - newVersion.set(toVersion); - return true; - } - - @Override - public boolean upgradeDatabase(Db db, int fromVersion, int toVersion) { - // just claims success on upgrade request - oldVersion.set(fromVersion); - newVersion.set(toVersion); - return true; - } - } - - /** - * A sample V1 database upgrader class. - */ - @IQVersion(1) - class V1DbUpgrader extends BaseDbUpgrader { - } - - /** - * A sample V2 database upgrader class. - */ - @IQVersion(2) - class V2DbUpgrader extends BaseDbUpgrader { - } - - - /** - * A sample V2 database upgrader class which inherits its - * version from the parent class. - */ - @IQVersion() - class V2DbUpgraderImpl extends V2DbUpgrader { - } + @Test + public void testDatabaseUpgrade() { + Db db = IciqlSuite.openNewDb(); + + List products = Product.getList(); + + // set the v1 upgrader and insert a record. + // this will trigger the upgrade. + V1DbUpgrader v1 = new V1DbUpgrader(); + db.setDbUpgrader(v1); + db.insert(products.get(0)); + + // confirm that upgrade occurred + assertEquals(0, v1.oldVersion.get()); + assertEquals(1, v1.newVersion.get()); + + // open a second connection to the database + // and then apply the v2 upgrade. + // For an in-memory db its important to keep the first connection + // alive so that the database is not destroyed. + Db db2 = IciqlSuite.openCurrentDb(); + + // set the v2 upgrader and insert a record. + // this will trigger the upgrade. + V2DbUpgrader v2 = new V2DbUpgrader(); + db2.setDbUpgrader(v2); + db2.insert(products.get(1)); + + // confirm that upgrade occurred + assertEquals(1, v2.oldVersion.get()); + assertEquals(2, v2.newVersion.get()); + + db.executeUpdate("DROP TABLE iq_versions"); + db.close(); + db2.close(); + } + + @Test + public void testDatabaseInheritedUpgrade() { + Db db = IciqlSuite.openNewDb(); + + List products = Product.getList(); + + // set the v1 upgrader and insert a record. + // this will trigger the upgrade. + V1DbUpgrader v1 = new V1DbUpgrader(); + db.setDbUpgrader(v1); + db.insert(products.get(0)); + + // confirm that upgrade occurred + assertEquals(0, v1.oldVersion.get()); + assertEquals(1, v1.newVersion.get()); + + // open a second connection to the database + // and then apply the v2 upgrade. + // For an in-memory db its important to keep the first connection + // alive so that the database is not destroyed. + Db db2 = IciqlSuite.openCurrentDb(); + + // set the v2 upgrader and insert a record. + // this will trigger the upgrade. + V2DbUpgraderImpl v2 = new V2DbUpgraderImpl(); + db2.setDbUpgrader(v2); + db2.insert(products.get(1)); + + // confirm that upgrade occurred + assertEquals(1, v2.oldVersion.get()); + assertEquals(2, v2.newVersion.get()); + + db.executeUpdate("DROP TABLE iq_versions"); + db.close(); + db2.close(); + } + + @Test + public void testTableUpgrade() { + Db db = IciqlSuite.openNewDb(); + + // insert first, this will create version record automatically + List original = SupportedTypes.createList(); + db.insertAll(original); + + // reset the dbUpgrader (clears the update check cache) + V2DbUpgrader dbUpgrader = new V2DbUpgrader(); + db.setDbUpgrader(dbUpgrader); + + SupportedTypes2 s2 = new SupportedTypes2(); + + List types = db.from(s2).select(); + assertEquals(10, types.size()); + assertEquals(1, dbUpgrader.oldVersion.get()); + assertEquals(2, dbUpgrader.newVersion.get()); + db.executeUpdate("DROP TABLE iq_versions"); + db.close(); + } + + /** + * A sample database upgrader class. + */ + class BaseDbUpgrader implements DbUpgrader { + final AtomicInteger oldVersion = new AtomicInteger(0); + final AtomicInteger newVersion = new AtomicInteger(0); + + @Override + public boolean upgradeTable(Db db, String schema, String table, int fromVersion, int toVersion) { + // just claims success on upgrade request + oldVersion.set(fromVersion); + newVersion.set(toVersion); + return true; + } + + @Override + public boolean upgradeDatabase(Db db, int fromVersion, int toVersion) { + // just claims success on upgrade request + oldVersion.set(fromVersion); + newVersion.set(toVersion); + return true; + } + } + + /** + * A sample V1 database upgrader class. + */ + @IQVersion(1) + class V1DbUpgrader extends BaseDbUpgrader { + } + + /** + * A sample V2 database upgrader class. + */ + @IQVersion(2) + class V2DbUpgrader extends BaseDbUpgrader { + } + + + /** + * A sample V2 database upgrader class which inherits its + * version from the parent class. + */ + @IQVersion() + class V2DbUpgraderImpl extends V2DbUpgrader { + } } diff --git a/src/test/java/com/iciql/test/ViewsTest.java b/src/test/java/com/iciql/test/ViewsTest.java index be2e085..cf4df7d 100644 --- a/src/test/java/com/iciql/test/ViewsTest.java +++ b/src/test/java/com/iciql/test/ViewsTest.java @@ -16,15 +16,6 @@ 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; @@ -32,83 +23,91 @@ import com.iciql.test.models.ProductViewFromQuery; import com.iciql.test.models.ProductViewInherited; import com.iciql.test.models.ProductViewInheritedComplex; import com.mysql.jdbc.StringUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * 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 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 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 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 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()); - } - } + /** + * 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 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 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 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 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()); + } + } } diff --git a/src/test/java/com/iciql/test/models/BooleanModel.java b/src/test/java/com/iciql/test/models/BooleanModel.java index 4d75ad9..8da9c54 100644 --- a/src/test/java/com/iciql/test/models/BooleanModel.java +++ b/src/test/java/com/iciql/test/models/BooleanModel.java @@ -15,84 +15,84 @@ */ package com.iciql.test.models; -import java.util.Arrays; -import java.util.List; - import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQTable; +import java.util.Arrays; +import java.util.List; + /** * Boolean types model. */ @IQTable(name = "BooleanTest") public class BooleanModel { - @IQColumn(primaryKey = true) - public Integer id; - - @IQColumn - public Boolean mybool; - - public BooleanModel() { - } - - BooleanModel(int id, boolean val) { - this.id = id; - this.mybool = val; - } - - public static List getList() { - return Arrays.asList(new BooleanModel(1, true), new BooleanModel(2, false), - new BooleanModel(3, true), new BooleanModel(4, false)); - } - - /** - * Test boolean as Integer - */ - @IQTable(name = "BooleanTest") - public static class BooleanAsIntModel { - @IQColumn(primaryKey = true) - public Integer id; - - @IQColumn - public Integer mybool; - - public BooleanAsIntModel() { - } - - BooleanAsIntModel(int id, boolean val) { - this.id = id; - this.mybool = val ? 1 : 0; - } - - public static List getList() { - return Arrays.asList(new BooleanAsIntModel(1, true), new BooleanAsIntModel(2, false), - new BooleanAsIntModel(3, true), new BooleanAsIntModel(4, false)); - } - } - - /** - * Test boolean as primitive short - */ - @IQTable(name = "BooleanTest") - public static class BooleanAsPrimitiveShortModel { - @IQColumn(primaryKey = true) - public Integer id; - - @IQColumn - public short mybool; - - public BooleanAsPrimitiveShortModel() { - } - - BooleanAsPrimitiveShortModel(int id, boolean val) { - this.id = id; - this.mybool = (short) (val ? 1 : 0); - } - - public static List getList() { - return Arrays.asList(new BooleanAsPrimitiveShortModel(1, true), new BooleanAsPrimitiveShortModel(2, false), - new BooleanAsPrimitiveShortModel(3, true), new BooleanAsPrimitiveShortModel(4, false)); - } - } + @IQColumn(primaryKey = true) + public Integer id; + + @IQColumn + public Boolean mybool; + + public BooleanModel() { + } + + BooleanModel(int id, boolean val) { + this.id = id; + this.mybool = val; + } + + public static List getList() { + return Arrays.asList(new BooleanModel(1, true), new BooleanModel(2, false), + new BooleanModel(3, true), new BooleanModel(4, false)); + } + + /** + * Test boolean as Integer + */ + @IQTable(name = "BooleanTest") + public static class BooleanAsIntModel { + @IQColumn(primaryKey = true) + public Integer id; + + @IQColumn + public Integer mybool; + + public BooleanAsIntModel() { + } + + BooleanAsIntModel(int id, boolean val) { + this.id = id; + this.mybool = val ? 1 : 0; + } + + public static List getList() { + return Arrays.asList(new BooleanAsIntModel(1, true), new BooleanAsIntModel(2, false), + new BooleanAsIntModel(3, true), new BooleanAsIntModel(4, false)); + } + } + + /** + * Test boolean as primitive short + */ + @IQTable(name = "BooleanTest") + public static class BooleanAsPrimitiveShortModel { + @IQColumn(primaryKey = true) + public Integer id; + + @IQColumn + public short mybool; + + public BooleanAsPrimitiveShortModel() { + } + + BooleanAsPrimitiveShortModel(int id, boolean val) { + this.id = id; + this.mybool = (short) (val ? 1 : 0); + } + + public static List getList() { + return Arrays.asList(new BooleanAsPrimitiveShortModel(1, true), new BooleanAsPrimitiveShortModel(2, false), + new BooleanAsPrimitiveShortModel(3, true), new BooleanAsPrimitiveShortModel(4, false)); + } + } } diff --git a/src/test/java/com/iciql/test/models/CategoryAnnotationOnly.java b/src/test/java/com/iciql/test/models/CategoryAnnotationOnly.java index 2c01afa..8e53822 100644 --- a/src/test/java/com/iciql/test/models/CategoryAnnotationOnly.java +++ b/src/test/java/com/iciql/test/models/CategoryAnnotationOnly.java @@ -17,57 +17,57 @@ 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.IQIndex; import com.iciql.Iciql.IQTable; import com.iciql.Iciql.IndexType; +import java.util.Arrays; +import java.util.List; + /** * A table containing category data. */ @IQTable(name = "AnnotatedCategory", primaryKey = "id") -@IQIndex(value = "categ", type=IndexType.UNIQUE) -@IQContraintUnique(uniqueColumns = { "categ" }) +@IQIndex(value = "categ", type = IndexType.UNIQUE) +@IQContraintUnique(uniqueColumns = {"categ"}) public class CategoryAnnotationOnly { - @IQColumn(name = "id", autoIncrement = true) - public Long categoryId; + @IQColumn(name = "id", autoIncrement = true) + public Long categoryId; - @IQColumn(name = "categ", length = 15, trim = true) - public String category; + @IQColumn(name = "categ", length = 15, trim = true) + public String category; - public CategoryAnnotationOnly() { - // public constructor - } + public CategoryAnnotationOnly() { + // public constructor + } - private CategoryAnnotationOnly(long categoryId, String category) { - this.categoryId = categoryId; - this.category = category; - } + 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); - } + private static CategoryAnnotationOnly create(int categoryId, String category) { + return new CategoryAnnotationOnly(categoryId, category); + } - public static List getList() { - CategoryAnnotationOnly[] list = { - create(1, "Beverages"), - create(2, "Condiments"), - create(3, "Produce"), - create(4, "Meat/Poultry"), - create(5,"Seafood") - }; - return Arrays.asList(list); - } + public static List getList() { + CategoryAnnotationOnly[] list = { + create(1, "Beverages"), + create(2, "Condiments"), + create(3, "Produce"), + create(4, "Meat/Poultry"), + create(5, "Seafood") + }; + return Arrays.asList(list); + } - @Override - public String toString() { - return category; - } + @Override + public String toString() { + return category; + } } diff --git a/src/test/java/com/iciql/test/models/ComplexObject.java b/src/test/java/com/iciql/test/models/ComplexObject.java index ce9b9ec..8a851e2 100644 --- a/src/test/java/com/iciql/test/models/ComplexObject.java +++ b/src/test/java/com/iciql/test/models/ComplexObject.java @@ -17,8 +17,7 @@ package com.iciql.test.models; -import static com.iciql.Define.length; -import static com.iciql.Define.primaryKey; +import com.iciql.Iciql; import java.math.BigDecimal; import java.sql.Time; @@ -27,40 +26,41 @@ import java.util.Arrays; import java.util.Date; import java.util.List; -import com.iciql.Iciql; +import static com.iciql.Define.length; +import static com.iciql.Define.primaryKey; /** * A table containing all possible data types. */ public class ComplexObject implements Iciql { - public Integer id; - public Long amount; - public String name; - public BigDecimal value; - public Date birthday; - public Time time; - public Timestamp created; + public Integer id; + public Long amount; + public String name; + public BigDecimal value; + public Date birthday; + public Time time; + public Timestamp created; - static ComplexObject build(Integer id, boolean isNull) { - ComplexObject obj = new ComplexObject(); - obj.id = id; - obj.amount = isNull ? null : Long.valueOf(1); - obj.name = isNull ? null : "hello"; - obj.value = isNull ? null : new BigDecimal("1"); - obj.birthday = isNull ? null : java.sql.Date.valueOf("2001-01-01"); - obj.time = isNull ? null : Time.valueOf("10:20:30"); - obj.created = isNull ? null : Timestamp.valueOf("2002-02-02 02:02:02"); - return obj; - } + static ComplexObject build(Integer id, boolean isNull) { + ComplexObject obj = new ComplexObject(); + obj.id = id; + obj.amount = isNull ? null : Long.valueOf(1); + obj.name = isNull ? null : "hello"; + obj.value = isNull ? null : new BigDecimal("1"); + obj.birthday = isNull ? null : java.sql.Date.valueOf("2001-01-01"); + obj.time = isNull ? null : Time.valueOf("10:20:30"); + obj.created = isNull ? null : Timestamp.valueOf("2002-02-02 02:02:02"); + return obj; + } - public void defineIQ() { - primaryKey(id); - length(name, 25); - } + public void defineIQ() { + primaryKey(id); + length(name, 25); + } - public static List getList() { - return Arrays.asList(new ComplexObject[] { build(0, true), build(1, false) }); - } + public static List getList() { + return Arrays.asList(new ComplexObject[]{build(0, true), build(1, false)}); + } } diff --git a/src/test/java/com/iciql/test/models/Customer.java b/src/test/java/com/iciql/test/models/Customer.java index 1537c53..898c5b5 100644 --- a/src/test/java/com/iciql/test/models/Customer.java +++ b/src/test/java/com/iciql/test/models/Customer.java @@ -17,48 +17,48 @@ package com.iciql.test.models; -import java.util.Arrays; -import java.util.List; - import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQTable; +import java.util.Arrays; +import java.util.List; + /** * A table containing customer data. */ @IQTable public class Customer { - @IQColumn(length = 25) - public String customerId; + @IQColumn(length = 25) + public String customerId; - @IQColumn(length = 2) - public String region; + @IQColumn(length = 2) + public String region; - public Customer() { - // public constructor - } + public Customer() { + // public constructor + } - public Customer(String customerId, String region) { - this.customerId = customerId; - this.region = region; - } + public Customer(String customerId, String region) { + this.customerId = customerId; + this.region = region; + } - @Override - public String toString() { - return customerId; - } + @Override + public String toString() { + return customerId; + } - public static List getList() { - return Arrays.asList( - new Customer("ALFKI", "WA"), - new Customer("ANATR", "WA"), - new Customer("ASLAN", "CA"), - new Customer("ANTON", "CA"), - new Customer("BROWN", "LA"), - new Customer("SMITH", "NY"), - new Customer("JONES", "ME"), - new Customer(null, null)); - } + public static List getList() { + return Arrays.asList( + new Customer("ALFKI", "WA"), + new Customer("ANATR", "WA"), + new Customer("ASLAN", "CA"), + new Customer("ANTON", "CA"), + new Customer("BROWN", "LA"), + new Customer("SMITH", "NY"), + new Customer("JONES", "ME"), + new Customer(null, null)); + } } diff --git a/src/test/java/com/iciql/test/models/DefaultValuesModel.java b/src/test/java/com/iciql/test/models/DefaultValuesModel.java index cb3b421..937f2b1 100644 --- a/src/test/java/com/iciql/test/models/DefaultValuesModel.java +++ b/src/test/java/com/iciql/test/models/DefaultValuesModel.java @@ -15,44 +15,44 @@ */ package com.iciql.test.models; -import java.util.Date; - import com.iciql.Iciql.EnumType; import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQEnum; import com.iciql.Iciql.IQTable; import com.iciql.test.models.EnumModels.Tree; +import java.util.Date; + /** * Default values model. */ @IQTable(name = "DefaultValuesTest") public class DefaultValuesModel { - @IQColumn(primaryKey = true, autoIncrement = true) - public Long myLong; + @IQColumn(primaryKey = true, autoIncrement = true) + public Long myLong; - @SuppressWarnings("deprecation") - @IQColumn - public Date myDate = new Date(100, 7, 1); + @SuppressWarnings("deprecation") + @IQColumn + public Date myDate = new Date(100, 7, 1); - @IQColumn - public Integer myInteger = 12345; + @IQColumn + public Integer myInteger = 12345; - @IQColumn - public Tree myEnumIdTree = Tree.WALNUT; + @IQColumn + public Tree myEnumIdTree = Tree.WALNUT; - @IQColumn - @IQEnum(EnumType.NAME) - public Tree myNameTree = Tree.MAPLE; + @IQColumn + @IQEnum(EnumType.NAME) + public Tree myNameTree = Tree.MAPLE; - @IQColumn - @IQEnum(EnumType.ORDINAL) - public Tree myOrdinalTree = Tree.PINE; + @IQColumn + @IQEnum(EnumType.ORDINAL) + public Tree myOrdinalTree = Tree.PINE; - @IQColumn(nullable = true) - public Tree myNullTree; + @IQColumn(nullable = true) + public Tree myNullTree; - public DefaultValuesModel() { - } + public DefaultValuesModel() { + } } diff --git a/src/test/java/com/iciql/test/models/EnumModels.java b/src/test/java/com/iciql/test/models/EnumModels.java index 2320734..de22328 100644 --- a/src/test/java/com/iciql/test/models/EnumModels.java +++ b/src/test/java/com/iciql/test/models/EnumModels.java @@ -16,208 +16,208 @@ package com.iciql.test.models; -import java.util.Arrays; -import java.util.List; - import com.iciql.Iciql.EnumId; import com.iciql.Iciql.EnumType; import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQEnum; import com.iciql.Iciql.IQTable; +import java.util.Arrays; +import java.util.List; + /** * Container for reusable enum model classes which exercise the 3 supported * types. */ public abstract class EnumModels { - /** - * Test of @IQEnum annotated enumeration. This strategy is the default - * strategy for all fields of the Tree enum. - * - * Individual Tree field declarations can override this strategy by - * specifying a different @IQEnum annotation. - * - * Here ORDINAL specifies that this enum will be mapped to an INT column. - */ - @IQEnum(EnumType.ENUMID) - public enum Tree implements EnumId { - PINE(10), OAK(20), BIRCH(30), WALNUT(40), MAPLE(50); - - private int enumid; - - Tree(int id) { - this.enumid = id; - } - - @Override - public Integer enumId() { - return enumid; - } - - @Override - public Class enumIdClass() { - return Integer.class; - } - - } - - /** - * Enum for testing custom ENUMID mapping. - */ - @IQEnum(EnumType.ENUMID) - public enum Genus implements EnumId { - PINUS("pinaceae"), QUERCUS("fagaceae"), BETULA("betulaceae"), JUGLANS("juglandaceae"), ACER("aceraceae"); - - private String family; - - Genus(String id) { - this.family = id; - } - - @Override - public String enumId() { - return family; - } - - @Override - public Class enumIdClass() { - return String.class; - } - } - - @IQColumn(primaryKey = true) - public Integer id; - - public abstract Tree tree(); - - public abstract Genus genus(); - - /** - * Test model for enum-as-enumid. - */ - @IQTable(inheritColumns = true) - public static class EnumIdModel extends EnumModels { - - // no need to specify ENUMID type as the enumeration definition - // specifies it. - @IQColumn - private Tree tree; - - // no need to specify ENUMID type as the enumeration definition - // specifies it. - @IQColumn - private Genus genus; - - public EnumIdModel() { - } - - public EnumIdModel(int id, Tree tree, Genus genus) { - this.id = id; - this.tree = tree; - this.genus = genus; - } - - @Override - public Tree tree() { - return tree; - } - - @Override - public Genus genus() { - return genus; - } - - public static List createList() { - return Arrays.asList(new EnumIdModel(400, Tree.WALNUT, Genus.JUGLANS), - new EnumIdModel(200, Tree.OAK, Genus.QUERCUS), - new EnumIdModel(500, Tree.MAPLE, Genus.ACER), - new EnumIdModel(300, Tree.BIRCH, Genus.BETULA), - new EnumIdModel(100, Tree.PINE, Genus.PINUS)); - } - } - - /** - * Test model for enum-as-ordinal. - */ - @IQTable(inheritColumns = true) - public static class EnumOrdinalModel extends EnumModels { - - // override the enumtype to ordinal - @IQEnum(EnumType.ORDINAL) - @IQColumn - private Tree tree; - - @IQColumn - private Genus genus; - - public EnumOrdinalModel() { - } - - public EnumOrdinalModel(int id, Tree tree, Genus genus) { - this.id = id; - this.tree = tree; - } - - @Override - public Tree tree() { - return tree; - } - - @Override - public Genus genus() { - return genus; - } - - public static List createList() { - return Arrays.asList(new EnumOrdinalModel(400, Tree.WALNUT, Genus.JUGLANS), - new EnumOrdinalModel(200, Tree.OAK, Genus.QUERCUS), - new EnumOrdinalModel(500, Tree.MAPLE, Genus.ACER), - new EnumOrdinalModel(300, Tree.BIRCH, Genus.BETULA), - new EnumOrdinalModel(100, Tree.PINE, Genus.PINUS)); - } - } - - /** - * Test model for enum-as-string. - */ - @IQTable(inheritColumns = true) - public static class EnumStringModel extends EnumModels { - - // override the enumtype to string - // ensure that we specify a length so that the column is VARCHAR - @IQEnum(EnumType.NAME) - @IQColumn(length = 25) - private Tree tree; - - @IQColumn(trim = true, length = 25) - private Genus genus; - - public EnumStringModel() { - } - - public EnumStringModel(int id, Tree tree, Genus genus) { - this.id = id; - this.tree = tree; - this.genus = genus; - } - - @Override - public Tree tree() { - return tree; - } - - @Override - public Genus genus() { - return genus; - } - - public static List createList() { - return Arrays.asList(new EnumStringModel(400, Tree.WALNUT, Genus.JUGLANS), - new EnumStringModel(200, Tree.OAK, Genus.QUERCUS), - new EnumStringModel(500, Tree.MAPLE, Genus.ACER), - new EnumStringModel(300, Tree.BIRCH, Genus.BETULA), - new EnumStringModel(100, Tree.PINE, Genus.PINUS)); - } - } + /** + * Test of @IQEnum annotated enumeration. This strategy is the default + * strategy for all fields of the Tree enum. + *

+ * Individual Tree field declarations can override this strategy by + * specifying a different @IQEnum annotation. + *

+ * Here ORDINAL specifies that this enum will be mapped to an INT column. + */ + @IQEnum(EnumType.ENUMID) + public enum Tree implements EnumId { + PINE(10), OAK(20), BIRCH(30), WALNUT(40), MAPLE(50); + + private int enumid; + + Tree(int id) { + this.enumid = id; + } + + @Override + public Integer enumId() { + return enumid; + } + + @Override + public Class enumIdClass() { + return Integer.class; + } + + } + + /** + * Enum for testing custom ENUMID mapping. + */ + @IQEnum(EnumType.ENUMID) + public enum Genus implements EnumId { + PINUS("pinaceae"), QUERCUS("fagaceae"), BETULA("betulaceae"), JUGLANS("juglandaceae"), ACER("aceraceae"); + + private String family; + + Genus(String id) { + this.family = id; + } + + @Override + public String enumId() { + return family; + } + + @Override + public Class enumIdClass() { + return String.class; + } + } + + @IQColumn(primaryKey = true) + public Integer id; + + public abstract Tree tree(); + + public abstract Genus genus(); + + /** + * Test model for enum-as-enumid. + */ + @IQTable(inheritColumns = true) + public static class EnumIdModel extends EnumModels { + + // no need to specify ENUMID type as the enumeration definition + // specifies it. + @IQColumn + private Tree tree; + + // no need to specify ENUMID type as the enumeration definition + // specifies it. + @IQColumn + private Genus genus; + + public EnumIdModel() { + } + + public EnumIdModel(int id, Tree tree, Genus genus) { + this.id = id; + this.tree = tree; + this.genus = genus; + } + + @Override + public Tree tree() { + return tree; + } + + @Override + public Genus genus() { + return genus; + } + + public static List createList() { + return Arrays.asList(new EnumIdModel(400, Tree.WALNUT, Genus.JUGLANS), + new EnumIdModel(200, Tree.OAK, Genus.QUERCUS), + new EnumIdModel(500, Tree.MAPLE, Genus.ACER), + new EnumIdModel(300, Tree.BIRCH, Genus.BETULA), + new EnumIdModel(100, Tree.PINE, Genus.PINUS)); + } + } + + /** + * Test model for enum-as-ordinal. + */ + @IQTable(inheritColumns = true) + public static class EnumOrdinalModel extends EnumModels { + + // override the enumtype to ordinal + @IQEnum(EnumType.ORDINAL) + @IQColumn + private Tree tree; + + @IQColumn + private Genus genus; + + public EnumOrdinalModel() { + } + + public EnumOrdinalModel(int id, Tree tree, Genus genus) { + this.id = id; + this.tree = tree; + } + + @Override + public Tree tree() { + return tree; + } + + @Override + public Genus genus() { + return genus; + } + + public static List createList() { + return Arrays.asList(new EnumOrdinalModel(400, Tree.WALNUT, Genus.JUGLANS), + new EnumOrdinalModel(200, Tree.OAK, Genus.QUERCUS), + new EnumOrdinalModel(500, Tree.MAPLE, Genus.ACER), + new EnumOrdinalModel(300, Tree.BIRCH, Genus.BETULA), + new EnumOrdinalModel(100, Tree.PINE, Genus.PINUS)); + } + } + + /** + * Test model for enum-as-string. + */ + @IQTable(inheritColumns = true) + public static class EnumStringModel extends EnumModels { + + // override the enumtype to string + // ensure that we specify a length so that the column is VARCHAR + @IQEnum(EnumType.NAME) + @IQColumn(length = 25) + private Tree tree; + + @IQColumn(trim = true, length = 25) + private Genus genus; + + public EnumStringModel() { + } + + public EnumStringModel(int id, Tree tree, Genus genus) { + this.id = id; + this.tree = tree; + this.genus = genus; + } + + @Override + public Tree tree() { + return tree; + } + + @Override + public Genus genus() { + return genus; + } + + public static List createList() { + return Arrays.asList(new EnumStringModel(400, Tree.WALNUT, Genus.JUGLANS), + new EnumStringModel(200, Tree.OAK, Genus.QUERCUS), + new EnumStringModel(500, Tree.MAPLE, Genus.ACER), + new EnumStringModel(300, Tree.BIRCH, Genus.BETULA), + new EnumStringModel(100, Tree.PINE, Genus.PINUS)); + } + } } diff --git a/src/test/java/com/iciql/test/models/MultipleBoolsModel.java b/src/test/java/com/iciql/test/models/MultipleBoolsModel.java index 7bc429c..7bdfd40 100644 --- a/src/test/java/com/iciql/test/models/MultipleBoolsModel.java +++ b/src/test/java/com/iciql/test/models/MultipleBoolsModel.java @@ -1,40 +1,39 @@ package com.iciql.test.models; -import java.util.Arrays; -import java.util.List; - import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQTable; +import java.util.Arrays; +import java.util.List; + /** * Model class to test the runtime exception of too many primitive boolean * fields in the model. - * + * * @author James Moger - * */ @IQTable public class MultipleBoolsModel { - @IQColumn(autoIncrement = true, primaryKey = true) - public int id; + @IQColumn(autoIncrement = true, primaryKey = true) + public int id; - @IQColumn - public boolean a; + @IQColumn + public boolean a; - @IQColumn - public boolean b; + @IQColumn + public boolean b; - public MultipleBoolsModel() { - } + public MultipleBoolsModel() { + } - public MultipleBoolsModel(boolean a, boolean b) { - this.a = a; - this.b = b; - } + public MultipleBoolsModel(boolean a, boolean b) { + this.a = a; + this.b = b; + } - public static List getList() { - return Arrays.asList(new MultipleBoolsModel(true, true), new MultipleBoolsModel(true, false), - new MultipleBoolsModel(true, false), new MultipleBoolsModel(false, false)); - } + public static List getList() { + return Arrays.asList(new MultipleBoolsModel(true, true), new MultipleBoolsModel(true, false), + new MultipleBoolsModel(true, false), new MultipleBoolsModel(false, false)); + } } \ No newline at end of file diff --git a/src/test/java/com/iciql/test/models/Order.java b/src/test/java/com/iciql/test/models/Order.java index 1fa9097..d406765 100644 --- a/src/test/java/com/iciql/test/models/Order.java +++ b/src/test/java/com/iciql/test/models/Order.java @@ -17,57 +17,54 @@ package com.iciql.test.models; -import static com.iciql.Define.length; -import static com.iciql.Define.primaryKey; -import static com.iciql.Define.scale; -import static com.iciql.Define.tableName; +import com.iciql.Iciql; import java.math.BigDecimal; import java.util.Arrays; import java.util.Date; import java.util.List; -import com.iciql.Iciql; +import static com.iciql.Define.*; /** * A table containing order data. */ public class Order implements Iciql { - public String customerId; - public Integer orderId; - public Date orderDate; - public BigDecimal total; + public String customerId; + public Integer orderId; + public Date orderDate; + public BigDecimal total; - public Order(String customerId, Integer orderId, String total, String orderDate) { - this.customerId = customerId; - this.orderId = orderId; - this.total = new BigDecimal(total); - this.orderDate = java.sql.Date.valueOf(orderDate); - } + public Order(String customerId, Integer orderId, String total, String orderDate) { + this.customerId = customerId; + this.orderId = orderId; + this.total = new BigDecimal(total); + this.orderDate = java.sql.Date.valueOf(orderDate); + } - public Order() { - // public constructor - } + public Order() { + // public constructor + } - public void defineIQ() { - tableName("Orders"); - length(customerId, 25); - length(total, 10); - scale(total, 2); - primaryKey(customerId, orderId); - } + public void defineIQ() { + tableName("Orders"); + length(customerId, 25); + length(total, 10); + scale(total, 2); + primaryKey(customerId, orderId); + } - public static List getList() { - Order[] list = { new Order("ALFKI", 10702, "330.00", "2007-01-02"), - new Order("ALFKI", 10952, "471.20", "2007-02-03"), - new Order("ANATR", 10308, "88.80", "2007-01-03"), - new Order("ANATR", 10625, "479.75", "2007-03-03"), - new Order("ANATR", 10759, "320.00", "2007-04-01"), - new Order("ANTON", 10365, "403.20", "2007-02-13"), - new Order("ANTON", 10682, "375.50", "2007-03-13"), - new Order("ANTON", 10355, "480.00", "2007-04-11") }; - return Arrays.asList(list); - } + public static List getList() { + Order[] list = {new Order("ALFKI", 10702, "330.00", "2007-01-02"), + new Order("ALFKI", 10952, "471.20", "2007-02-03"), + new Order("ANATR", 10308, "88.80", "2007-01-03"), + new Order("ANATR", 10625, "479.75", "2007-03-03"), + new Order("ANATR", 10759, "320.00", "2007-04-01"), + new Order("ANTON", 10365, "403.20", "2007-02-13"), + new Order("ANTON", 10682, "375.50", "2007-03-13"), + new Order("ANTON", 10355, "480.00", "2007-04-11")}; + return Arrays.asList(list); + } } diff --git a/src/test/java/com/iciql/test/models/PrimitivesModel.java b/src/test/java/com/iciql/test/models/PrimitivesModel.java index 44e8b9b..078b565 100644 --- a/src/test/java/com/iciql/test/models/PrimitivesModel.java +++ b/src/test/java/com/iciql/test/models/PrimitivesModel.java @@ -15,76 +15,76 @@ */ package com.iciql.test.models; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQTable; import com.iciql.test.IciqlSuite; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + /** * Primitive types model. */ @IQTable(name = "PrimitivesTest") public class PrimitivesModel { - @IQColumn(primaryKey = true) - public long myLong; + @IQColumn(primaryKey = true) + public long myLong; - @IQColumn - public int myInteger; + @IQColumn + public int myInteger; - @IQColumn - public short myShort; + @IQColumn + public short myShort; - @IQColumn - public byte myByte; + @IQColumn + public byte myByte; - @IQColumn - public boolean myBoolean; + @IQColumn + public boolean myBoolean; - @IQColumn - public double myDouble; + @IQColumn + public double myDouble; - @IQColumn - public float myFloat; + @IQColumn + public float myFloat; - public PrimitivesModel() { - Random rand = new Random(); - myLong = rand.nextLong(); - myInteger = rand.nextInt(); - myShort = (short) rand.nextInt(Short.MAX_VALUE); - myByte = (byte) rand.nextInt(Byte.MAX_VALUE); - myBoolean = rand.nextInt(1) == 1; - myDouble = rand.nextDouble(); - myFloat = rand.nextFloat(); - } + public PrimitivesModel() { + Random rand = new Random(); + myLong = rand.nextLong(); + myInteger = rand.nextInt(); + myShort = (short) rand.nextInt(Short.MAX_VALUE); + myByte = (byte) rand.nextInt(Byte.MAX_VALUE); + myBoolean = rand.nextInt(1) == 1; + myDouble = rand.nextDouble(); + myFloat = rand.nextFloat(); + } - public boolean equivalentTo(PrimitivesModel p) { - boolean same = true; - same &= myLong == p.myLong; - same &= myInteger == p.myInteger; - same &= myShort == p.myShort; - same &= myByte == p.myByte; - same &= myBoolean == p.myBoolean; - same &= IciqlSuite.equivalentTo(myDouble, p.myDouble); - same &= IciqlSuite.equivalentTo(myFloat, p.myFloat); - return same; - } + public boolean equivalentTo(PrimitivesModel p) { + boolean same = true; + same &= myLong == p.myLong; + same &= myInteger == p.myInteger; + same &= myShort == p.myShort; + same &= myByte == p.myByte; + same &= myBoolean == p.myBoolean; + same &= IciqlSuite.equivalentTo(myDouble, p.myDouble); + same &= IciqlSuite.equivalentTo(myFloat, p.myFloat); + return same; + } - public static List getList() { - List list = new ArrayList(); - for (int i = 1; i <= 10; i++) { - PrimitivesModel p = new PrimitivesModel(); - p.myLong = i; - list.add(p); - } - return list; - } + public static List getList() { + List list = new ArrayList(); + for (int i = 1; i <= 10; i++) { + PrimitivesModel p = new PrimitivesModel(); + p.myLong = i; + list.add(p); + } + return list; + } - @Override - public String toString() { - return String.valueOf(myLong); - } + @Override + public String toString() { + return String.valueOf(myLong); + } } diff --git a/src/test/java/com/iciql/test/models/Product.java b/src/test/java/com/iciql/test/models/Product.java index 7feb998..bebc4a9 100644 --- a/src/test/java/com/iciql/test/models/Product.java +++ b/src/test/java/com/iciql/test/models/Product.java @@ -17,15 +17,12 @@ package com.iciql.test.models; -import static com.iciql.Define.index; -import static com.iciql.Define.length; -import static com.iciql.Define.primaryKey; -import static com.iciql.Define.tableName; +import com.iciql.Iciql; import java.util.Arrays; import java.util.List; -import com.iciql.Iciql; +import static com.iciql.Define.*; /** * A table containing product data. @@ -33,63 +30,63 @@ import com.iciql.Iciql; public class Product implements Iciql { - public Integer productId; - public String productName; - public String category; - public Double unitPrice; - public Integer unitsInStock; - - public Product() { - // public constructor - } - - private Product(int productId, String productName, String category, double unitPrice, int unitsInStock) { - this.productId = productId; - this.productName = productName; - this.category = category; - this.unitPrice = unitPrice; - this.unitsInStock = unitsInStock; - } - - public String getName() { - return productName; - } - - public int getId() { - return productId; - } - - @Override - public void defineIQ() { - tableName("Product"); - primaryKey(productId); - length(productName, 255); - length(category, 255); - index("MyIndex", IndexType.STANDARD, productName, category); - } - - private static Product create(int productId, String productName, String category, double unitPrice, - int unitsInStock) { - return new Product(productId, productName, category, unitPrice, unitsInStock); - } - - public static List getList() { - Product[] list = { create(1, "Chai", "Beverages", 18, 39), create(2, "Chang", "Beverages", 19.0, 17), - create(3, "Aniseed Syrup", "Condiments", 10.0, 13), - create(4, "Chef Anton's Cajun Seasoning", "Condiments", 22.0, 53), - create(5, "Chef Anton's Gumbo Mix", "Condiments", 21.3500, 0), - create(6, "Grandma's Boysenberry Spread", "Condiments", 25.0, 120), - create(7, "Uncle Bob's Organic Dried Pears", "Produce", 30.0, 15), - create(8, "Northwoods Cranberry Sauce", "Condiments", 40.0, 6), - create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29), - create(10, "Ikura", "Seafood", 31.0, 31), }; - - return Arrays.asList(list); - } - - @Override - public String toString() { - return productName + ": " + unitsInStock; - } + public Integer productId; + public String productName; + public String category; + public Double unitPrice; + public Integer unitsInStock; + + public Product() { + // public constructor + } + + private Product(int productId, String productName, String category, double unitPrice, int unitsInStock) { + this.productId = productId; + this.productName = productName; + this.category = category; + this.unitPrice = unitPrice; + this.unitsInStock = unitsInStock; + } + + public String getName() { + return productName; + } + + public int getId() { + return productId; + } + + @Override + public void defineIQ() { + tableName("Product"); + primaryKey(productId); + length(productName, 255); + length(category, 255); + index("MyIndex", IndexType.STANDARD, productName, category); + } + + private static Product create(int productId, String productName, String category, double unitPrice, + int unitsInStock) { + return new Product(productId, productName, category, unitPrice, unitsInStock); + } + + public static List getList() { + Product[] list = {create(1, "Chai", "Beverages", 18, 39), create(2, "Chang", "Beverages", 19.0, 17), + create(3, "Aniseed Syrup", "Condiments", 10.0, 13), + create(4, "Chef Anton's Cajun Seasoning", "Condiments", 22.0, 53), + create(5, "Chef Anton's Gumbo Mix", "Condiments", 21.3500, 0), + create(6, "Grandma's Boysenberry Spread", "Condiments", 25.0, 120), + create(7, "Uncle Bob's Organic Dried Pears", "Produce", 30.0, 15), + create(8, "Northwoods Cranberry Sauce", "Condiments", 40.0, 6), + create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29), + create(10, "Ikura", "Seafood", 31.0, 31),}; + + return Arrays.asList(list); + } + + @Override + public String toString() { + return productName + ": " + unitsInStock; + } } diff --git a/src/test/java/com/iciql/test/models/ProductAnnotationOnly.java b/src/test/java/com/iciql/test/models/ProductAnnotationOnly.java index ea5856b..c091b0e 100644 --- a/src/test/java/com/iciql/test/models/ProductAnnotationOnly.java +++ b/src/test/java/com/iciql/test/models/ProductAnnotationOnly.java @@ -17,79 +17,79 @@ package com.iciql.test.models; -import java.util.Arrays; -import java.util.List; - import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQIndex; import com.iciql.Iciql.IQIndexes; import com.iciql.Iciql.IQTable; import com.iciql.Iciql.IndexType; +import java.util.Arrays; +import java.util.List; + /** * A table containing product data. */ @IQTable(name = "AnnotatedProduct") -@IQIndexes({ @IQIndex({ "name", "cat" }), @IQIndex(name = "nameidx", type = IndexType.HASH, value = "name") }) +@IQIndexes({@IQIndex({"name", "cat"}), @IQIndex(name = "nameidx", type = IndexType.HASH, value = "name")}) public class ProductAnnotationOnly { - public String unmappedField; - - @IQColumn(name = "id", autoIncrement = true, primaryKey = 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 ProductAnnotationOnly() { - // public constructor - } - - private ProductAnnotationOnly(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 ProductAnnotationOnly create(int productId, String productName, String category, - double unitPrice, int unitsInStock, String unmappedField) { - return new ProductAnnotationOnly(productId, productName, category, unitPrice, unitsInStock, - unmappedField); - } - - public static List getList() { - String unmappedField = "unmapped"; - ProductAnnotationOnly[] 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); - } - - @Override - public String toString() { - return productName + ": " + unitsInStock; - } + public String unmappedField; + + @IQColumn(name = "id", autoIncrement = true, primaryKey = 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 ProductAnnotationOnly() { + // public constructor + } + + private ProductAnnotationOnly(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 ProductAnnotationOnly create(int productId, String productName, String category, + double unitPrice, int unitsInStock, String unmappedField) { + return new ProductAnnotationOnly(productId, productName, category, unitPrice, unitsInStock, + unmappedField); + } + + public static List getList() { + String unmappedField = "unmapped"; + ProductAnnotationOnly[] 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); + } + + @Override + public String toString() { + return productName + ": " + unitsInStock; + } } diff --git a/src/test/java/com/iciql/test/models/ProductAnnotationOnlyWithForeignKey.java b/src/test/java/com/iciql/test/models/ProductAnnotationOnlyWithForeignKey.java index 4268a89..d6edc1a 100644 --- a/src/test/java/com/iciql/test/models/ProductAnnotationOnlyWithForeignKey.java +++ b/src/test/java/com/iciql/test/models/ProductAnnotationOnlyWithForeignKey.java @@ -17,9 +17,6 @@ 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; @@ -28,75 +25,78 @@ import com.iciql.Iciql.IQIndexes; import com.iciql.Iciql.IQTable; import com.iciql.Iciql.IndexType; +import java.util.Arrays; +import java.util.List; + /** * A table containing product data. */ @IQTable(name = "AnnotatedProduct", primaryKey = "id") -@IQIndexes({ @IQIndex({ "name", "cat" }), @IQIndex(name = "nameidx", type = IndexType.HASH, value = "name") }) +@IQIndexes({@IQIndex({"name", "cat"}), @IQIndex(name = "nameidx", type = IndexType.HASH, value = "name")}) @IQContraintForeignKey( - foreignColumns= { "cat" }, - referenceName = "AnnotatedCategory", - referenceColumns = { "categ" }, - deleteType = ConstraintDeleteType.CASCADE + foreignColumns = {"cat"}, + referenceName = "AnnotatedCategory", + 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 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; - } + 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 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; + } } diff --git a/src/test/java/com/iciql/test/models/ProductInheritedAnnotation.java b/src/test/java/com/iciql/test/models/ProductInheritedAnnotation.java index 112f4ef..c41a3f0 100644 --- a/src/test/java/com/iciql/test/models/ProductInheritedAnnotation.java +++ b/src/test/java/com/iciql/test/models/ProductInheritedAnnotation.java @@ -17,11 +17,11 @@ package com.iciql.test.models; +import com.iciql.Iciql.IQTable; + import java.util.Arrays; import java.util.List; -import com.iciql.Iciql.IQTable; - /** * This class inherits all its fields from a parent class which has annotated * columns. The IQTable annotation of the parent class is ignored and only the @@ -31,34 +31,34 @@ import com.iciql.Iciql.IQTable; @IQTable(inheritColumns = true, annotationsOnly = false) public class ProductInheritedAnnotation extends ProductMixedAnnotation { - public ProductInheritedAnnotation() { - // public constructor - } + public ProductInheritedAnnotation() { + // public constructor + } - private ProductInheritedAnnotation(int productId, String productName, String category, double unitPrice, - int unitsInStock, String mappedField) { - super(productId, productName, category, unitPrice, unitsInStock, mappedField); - } + private ProductInheritedAnnotation(int productId, String productName, String category, double unitPrice, + int unitsInStock, String mappedField) { + super(productId, productName, category, unitPrice, unitsInStock, mappedField); + } - private static ProductInheritedAnnotation create(int productId, String productName, String category, - double unitPrice, int unitsInStock, String mappedField) { - return new ProductInheritedAnnotation(productId, productName, category, unitPrice, unitsInStock, - mappedField); - } + private static ProductInheritedAnnotation create(int productId, String productName, String category, + double unitPrice, int unitsInStock, String mappedField) { + return new ProductInheritedAnnotation(productId, productName, category, unitPrice, unitsInStock, + mappedField); + } - public static List getData() { - String mappedField = "mapped"; - ProductInheritedAnnotation[] list = { create(1, "Chai", "Beverages", 18, 39, mappedField), - create(2, "Chang", "Beverages", 19.0, 17, mappedField), - create(3, "Aniseed Syrup", "Condiments", 10.0, 13, mappedField), - create(4, "Chef Anton's Cajun Seasoning", "Condiments", 22.0, 53, mappedField), - create(5, "Chef Anton's Gumbo Mix", "Condiments", 21.3500, 0, mappedField), - create(6, "Grandma's Boysenberry Spread", "Condiments", 25.0, 120, mappedField), - create(7, "Uncle Bob's Organic Dried Pears", "Produce", 30.0, 15, mappedField), - create(8, "Northwoods Cranberry Sauce", "Condiments", 40.0, 6, mappedField), - create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29, mappedField), - create(10, "Ikura", "Seafood", 31.0, 31, mappedField), }; - return Arrays.asList(list); - } + public static List getData() { + String mappedField = "mapped"; + ProductInheritedAnnotation[] list = {create(1, "Chai", "Beverages", 18, 39, mappedField), + create(2, "Chang", "Beverages", 19.0, 17, mappedField), + create(3, "Aniseed Syrup", "Condiments", 10.0, 13, mappedField), + create(4, "Chef Anton's Cajun Seasoning", "Condiments", 22.0, 53, mappedField), + create(5, "Chef Anton's Gumbo Mix", "Condiments", 21.3500, 0, mappedField), + create(6, "Grandma's Boysenberry Spread", "Condiments", 25.0, 120, mappedField), + create(7, "Uncle Bob's Organic Dried Pears", "Produce", 30.0, 15, mappedField), + create(8, "Northwoods Cranberry Sauce", "Condiments", 40.0, 6, mappedField), + create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29, mappedField), + create(10, "Ikura", "Seafood", 31.0, 31, mappedField),}; + return Arrays.asList(list); + } } diff --git a/src/test/java/com/iciql/test/models/ProductMixedAnnotation.java b/src/test/java/com/iciql/test/models/ProductMixedAnnotation.java index a893a69..7fcf1d5 100644 --- a/src/test/java/com/iciql/test/models/ProductMixedAnnotation.java +++ b/src/test/java/com/iciql/test/models/ProductMixedAnnotation.java @@ -17,88 +17,88 @@ package com.iciql.test.models; -import java.util.Arrays; -import java.util.List; - import com.iciql.Define; import com.iciql.Iciql; import com.iciql.Iciql.IQIndex; import com.iciql.Iciql.IQTable; +import java.util.Arrays; +import java.util.List; + /** * A table containing product data. */ @IQTable(annotationsOnly = false) -@IQIndex({ "name", "cat" }) +@IQIndex({"name", "cat"}) public class ProductMixedAnnotation implements Iciql { - public Double unitPrice; - public Integer unitsInStock; - public String mappedField; - - @IQIgnore - public String productDescription; - - @IQColumn(name = "cat", length = 255) - public String category; - - @IQColumn(name = "id", primaryKey = true) - private Integer productId; - - @IQColumn(name = "name", length = 255) - private String productName; - - public ProductMixedAnnotation() { - // public constructor - } - - protected ProductMixedAnnotation(int productId, String productName, String category, double unitPrice, - int unitsInStock, String mappedField) { - this.productId = productId; - this.productName = productName; - this.category = category; - this.unitPrice = unitPrice; - this.unitsInStock = unitsInStock; - this.mappedField = mappedField; - this.productDescription = category + ": " + productName; - } - - private static ProductMixedAnnotation create(int productId, String productName, String category, - double unitPrice, int unitsInStock, String mappedField) { - return new ProductMixedAnnotation(productId, productName, category, unitPrice, unitsInStock, - mappedField); - } - - public static List getList() { - String mappedField = "mapped"; - ProductMixedAnnotation[] list = { create(1, "Chai", "Beverages", 18, 39, mappedField), - create(2, "Chang", "Beverages", 19.0, 17, mappedField), - create(3, "Aniseed Syrup", "Condiments", 10.0, 13, mappedField), - create(4, "Chef Anton's Cajun Seasoning", "Condiments", 22.0, 53, mappedField), - create(5, "Chef Anton's Gumbo Mix", "Condiments", 21.3500, 0, mappedField), - create(6, "Grandma's Boysenberry Spread", "Condiments", 25.0, 120, mappedField), - create(7, "Uncle Bob's Organic Dried Pears", "Produce", 30.0, 15, mappedField), - create(8, "Northwoods Cranberry Sauce", "Condiments", 40.0, 6, mappedField), - create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29, mappedField), - create(10, "Ikura", "Seafood", 31.0, 31, mappedField), }; - return Arrays.asList(list); - } - - public String toString() { - return productName + ": " + unitsInStock; - } - - public int id() { - return productId; - } - - public String name() { - return productName; - } - - @Override - public void defineIQ() { - Define.length(mappedField, 25); - } + public Double unitPrice; + public Integer unitsInStock; + public String mappedField; + + @IQIgnore + public String productDescription; + + @IQColumn(name = "cat", length = 255) + public String category; + + @IQColumn(name = "id", primaryKey = true) + private Integer productId; + + @IQColumn(name = "name", length = 255) + private String productName; + + public ProductMixedAnnotation() { + // public constructor + } + + protected ProductMixedAnnotation(int productId, String productName, String category, double unitPrice, + int unitsInStock, String mappedField) { + this.productId = productId; + this.productName = productName; + this.category = category; + this.unitPrice = unitPrice; + this.unitsInStock = unitsInStock; + this.mappedField = mappedField; + this.productDescription = category + ": " + productName; + } + + private static ProductMixedAnnotation create(int productId, String productName, String category, + double unitPrice, int unitsInStock, String mappedField) { + return new ProductMixedAnnotation(productId, productName, category, unitPrice, unitsInStock, + mappedField); + } + + public static List getList() { + String mappedField = "mapped"; + ProductMixedAnnotation[] list = {create(1, "Chai", "Beverages", 18, 39, mappedField), + create(2, "Chang", "Beverages", 19.0, 17, mappedField), + create(3, "Aniseed Syrup", "Condiments", 10.0, 13, mappedField), + create(4, "Chef Anton's Cajun Seasoning", "Condiments", 22.0, 53, mappedField), + create(5, "Chef Anton's Gumbo Mix", "Condiments", 21.3500, 0, mappedField), + create(6, "Grandma's Boysenberry Spread", "Condiments", 25.0, 120, mappedField), + create(7, "Uncle Bob's Organic Dried Pears", "Produce", 30.0, 15, mappedField), + create(8, "Northwoods Cranberry Sauce", "Condiments", 40.0, 6, mappedField), + create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29, mappedField), + create(10, "Ikura", "Seafood", 31.0, 31, mappedField),}; + return Arrays.asList(list); + } + + public String toString() { + return productName + ": " + unitsInStock; + } + + public int id() { + return productId; + } + + public String name() { + return productName; + } + + @Override + public void defineIQ() { + Define.length(mappedField, 25); + } } diff --git a/src/test/java/com/iciql/test/models/ProductNoCreateTable.java b/src/test/java/com/iciql/test/models/ProductNoCreateTable.java index cbf96e9..9c75ba5 100644 --- a/src/test/java/com/iciql/test/models/ProductNoCreateTable.java +++ b/src/test/java/com/iciql/test/models/ProductNoCreateTable.java @@ -17,12 +17,12 @@ package com.iciql.test.models; -import java.util.Arrays; -import java.util.List; - import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQTable; +import java.util.Arrays; +import java.util.List; + /** * A table containing product data. */ @@ -30,30 +30,30 @@ import com.iciql.Iciql.IQTable; @IQTable(create = false) public class ProductNoCreateTable { - @SuppressWarnings("unused") - @IQColumn(name = "id") - private Integer productId; + @SuppressWarnings("unused") + @IQColumn(name = "id") + private Integer productId; - @SuppressWarnings("unused") - @IQColumn(name = "name") - private String productName; + @SuppressWarnings("unused") + @IQColumn(name = "name") + private String productName; - public ProductNoCreateTable() { - // public constructor - } + public ProductNoCreateTable() { + // public constructor + } - private ProductNoCreateTable(int productId, String productName) { - this.productId = productId; - this.productName = productName; - } + private ProductNoCreateTable(int productId, String productName) { + this.productId = productId; + this.productName = productName; + } - private static ProductNoCreateTable create(int productId, String productName) { - return new ProductNoCreateTable(productId, productName); - } + private static ProductNoCreateTable create(int productId, String productName) { + return new ProductNoCreateTable(productId, productName); + } - public static List getList() { - ProductNoCreateTable[] list = { create(1, "Chai"), create(2, "Chang") }; - return Arrays.asList(list); - } + public static List getList() { + ProductNoCreateTable[] list = {create(1, "Chai"), create(2, "Chang")}; + return Arrays.asList(list); + } } diff --git a/src/test/java/com/iciql/test/models/ProductView.java b/src/test/java/com/iciql/test/models/ProductView.java index 2efe9eb..1739004 100644 --- a/src/test/java/com/iciql/test/models/ProductView.java +++ b/src/test/java/com/iciql/test/models/ProductView.java @@ -27,21 +27,21 @@ import com.iciql.Iciql.IQView; @IQView(name = "AnnotatedProductView", tableName = "AnnotatedProduct") public class ProductView { - public String unmappedField; + public String unmappedField; - @IQColumn(name = "id", autoIncrement = true) - @IQConstraint("this <= 7 AND this > 2") - public Long productId; + @IQColumn(name = "id", autoIncrement = true) + @IQConstraint("this <= 7 AND this > 2") + public Long productId; - @IQColumn(name = "name") - public String productName; + @IQColumn(name = "name") + public String productName; - public ProductView() { - // public constructor - } + public ProductView() { + // public constructor + } - public String toString() { - return productName + " (" + productId + ")"; - } + public String toString() { + return productName + " (" + productId + ")"; + } } diff --git a/src/test/java/com/iciql/test/models/ProductViewFromQuery.java b/src/test/java/com/iciql/test/models/ProductViewFromQuery.java index 2f2f194..0a0595d 100644 --- a/src/test/java/com/iciql/test/models/ProductViewFromQuery.java +++ b/src/test/java/com/iciql/test/models/ProductViewFromQuery.java @@ -24,19 +24,19 @@ import com.iciql.Iciql.IQView; */ @IQView(name = "AnnotatedProductViewInherited", inheritColumns = true) -public class ProductViewFromQuery extends ProductAnnotationOnly { +public class ProductViewFromQuery extends ProductAnnotationOnly { - public String unmappedField; + public String unmappedField; - @IQColumn(name = "id") - public Long productId; + @IQColumn(name = "id") + public Long productId; - public ProductViewFromQuery() { - // public constructor - } + public ProductViewFromQuery() { + // public constructor + } - public String toString() { - return productName + " (" + productId + ")"; - } + public String toString() { + return productName + " (" + productId + ")"; + } } diff --git a/src/test/java/com/iciql/test/models/ProductViewInherited.java b/src/test/java/com/iciql/test/models/ProductViewInherited.java index e9c274b..48137ed 100644 --- a/src/test/java/com/iciql/test/models/ProductViewInherited.java +++ b/src/test/java/com/iciql/test/models/ProductViewInherited.java @@ -27,18 +27,18 @@ import com.iciql.Iciql.IQView; @IQView(name = "AnnotatedProductViewInherited", inheritColumns = true) public class ProductViewInherited extends ProductAnnotationOnly { - public String unmappedField; + public String unmappedField; - @IQColumn(name = "id", autoIncrement = true) - @IQConstraint("this <= 7 AND this > 2") - public Long productId; + @IQColumn(name = "id", autoIncrement = true) + @IQConstraint("this <= 7 AND this > 2") + public Long productId; - public ProductViewInherited() { - // public constructor - } + public ProductViewInherited() { + // public constructor + } - public String toString() { - return productName + " (" + productId + ")"; - } + public String toString() { + return productName + " (" + productId + ")"; + } } diff --git a/src/test/java/com/iciql/test/models/ProductViewInheritedComplex.java b/src/test/java/com/iciql/test/models/ProductViewInheritedComplex.java index 55e7ba8..a373251 100644 --- a/src/test/java/com/iciql/test/models/ProductViewInheritedComplex.java +++ b/src/test/java/com/iciql/test/models/ProductViewInheritedComplex.java @@ -23,6 +23,6 @@ import com.iciql.Iciql.IQView; */ @IQView(inheritColumns = true) -public class ProductViewInheritedComplex extends ProductViewInherited { +public class ProductViewInheritedComplex extends ProductViewInherited { } diff --git a/src/test/java/com/iciql/test/models/StaticQueries.java b/src/test/java/com/iciql/test/models/StaticQueries.java index 09f84e6..b78be57 100644 --- a/src/test/java/com/iciql/test/models/StaticQueries.java +++ b/src/test/java/com/iciql/test/models/StaticQueries.java @@ -15,73 +15,73 @@ */ package com.iciql.test.models; -import java.sql.Timestamp; - import com.iciql.Iciql.EnumType; import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQEnum; import com.iciql.Iciql.IQTable; import com.iciql.test.models.EnumModels.Tree; +import java.sql.Timestamp; + /** * Static query models. */ public class StaticQueries { - @IQTable(name = "StaticQueryTest1") - public static class StaticModel1 { + @IQTable(name = "StaticQueryTest1") + public static class StaticModel1 { - @IQColumn(primaryKey = true, autoIncrement = true) - public Integer id; + @IQColumn(primaryKey = true, autoIncrement = true) + public Integer id; - @IQColumn - @IQEnum(EnumType.NAME) - public Tree myTree; + @IQColumn + @IQEnum(EnumType.NAME) + public Tree myTree; - @IQColumn - public String myString; + @IQColumn + public String myString; - @IQColumn - public Boolean myBool; + @IQColumn + public Boolean myBool; - @IQColumn - public Timestamp myTimestamp; + @IQColumn + public Timestamp myTimestamp; - @IQColumn - public java.sql.Date myDate; + @IQColumn + public java.sql.Date myDate; - @IQColumn - public java.sql.Time myTime; + @IQColumn + public java.sql.Time myTime; - public StaticModel1() { - } - } + public StaticModel1() { + } + } - @IQTable(name = "StaticQueryTest2") - public static class StaticModel2 { + @IQTable(name = "StaticQueryTest2") + public static class StaticModel2 { - @IQColumn(primaryKey = true, autoIncrement = true) - public Integer id; + @IQColumn(primaryKey = true, autoIncrement = true) + public Integer id; - @IQColumn - @IQEnum(EnumType.ENUMID) - public Tree myTree; + @IQColumn + @IQEnum(EnumType.ENUMID) + public Tree myTree; - public StaticModel2() { - } - } + public StaticModel2() { + } + } - @IQTable(name = "StaticQueryTest3") - public static class StaticModel3 { + @IQTable(name = "StaticQueryTest3") + public static class StaticModel3 { - @IQColumn(primaryKey = true, autoIncrement = true) - public Integer id; + @IQColumn(primaryKey = true, autoIncrement = true) + public Integer id; - @IQColumn - @IQEnum(EnumType.ORDINAL) - public Tree myTree; + @IQColumn + @IQEnum(EnumType.ORDINAL) + public Tree myTree; - public StaticModel3() { - } - } + public StaticModel3() { + } + } } diff --git a/src/test/java/com/iciql/test/models/SupportedTypes.java b/src/test/java/com/iciql/test/models/SupportedTypes.java index e04a3ab..8640c27 100644 --- a/src/test/java/com/iciql/test/models/SupportedTypes.java +++ b/src/test/java/com/iciql/test/models/SupportedTypes.java @@ -17,16 +17,6 @@ package com.iciql.test.models; -import java.io.Serializable; -import java.math.BigDecimal; -import java.math.MathContext; -import java.math.RoundingMode; -import java.text.SimpleDateFormat; -import java.util.Arrays; -import java.util.Calendar; -import java.util.List; -import java.util.Random; - import com.iciql.Iciql.EnumType; import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQEnum; @@ -40,182 +30,192 @@ import com.iciql.test.IciqlSuite; import com.iciql.test.models.EnumModels.Tree; import com.iciql.util.Utils; +import java.io.Serializable; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; +import java.util.List; +import java.util.Random; + /** * A data class that contains a column for each data type. */ @IQTable -@IQIndexes({ @IQIndex({ "myLong", "myInteger" }), @IQIndex(type = IndexType.HASH, value = "myString") }) +@IQIndexes({@IQIndex({"myLong", "myInteger"}), @IQIndex(type = IndexType.HASH, value = "myString")}) @IQVersion(1) public class SupportedTypes implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public static final SupportedTypes SAMPLE = new SupportedTypes(); + public static final SupportedTypes SAMPLE = new SupportedTypes(); - /** - * Test of plain enumeration. - * - * Each field declaraton of this enum must specify a mapping strategy. - */ - public enum Flower { - ROSE, TULIP, MUM, PETUNIA, MARIGOLD, DAFFODIL; - } - - @IQColumn(primaryKey = true, autoIncrement = true) - public Integer id; - - @IQColumn - private Boolean myBool; - - @IQColumn - private Byte myByte; - - @IQColumn - private Short myShort; - - @IQColumn - public Integer myInteger; - - @IQColumn - private Long myLong; - - @IQColumn - private Float myFloat; - - @IQColumn - private Double myDouble; - - // scale change must match the test value scale - @IQColumn(length = 10, scale = 5) - private BigDecimal myBigDecimal; - - @IQColumn(length = 40, trim = true) - public String myString; - - @IQColumn - private java.util.Date myUtilDate; - - @IQColumn - private java.sql.Date mySqlDate; - - @IQColumn - private java.sql.Time mySqlTime; - - @IQColumn - private java.sql.Timestamp mySqlTimestamp; - - @IQColumn - private byte[] myBlob; - - // test default enum type NAME - @IQColumn(trim = true, length = 25) - private Flower myDefaultFlower; - - @IQEnum(EnumType.NAME) - @IQColumn(trim = true, length = 25) - private Flower myFavoriteFlower; - - @IQEnum(EnumType.ORDINAL) - @IQColumn - private Flower myOtherFavoriteFlower; - - @IQEnum(EnumType.ORDINAL) - @IQColumn - // override the default enum strategy and use the ordinal value - private Tree myFavoriteTree; - - // @IQEnum is set on the enumeration definition and is shared - // by all uses of Tree as an @IQColumn - @IQColumn - private Tree myOtherFavoriteTree; - - public static List createList() { - List list = Utils.newArrayList(); - Calendar c = Calendar.getInstance(); - c.setTimeInMillis(System.currentTimeMillis()); - c.set(Calendar.MILLISECOND, 0); - long now = c.getTimeInMillis(); - - long oneday = 24 * 60 * 60 * 1000L; - for (int i = 0; i < 10; i++) { - SupportedTypes s = randomValue(now - (i * oneday)); - s.myInteger = i + 1; - list.add(s); - } - return list; - } - - static SupportedTypes randomValue(long time) { - Random rand = new Random(); - SupportedTypes s = new SupportedTypes(); - s.myBool = new Boolean(rand.nextBoolean()); - s.myByte = new Byte((byte) rand.nextInt(Byte.MAX_VALUE)); - s.myShort = new Short((short) rand.nextInt(Short.MAX_VALUE)); - s.myLong = new Long(rand.nextLong()); - s.myFloat = new Float(rand.nextFloat()); - s.myDouble = new Double(rand.nextDouble()); - s.myBigDecimal = new BigDecimal(rand.nextDouble()); - // scale must match annotation - s.myBigDecimal = s.myBigDecimal.setScale(5, RoundingMode.UP); - s.myString = Long.toHexString(rand.nextLong()); - s.myUtilDate = new java.util.Date(time); - s.mySqlDate = new java.sql.Date(time); - s.mySqlTime = new java.sql.Time(time); - s.mySqlTimestamp = new java.sql.Timestamp(time); - s.myBlob = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; - s.myDefaultFlower = Flower.DAFFODIL; - s.myFavoriteFlower = Flower.MUM; - s.myOtherFavoriteFlower = Flower.MARIGOLD; - s.myFavoriteTree = Tree.BIRCH; - s.myOtherFavoriteTree = Tree.WALNUT; - return s; - } - - public boolean equivalentTo(SupportedTypes s) { - boolean same = true; - same &= same("myBool", myBool.equals(s.myBool)); - same &= same("myByte", myByte.equals(s.myByte)); - same &= same("myShort", myShort.equals(s.myShort)); - same &= same("myInteger", myInteger.equals(s.myInteger)); - same &= same("myLong", myLong.equals(s.myLong)); - same &= same("myFloat", IciqlSuite.equivalentTo(myFloat, s.myFloat)); - same &= same("myDouble", IciqlSuite.equivalentTo(myDouble, s.myDouble)); - - BigDecimal bda = myBigDecimal.round(MathContext.DECIMAL32); - BigDecimal bdb = s.myBigDecimal.round(MathContext.DECIMAL32); - same &= same("myBigDecimal", bda.compareTo(bdb) == 0); - - SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); - same &= same("myUtilDate", df.format(myUtilDate).equals(df.format(s.myUtilDate))); - same &= same("mySqlTimestamp", df.format(mySqlTimestamp).equals(df.format(s.mySqlTimestamp))); - same &= same("mySqlDate", mySqlDate.toString().equals(s.mySqlDate.toString())); - same &= same("mySqlTime", mySqlTime.toString().equals(s.mySqlTime.toString())); - same &= same("myString", myString.equals(s.myString)); - same &= same("myBlob", Arrays.equals(myBlob, s.myBlob)); - same &= same("myDefaultFlower", myDefaultFlower.equals(s.myDefaultFlower)); - same &= same("myFavoriteFlower", myFavoriteFlower.equals(s.myFavoriteFlower)); - same &= same("myOtherFavoriteFlower", myOtherFavoriteFlower.equals(s.myOtherFavoriteFlower)); - same &= same("myFavoriteTree", myFavoriteTree.equals(s.myFavoriteTree)); - same &= same("myOtherFavoriteTree", myOtherFavoriteTree.equals(s.myOtherFavoriteTree)); - return same; - } - - private boolean same(String field, boolean same) { - if (!same) { - throw new IciqlException("{0} is not the same", field); - } - return same; - } - - /** - * This class demonstrates the table upgrade. - */ - @IQTable(name = "SupportedTypes", inheritColumns = true) - @IQVersion(2) - public static class SupportedTypes2 extends SupportedTypes { - - public SupportedTypes2() { - // nothing to do - } - } + /** + * Test of plain enumeration. + *

+ * Each field declaraton of this enum must specify a mapping strategy. + */ + public enum Flower { + ROSE, TULIP, MUM, PETUNIA, MARIGOLD, DAFFODIL; + } + + @IQColumn(primaryKey = true, autoIncrement = true) + public Integer id; + + @IQColumn + private Boolean myBool; + + @IQColumn + private Byte myByte; + + @IQColumn + private Short myShort; + + @IQColumn + public Integer myInteger; + + @IQColumn + private Long myLong; + + @IQColumn + private Float myFloat; + + @IQColumn + private Double myDouble; + + // scale change must match the test value scale + @IQColumn(length = 10, scale = 5) + private BigDecimal myBigDecimal; + + @IQColumn(length = 40, trim = true) + public String myString; + + @IQColumn + private java.util.Date myUtilDate; + + @IQColumn + private java.sql.Date mySqlDate; + + @IQColumn + private java.sql.Time mySqlTime; + + @IQColumn + private java.sql.Timestamp mySqlTimestamp; + + @IQColumn + private byte[] myBlob; + + // test default enum type NAME + @IQColumn(trim = true, length = 25) + private Flower myDefaultFlower; + + @IQEnum(EnumType.NAME) + @IQColumn(trim = true, length = 25) + private Flower myFavoriteFlower; + + @IQEnum(EnumType.ORDINAL) + @IQColumn + private Flower myOtherFavoriteFlower; + + @IQEnum(EnumType.ORDINAL) + @IQColumn + // override the default enum strategy and use the ordinal value + private Tree myFavoriteTree; + + // @IQEnum is set on the enumeration definition and is shared + // by all uses of Tree as an @IQColumn + @IQColumn + private Tree myOtherFavoriteTree; + + public static List createList() { + List list = Utils.newArrayList(); + Calendar c = Calendar.getInstance(); + c.setTimeInMillis(System.currentTimeMillis()); + c.set(Calendar.MILLISECOND, 0); + long now = c.getTimeInMillis(); + + long oneday = 24 * 60 * 60 * 1000L; + for (int i = 0; i < 10; i++) { + SupportedTypes s = randomValue(now - (i * oneday)); + s.myInteger = i + 1; + list.add(s); + } + return list; + } + + static SupportedTypes randomValue(long time) { + Random rand = new Random(); + SupportedTypes s = new SupportedTypes(); + s.myBool = new Boolean(rand.nextBoolean()); + s.myByte = new Byte((byte) rand.nextInt(Byte.MAX_VALUE)); + s.myShort = new Short((short) rand.nextInt(Short.MAX_VALUE)); + s.myLong = new Long(rand.nextLong()); + s.myFloat = new Float(rand.nextFloat()); + s.myDouble = new Double(rand.nextDouble()); + s.myBigDecimal = new BigDecimal(rand.nextDouble()); + // scale must match annotation + s.myBigDecimal = s.myBigDecimal.setScale(5, RoundingMode.UP); + s.myString = Long.toHexString(rand.nextLong()); + s.myUtilDate = new java.util.Date(time); + s.mySqlDate = new java.sql.Date(time); + s.mySqlTime = new java.sql.Time(time); + s.mySqlTimestamp = new java.sql.Timestamp(time); + s.myBlob = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + s.myDefaultFlower = Flower.DAFFODIL; + s.myFavoriteFlower = Flower.MUM; + s.myOtherFavoriteFlower = Flower.MARIGOLD; + s.myFavoriteTree = Tree.BIRCH; + s.myOtherFavoriteTree = Tree.WALNUT; + return s; + } + + public boolean equivalentTo(SupportedTypes s) { + boolean same = true; + same &= same("myBool", myBool.equals(s.myBool)); + same &= same("myByte", myByte.equals(s.myByte)); + same &= same("myShort", myShort.equals(s.myShort)); + same &= same("myInteger", myInteger.equals(s.myInteger)); + same &= same("myLong", myLong.equals(s.myLong)); + same &= same("myFloat", IciqlSuite.equivalentTo(myFloat, s.myFloat)); + same &= same("myDouble", IciqlSuite.equivalentTo(myDouble, s.myDouble)); + + BigDecimal bda = myBigDecimal.round(MathContext.DECIMAL32); + BigDecimal bdb = s.myBigDecimal.round(MathContext.DECIMAL32); + same &= same("myBigDecimal", bda.compareTo(bdb) == 0); + + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + same &= same("myUtilDate", df.format(myUtilDate).equals(df.format(s.myUtilDate))); + same &= same("mySqlTimestamp", df.format(mySqlTimestamp).equals(df.format(s.mySqlTimestamp))); + same &= same("mySqlDate", mySqlDate.toString().equals(s.mySqlDate.toString())); + same &= same("mySqlTime", mySqlTime.toString().equals(s.mySqlTime.toString())); + same &= same("myString", myString.equals(s.myString)); + same &= same("myBlob", Arrays.equals(myBlob, s.myBlob)); + same &= same("myDefaultFlower", myDefaultFlower.equals(s.myDefaultFlower)); + same &= same("myFavoriteFlower", myFavoriteFlower.equals(s.myFavoriteFlower)); + same &= same("myOtherFavoriteFlower", myOtherFavoriteFlower.equals(s.myOtherFavoriteFlower)); + same &= same("myFavoriteTree", myFavoriteTree.equals(s.myFavoriteTree)); + same &= same("myOtherFavoriteTree", myOtherFavoriteTree.equals(s.myOtherFavoriteTree)); + return same; + } + + private boolean same(String field, boolean same) { + if (!same) { + throw new IciqlException("{0} is not the same", field); + } + return same; + } + + /** + * This class demonstrates the table upgrade. + */ + @IQTable(name = "SupportedTypes", inheritColumns = true) + @IQVersion(2) + public static class SupportedTypes2 extends SupportedTypes { + + public SupportedTypes2() { + // nothing to do + } + } } diff --git a/src/test/resources/iciql.properties b/src/test/resources/iciql.properties index cecb056..d79d318 100644 --- a/src/test/resources/iciql.properties +++ b/src/test/resources/iciql.properties @@ -1,7 +1,6 @@ # # Example resource file for DaoClasspathStatementProvider # - -get.products = select * from Product -%test.get.products = select * from Product where category = 'Beverages' -%dev.get.products = select * from Product where category = 'Condiments' +get.products=select * from Product +%test.get.products=select * from Product where category = 'Beverages' +%dev.get.products=select * from Product where category = 'Condiments' -- 2.39.5