aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJames Moger <james.moger@gitblit.com>2016-04-05 12:27:55 -0400
committerJames Moger <james.moger@gitblit.com>2016-04-05 12:27:55 -0400
commitbb87e621eefd97872aa1a619d38c166b2f07db84 (patch)
treed7199be13f0015e218ae2ded4ebd8af11b955845 /src
parent98bb5dc01796728de5b18f84e19766276d12d1db (diff)
downloadiciql-bb87e621eefd97872aa1a619d38c166b2f07db84.tar.gz
iciql-bb87e621eefd97872aa1a619d38c166b2f07db84.zip
Reformat project with default IntelliJ settings
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/iciql/CompareType.java30
-rw-r--r--src/main/java/com/iciql/Condition.java85
-rw-r--r--src/main/java/com/iciql/ConditionAndOr.java16
-rw-r--r--src/main/java/com/iciql/ConditionOpenClose.java16
-rw-r--r--src/main/java/com/iciql/Constants.java68
-rw-r--r--src/main/java/com/iciql/Dao.java261
-rw-r--r--src/main/java/com/iciql/DaoClasspathStatementProvider.java131
-rw-r--r--src/main/java/com/iciql/DaoProxy.java1251
-rw-r--r--src/main/java/com/iciql/DaoStatementProvider.java17
-rw-r--r--src/main/java/com/iciql/Db.java1670
-rw-r--r--src/main/java/com/iciql/DbInspector.java327
-rw-r--r--src/main/java/com/iciql/DbUpgrader.java82
-rw-r--r--src/main/java/com/iciql/DbVersion.java51
-rw-r--r--src/main/java/com/iciql/Define.java210
-rw-r--r--src/main/java/com/iciql/Filter.java2
-rw-r--r--src/main/java/com/iciql/Function.java242
-rw-r--r--src/main/java/com/iciql/Iciql.java1228
-rw-r--r--src/main/java/com/iciql/IciqlException.java332
-rw-r--r--src/main/java/com/iciql/ModelUtils.java927
-rw-r--r--src/main/java/com/iciql/NestedConditions.java164
-rw-r--r--src/main/java/com/iciql/OrderExpression.java53
-rw-r--r--src/main/java/com/iciql/Query.java2104
-rw-r--r--src/main/java/com/iciql/QueryBetween.java62
-rw-r--r--src/main/java/com/iciql/QueryCondition.java238
-rw-r--r--src/main/java/com/iciql/QueryJoin.java78
-rw-r--r--src/main/java/com/iciql/QueryJoinCondition.java93
-rw-r--r--src/main/java/com/iciql/QueryWhere.java1188
-rw-r--r--src/main/java/com/iciql/RuntimeParameter.java41
-rw-r--r--src/main/java/com/iciql/RuntimeToken.java57
-rw-r--r--src/main/java/com/iciql/SQLDialect.java370
-rw-r--r--src/main/java/com/iciql/SQLDialectDefault.java984
-rw-r--r--src/main/java/com/iciql/SQLDialectDerby.java82
-rw-r--r--src/main/java/com/iciql/SQLDialectH2.java206
-rw-r--r--src/main/java/com/iciql/SQLDialectHSQL.java230
-rw-r--r--src/main/java/com/iciql/SQLDialectMSSQL.java64
-rw-r--r--src/main/java/com/iciql/SQLDialectMySQL.java124
-rw-r--r--src/main/java/com/iciql/SQLDialectPostgreSQL.java264
-rw-r--r--src/main/java/com/iciql/SQLDialectSQLite.java300
-rw-r--r--src/main/java/com/iciql/SQLStatement.java316
-rw-r--r--src/main/java/com/iciql/SelectColumn.java59
-rw-r--r--src/main/java/com/iciql/SelectTable.java165
-rw-r--r--src/main/java/com/iciql/SubQuery.java22
-rw-r--r--src/main/java/com/iciql/SubQueryCondition.java29
-rw-r--r--src/main/java/com/iciql/TableDefinition.java2324
-rw-r--r--src/main/java/com/iciql/TableInspector.java1353
-rw-r--r--src/main/java/com/iciql/TestCondition.java157
-rw-r--r--src/main/java/com/iciql/Token.java16
-rw-r--r--src/main/java/com/iciql/UpdateColumn.java13
-rw-r--r--src/main/java/com/iciql/UpdateColumnIncrement.java54
-rw-r--r--src/main/java/com/iciql/UpdateColumnSet.java70
-rw-r--r--src/main/java/com/iciql/ValidationRemark.java190
-rw-r--r--src/main/java/com/iciql/adapter/GsonTypeAdapter.java50
-rw-r--r--src/main/java/com/iciql/adapter/JavaSerializationTypeAdapter.java119
-rw-r--r--src/main/java/com/iciql/adapter/SnakeYamlTypeAdapter.java67
-rw-r--r--src/main/java/com/iciql/adapter/XStreamTypeAdapter.java66
-rw-r--r--src/main/java/com/iciql/adapter/postgresql/JsonObjectAdapter.java40
-rw-r--r--src/main/java/com/iciql/adapter/postgresql/JsonStringAdapter.java73
-rw-r--r--src/main/java/com/iciql/adapter/postgresql/JsonbObjectAdapter.java40
-rw-r--r--src/main/java/com/iciql/adapter/postgresql/JsonbStringAdapter.java73
-rw-r--r--src/main/java/com/iciql/adapter/postgresql/XmlObjectAdapter.java9
-rw-r--r--src/main/java/com/iciql/adapter/postgresql/XmlStringAdapter.java73
-rw-r--r--src/main/java/com/iciql/bytecode/And.java32
-rw-r--r--src/main/java/com/iciql/bytecode/ArrayGet.java38
-rw-r--r--src/main/java/com/iciql/bytecode/CaseWhen.java64
-rw-r--r--src/main/java/com/iciql/bytecode/ClassReader.java2800
-rw-r--r--src/main/java/com/iciql/bytecode/Constant.java16
-rw-r--r--src/main/java/com/iciql/bytecode/ConstantNumber.java64
-rw-r--r--src/main/java/com/iciql/bytecode/ConstantString.java38
-rw-r--r--src/main/java/com/iciql/bytecode/Function.java30
-rw-r--r--src/main/java/com/iciql/bytecode/Not.java50
-rw-r--r--src/main/java/com/iciql/bytecode/Null.java22
-rw-r--r--src/main/java/com/iciql/bytecode/Operation.java162
-rw-r--r--src/main/java/com/iciql/bytecode/Or.java34
-rw-r--r--src/main/java/com/iciql/bytecode/Variable.java32
-rw-r--r--src/main/java/com/iciql/bytecode/package.html5
-rw-r--r--src/main/java/com/iciql/package.html5
-rw-r--r--src/main/java/com/iciql/util/GenerateModels.java290
-rw-r--r--src/main/java/com/iciql/util/IciqlLogger.java359
-rw-r--r--src/main/java/com/iciql/util/JdbcUtils.java426
-rw-r--r--src/main/java/com/iciql/util/Slf4jIciqlListener.java113
-rw-r--r--src/main/java/com/iciql/util/StatementBuilder.java240
-rw-r--r--src/main/java/com/iciql/util/StringUtils.java656
-rw-r--r--src/main/java/com/iciql/util/Utils.java1140
-rw-r--r--src/main/java/com/iciql/util/WeakIdentityHashMap.java414
-rw-r--r--src/main/java/com/iciql/util/package.html5
-rw-r--r--src/main/java/java/lang/AutoCloseable.java6
-rw-r--r--src/test/java/com/iciql/test/AliasMapTest.java201
-rw-r--r--src/test/java/com/iciql/test/AnnotationsTest.java321
-rw-r--r--src/test/java/com/iciql/test/BooleanModelTest.java275
-rw-r--r--src/test/java/com/iciql/test/ClobTest.java167
-rw-r--r--src/test/java/com/iciql/test/ConcurrencyTest.java337
-rw-r--r--src/test/java/com/iciql/test/DataTypeAdapterTest.java109
-rw-r--r--src/test/java/com/iciql/test/DefaultValuesTest.java63
-rw-r--r--src/test/java/com/iciql/test/EnumsTest.java243
-rw-r--r--src/test/java/com/iciql/test/ForeignKeyTest.java95
-rw-r--r--src/test/java/com/iciql/test/IciqlSuite.java1268
-rw-r--r--src/test/java/com/iciql/test/JoinTest.java242
-rw-r--r--src/test/java/com/iciql/test/ModelsTest.java287
-rw-r--r--src/test/java/com/iciql/test/NestedConditionsTest.java478
-rw-r--r--src/test/java/com/iciql/test/OneOfTest.java235
-rw-r--r--src/test/java/com/iciql/test/PrimitivesTest.java177
-rw-r--r--src/test/java/com/iciql/test/ProductDaoTest.java527
-rw-r--r--src/test/java/com/iciql/test/RuntimeQueryTest.java297
-rw-r--r--src/test/java/com/iciql/test/SamplesTest.java823
-rw-r--r--src/test/java/com/iciql/test/TransactionTest.java181
-rw-r--r--src/test/java/com/iciql/test/UUIDTest.java165
-rw-r--r--src/test/java/com/iciql/test/UpdateTest.java297
-rw-r--r--src/test/java/com/iciql/test/UpgradesTest.java298
-rw-r--r--src/test/java/com/iciql/test/ViewsTest.java163
-rw-r--r--src/test/java/com/iciql/test/models/BooleanModel.java142
-rw-r--r--src/test/java/com/iciql/test/models/CategoryAnnotationOnly.java66
-rw-r--r--src/test/java/com/iciql/test/models/ComplexObject.java56
-rw-r--r--src/test/java/com/iciql/test/models/Customer.java58
-rw-r--r--src/test/java/com/iciql/test/models/DefaultValuesModel.java42
-rw-r--r--src/test/java/com/iciql/test/models/EnumModels.java384
-rw-r--r--src/test/java/com/iciql/test/models/MultipleBoolsModel.java41
-rw-r--r--src/test/java/com/iciql/test/models/Order.java69
-rw-r--r--src/test/java/com/iciql/test/models/PrimitivesModel.java104
-rw-r--r--src/test/java/com/iciql/test/models/Product.java123
-rw-r--r--src/test/java/com/iciql/test/models/ProductAnnotationOnly.java122
-rw-r--r--src/test/java/com/iciql/test/models/ProductAnnotationOnlyWithForeignKey.java128
-rw-r--r--src/test/java/com/iciql/test/models/ProductInheritedAnnotation.java56
-rw-r--r--src/test/java/com/iciql/test/models/ProductMixedAnnotation.java144
-rw-r--r--src/test/java/com/iciql/test/models/ProductNoCreateTable.java46
-rw-r--r--src/test/java/com/iciql/test/models/ProductView.java24
-rw-r--r--src/test/java/com/iciql/test/models/ProductViewFromQuery.java20
-rw-r--r--src/test/java/com/iciql/test/models/ProductViewInherited.java20
-rw-r--r--src/test/java/com/iciql/test/models/ProductViewInheritedComplex.java2
-rw-r--r--src/test/java/com/iciql/test/models/StaticQueries.java84
-rw-r--r--src/test/java/com/iciql/test/models/SupportedTypes.java358
-rw-r--r--src/test/resources/iciql.properties7
131 files changed, 17555 insertions, 17857 deletions
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 <A>
- * the operand type
+ *
+ * @param <A> the operand type
*/
class Condition<A> implements Token {
- CompareType compareType;
- A x, y, z;
- Iterable<A> i;
+ CompareType compareType;
+ A x, y, z;
+ Iterable<A> 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<A> i, CompareType compareType) {
- this(x, null, null, i, compareType);
- }
+ Condition(A x, Iterable<A> i, CompareType compareType) {
+ this(x, null, null, i, compareType);
+ }
- Condition(A x, A y, A z, Iterable<A> 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<A> i, CompareType compareType) {
+ this.compareType = compareType;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.i = i;
+ }
- @SuppressWarnings("unchecked")
- public <T> void appendSQL(SQLStatement stat, Query<T> 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<Object>)i, compareType);
- }
- }
- }
+ @SuppressWarnings("unchecked")
+ public <T> void appendSQL(SQLStatement stat, Query<T> 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<Object>) 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 <T> void appendSQL(SQLStatement stat, Query<T> query) {
- stat.appendSQL(text);
- }
+ public <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> query) {
- stat.appendSQL(text);
- }
+ public <T> void appendSQL(SQLStatement stat, Query<T> 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
- */
- <T> boolean insert(T t);
-
- /**
- * Insert an object into the database and return it's primary key.
- *
- * @param t
- * @return
- */
- <T> long insertAndGetKey(T t);
-
- /**
- * Insert all objects into the database.
- *
- * @param list
- */
- <T> void insertAll(List<T> list);
-
- /**
- * Insert all objects into the database and return the list of primary keys.
- *
- * @param t
- * @return a list of primary keys
- */
- <T> List<Long> insertAllAndGetKeys(List<T> t);
-
- /**
- * Updates an object in the database.
- *
- * @param t
- * @return true if successful
- */
- <T> boolean update(T t);
-
- /**
- * Updates all objects in the database.
- *
- * @param list
- */
- <T> void updateAll(List<T> list);
-
- /**
- * Inserts or updates an object in the database.
- *
- * @param t
- */
- <T> void merge(T t);
-
- /**
- * Deletes an object from the database.
- *
- * @param t
- * @return true if successful
- */
- <T> boolean delete(T t);
-
- /**
- * Deletes all objects from the database.
- *
- * @param list
- */
- <T> void deleteAll(List<T> 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
+ */
+ <T> boolean insert(T t);
+
+ /**
+ * Insert an object into the database and return it's primary key.
+ *
+ * @param t
+ * @return
+ */
+ <T> long insertAndGetKey(T t);
+
+ /**
+ * Insert all objects into the database.
+ *
+ * @param list
+ */
+ <T> void insertAll(List<T> list);
+
+ /**
+ * Insert all objects into the database and return the list of primary keys.
+ *
+ * @param t
+ * @return a list of primary keys
+ */
+ <T> List<Long> insertAllAndGetKeys(List<T> t);
+
+ /**
+ * Updates an object in the database.
+ *
+ * @param t
+ * @return true if successful
+ */
+ <T> boolean update(T t);
+
+ /**
+ * Updates all objects in the database.
+ *
+ * @param list
+ */
+ <T> void updateAll(List<T> list);
+
+ /**
+ * Inserts or updates an object in the database.
+ *
+ * @param t
+ */
+ <T> void merge(T t);
+
+ /**
+ * Deletes an object from the database.
+ *
+ * @param t
+ * @return true if successful
+ */
+ <T> boolean delete(T t);
+
+ /**
+ * Deletes all objects from the database.
+ *
+ * @param list
+ */
+ <T> void deleteAll(List<T> 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.
+ * <p>
+ * You don't need to explicitly bind the parameters as each parameter
+ * is accessible by the standard "argN" syntax (0-indexed).
+ * <p>
+ * 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 <X>
+ * @author James Moger
*/
final class DaoProxy<X extends Dao> implements InvocationHandler, Dao {
- private final Db db;
+ private final Db db;
- private final Class<X> daoInterface;
+ private final Class<X> daoInterface;
- private final char bindingDelimiter = ':';
+ private final char bindingDelimiter = ':';
- private final Map<Method, IndexedSql> indexedSqlCache;
+ private final Map<Method, IndexedSql> indexedSqlCache;
- DaoProxy(Db db, Class<X> daoInterface) {
- this.db = db;
- this.daoInterface = daoInterface;
- this.indexedSqlCache = new ConcurrentHashMap<Method, IndexedSql>();
- }
+ DaoProxy(Db db, Class<X> daoInterface) {
+ this.db = db;
+ this.daoInterface = daoInterface;
+ this.indexedSqlCache = new ConcurrentHashMap<Method, IndexedSql>();
+ }
- /**
- * 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<Class<?>> interfaces = new HashSet<Class<?>>();
- interfaces.add(Dao.class);
- interfaces.add(daoInterface);
- for (Class<?> clazz : daoInterface.getInterfaces()) {
- interfaces.add(clazz);
- }
+ Set<Class<?>> interfaces = new HashSet<Class<?>>();
+ 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<? extends DataTypeAdapter<?>> 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<? extends DataTypeAdapter<?>> 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<Object> objects;
- if (!isJavaType && adapter == null) {
+ List<Object> 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<String, IndexedArgument> 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<IndexedArgument> 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<String, IndexedArgument> buildParameterIndex(Method method) {
-
- Map<String, IndexedArgument> index = new TreeMap<String, IndexedArgument>();
-
- 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<String, IndexedArgument> 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<IndexedArgument> 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<String, IndexedArgument> buildParameterIndex(Method method) {
+
+ Map<String, IndexedArgument> index = new TreeMap<String, IndexedArgument>();
+
+ 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<String, IndexedArgument> beanIndex = buildBeanIndex(i, prefix, argumentClass);
- index.putAll(beanIndex);
- }
-
- Class<? extends DataTypeAdapter<?>> 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<String, IndexedArgument> buildBeanIndex(int argumentIndex, String prefix, Class<?> beanClass) {
-
- final String beanPrefix = StringUtils.isNullOrEmpty(prefix) ? "" : (prefix + ".");
- final Map<String, IndexedArgument> index = new TreeMap<String, IndexedArgument>();
-
- // 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> T getAnnotation(Class<T> 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<String, IndexedArgument> beanIndex = buildBeanIndex(i, prefix, argumentClass);
+ index.putAll(beanIndex);
+ }
+
+ Class<? extends DataTypeAdapter<?>> 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<String, IndexedArgument> buildBeanIndex(int argumentIndex, String prefix, Class<?> beanClass) {
+
+ final String beanPrefix = StringUtils.isNullOrEmpty(prefix) ? "" : (prefix + ".");
+ final Map<String, IndexedArgument> index = new TreeMap<String, IndexedArgument>();
+
+ // 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> T getAnnotation(Class<T> 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<X extends Dao> implements InvocationHandler, Dao {
*
*/
- @Override
- public final Db db() {
- return db;
- }
-
- @Override
- public final <T> boolean insert(T t) {
- return db.insert(t);
- }
-
- @Override
- public final <T> void insertAll(List<T> t) {
- db.insertAll(t);
- }
-
- @Override
- public final <T> long insertAndGetKey(T t) {
- return db.insertAndGetKey(t);
- }
-
- @Override
- public final <T> List<Long> insertAllAndGetKeys(List<T> t) {
- return db.insertAllAndGetKeys(t);
- }
-
- @Override
- public final <T> boolean update(T t) {
- return db.update(t);
- }
-
- @Override
- public final <T> void updateAll(List<T> t) {
- db.updateAll(t);
- }
-
- @Override
- public final <T> void merge(T t) {
- db.merge(t);
- }
-
- @Override
- public final <T> boolean delete(T t) {
- return db.delete(t);
- }
-
- @Override
- public final <T> void deleteAll(List<T> 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.
- * <p>
- * 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.
- * </p>
- */
- private class IndexedSql {
- final String sql;
- final List<IndexedArgument> indexedArgs;
-
- IndexedSql(String sql, List<IndexedArgument> 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 <T> boolean insert(T t) {
+ return db.insert(t);
+ }
+
+ @Override
+ public final <T> void insertAll(List<T> t) {
+ db.insertAll(t);
+ }
+
+ @Override
+ public final <T> long insertAndGetKey(T t) {
+ return db.insertAndGetKey(t);
+ }
+
+ @Override
+ public final <T> List<Long> insertAllAndGetKeys(List<T> t) {
+ return db.insertAllAndGetKeys(t);
+ }
+
+ @Override
+ public final <T> boolean update(T t) {
+ return db.update(t);
+ }
+
+ @Override
+ public final <T> void updateAll(List<T> t) {
+ db.updateAll(t);
+ }
+
+ @Override
+ public final <T> void merge(T t) {
+ db.merge(t);
+ }
+
+ @Override
+ public final <T> boolean delete(T t) {
+ return db.delete(t);
+ }
+
+ @Override
+ public final <T> void deleteAll(List<T> 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.
+ * <p>
+ * 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.
+ * </p>
+ */
+ private class IndexedSql {
+ final String sql;
+ final List<IndexedArgument> indexedArgs;
+
+ IndexedSql(String sql, List<IndexedArgument> 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<? extends DataTypeAdapter<?>> 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.
+ * <p>
+ * 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.
+ * </p>
+ */
+ private class IndexedArgument {
+ final int index;
+ final Class<? extends DataTypeAdapter<?>> typeAdapter;
+ final Method method;
+ final Field field;
+
+ IndexedArgument(int index, Class<? extends DataTypeAdapter<?>> 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<? extends DataTypeAdapter<?>> 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.
- * <p>
- * 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.
- * </p>
- */
- private class IndexedArgument {
- final int index;
- final Class<? extends DataTypeAdapter<?>> typeAdapter;
- final Method method;
- final Field field;
-
- IndexedArgument(int index, Class<? extends DataTypeAdapter<?>> 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<Object, Token> TOKENS;
-
- private static final Map<String, Class<? extends SQLDialect>> DIALECTS;
-
- private final Connection conn;
- private final Mode mode;
- private final Map<Class<?>, TableDefinition<?>> classMap = Collections
- .synchronizedMap(new HashMap<Class<?>, TableDefinition<?>>());
- private final SQLDialect dialect;
- private DbUpgrader dbUpgrader = new DefaultDbUpgrader();
- private final Set<Class<?>> upgradeChecked = Collections.synchronizedSet(new HashSet<Class<?>>());
-
- private boolean skipCreate;
- private boolean autoSavePoint = true;
- private DaoStatementProvider daoStatementProvider;
-
- static {
- TOKENS = Collections.synchronizedMap(new WeakIdentityHashMap<Object, Token>());
- DIALECTS = Collections.synchronizedMap(new HashMap<String, Class<? extends SQLDialect>>());
- // 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<? extends SQLDialect> dialectClass) {
- DIALECTS.put(token, dialectClass);
- }
-
- SQLDialect getDialect(String databaseName, String className) {
- Class<? extends SQLDialect> 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> X registerToken(X x, Token token) {
- TOKENS.put(x, token);
- return x;
- }
-
- static <X> boolean isToken(X x) {
- return TOKENS.containsKey(x);
- }
-
- static Token getToken(Object x) {
- return TOKENS.get(x);
- }
-
- static <T> T instance(Class<T> 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 extends Dao> X open(Class<X> daoClass) {
- return new DaoProxy<X>(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 <T> 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 <T> 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:
- * <p>
- * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0)
- * <p>
- * 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.
- * <p>
- * See the Derby dialect for an implementation of this technique.
- * <p>
- * If the dialect does not support merge an IciqlException will be thrown.
- *
- * @param t
- */
- public <T> 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:
- * <p>
- * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0)
- * <p>
- * 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.
- * <p>
- * See the Derby dialect for an implementation of this technique.
- * <p>
- * If the dialect does not support merge an IciqlException will be thrown.
- *
- * @param t
- */
- public <T> void merge(T t) {
- upsert(t);
- }
-
- public <T> boolean update(T t) {
- Class<?> clazz = t.getClass();
- return define(clazz).createIfRequired(this).update(this, t) == 1;
- }
-
- public <T> boolean delete(T t) {
- Class<?> clazz = t.getClass();
- return define(clazz).createIfRequired(this).delete(this, t) == 1;
- }
-
- public <T extends Object> Query<T> from(T alias) {
- Class<?> clazz = alias.getClass();
- define(clazz).createIfRequired(this);
- return Query.from(this, alias);
- }
-
- @SuppressWarnings("unchecked")
- public <T> boolean dropTable(Class<? extends T> modelClass) {
- TableDefinition<T> def = (TableDefinition<T>) 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 <T> boolean dropView(Class<? extends T> modelClass) {
- TableDefinition<T> def = (TableDefinition<T>) 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 <T> List<T> buildObjects(Class<? extends T> modelClass, ResultSet rs) {
- return buildObjects(modelClass, false, rs);
- }
-
- @SuppressWarnings("unchecked")
- public <T> List<T> buildObjects(Class<? extends T> modelClass, boolean wildcardSelect, ResultSet rs) {
- List<T> result = new ArrayList<T>();
- TableDefinition<T> def = (TableDefinition<T>) 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;
- }
-
- <T> void upgradeTable(TableDefinition<T> 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);
- }
- }
- }
- }
- }
- }
-
- <T> TableDefinition<T> define(Class<T> clazz) {
- TableDefinition<T> def = getTableDefinition(clazz);
- if (def == null) {
- upgradeDb();
- def = new TableDefinition<T>(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;
- }
-
- <T> boolean hasCreated(Class<T> 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 <A> TestCondition<A> test(A x) {
- return new TestCondition<A>(x);
- }
-
- public <T> void insertAll(List<T> 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 <T> List<Long> insertAllAndGetKeys(List<T> list) {
- List<Long> identities = new ArrayList<Long>();
- 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 <T> void updateAll(List<T> 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 <T> void deleteAll(List<T> 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")
- <T> TableDefinition<T> getTableDefinition(Class<T> clazz) {
- return (TableDefinition<T>) classMap.get(clazz);
- }
-
- /**
- * Run a SQL query directly against the database.
- *
- * Be sure to close the ResultSet with
- *
- * <pre>
- * JdbcUtils.closeSilently(rs, true);
- * </pre>
- *
- * @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
- *
- * <pre>
- * JdbcUtils.closeSilently(rs, true);
- * </pre>
- *
- * @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 <T> List<T> executeQuery(Class<? extends T> 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 <T> List<T> executeQuery(Class<? extends T> 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<Object, Token> TOKENS;
+
+ private static final Map<String, Class<? extends SQLDialect>> DIALECTS;
+
+ private final Connection conn;
+ private final Mode mode;
+ private final Map<Class<?>, TableDefinition<?>> classMap = Collections
+ .synchronizedMap(new HashMap<Class<?>, TableDefinition<?>>());
+ private final SQLDialect dialect;
+ private DbUpgrader dbUpgrader = new DefaultDbUpgrader();
+ private final Set<Class<?>> upgradeChecked = Collections.synchronizedSet(new HashSet<Class<?>>());
+
+ private boolean skipCreate;
+ private boolean autoSavePoint = true;
+ private DaoStatementProvider daoStatementProvider;
+
+ static {
+ TOKENS = Collections.synchronizedMap(new WeakIdentityHashMap<Object, Token>());
+ DIALECTS = Collections.synchronizedMap(new HashMap<String, Class<? extends SQLDialect>>());
+ // 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<? extends SQLDialect> dialectClass) {
+ DIALECTS.put(token, dialectClass);
+ }
+
+ SQLDialect getDialect(String databaseName, String className) {
+ Class<? extends SQLDialect> 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> X registerToken(X x, Token token) {
+ TOKENS.put(x, token);
+ return x;
+ }
+
+ static <X> boolean isToken(X x) {
+ return TOKENS.containsKey(x);
+ }
+
+ static Token getToken(Object x) {
+ return TOKENS.get(x);
+ }
+
+ static <T> T instance(Class<T> 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 extends Dao> X open(Class<X> daoClass) {
+ return new DaoProxy<X>(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 <T> 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 <T> 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.
+ * <p>
+ * If the database does not support a MERGE or INSERT OR REPLACE INTO syntax
+ * the dialect can try to simulate a merge by implementing:
+ * <p>
+ * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0)
+ * <p>
+ * 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.
+ * <p>
+ * See the Derby dialect for an implementation of this technique.
+ * <p>
+ * If the dialect does not support merge an IciqlException will be thrown.
+ *
+ * @param t
+ */
+ public <T> 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.
+ * <p>
+ * If the database does not support a MERGE or INSERT OR REPLACE INTO syntax
+ * the dialect can try to simulate a merge by implementing:
+ * <p>
+ * INSERT INTO foo... (SELECT ?,... FROM foo WHERE pk=? HAVING count(*)=0)
+ * <p>
+ * 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.
+ * <p>
+ * See the Derby dialect for an implementation of this technique.
+ * <p>
+ * If the dialect does not support merge an IciqlException will be thrown.
+ *
+ * @param t
+ */
+ public <T> void merge(T t) {
+ upsert(t);
+ }
+
+ public <T> boolean update(T t) {
+ Class<?> clazz = t.getClass();
+ return define(clazz).createIfRequired(this).update(this, t) == 1;
+ }
+
+ public <T> boolean delete(T t) {
+ Class<?> clazz = t.getClass();
+ return define(clazz).createIfRequired(this).delete(this, t) == 1;
+ }
+
+ public <T extends Object> Query<T> from(T alias) {
+ Class<?> clazz = alias.getClass();
+ define(clazz).createIfRequired(this);
+ return Query.from(this, alias);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> boolean dropTable(Class<? extends T> modelClass) {
+ TableDefinition<T> def = (TableDefinition<T>) 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 <T> boolean dropView(Class<? extends T> modelClass) {
+ TableDefinition<T> def = (TableDefinition<T>) 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 <T> List<T> buildObjects(Class<? extends T> modelClass, ResultSet rs) {
+ return buildObjects(modelClass, false, rs);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T> List<T> buildObjects(Class<? extends T> modelClass, boolean wildcardSelect, ResultSet rs) {
+ List<T> result = new ArrayList<T>();
+ TableDefinition<T> def = (TableDefinition<T>) 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;
+ }
+
+ <T> void upgradeTable(TableDefinition<T> 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);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ <T> TableDefinition<T> define(Class<T> clazz) {
+ TableDefinition<T> def = getTableDefinition(clazz);
+ if (def == null) {
+ upgradeDb();
+ def = new TableDefinition<T>(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;
+ }
+
+ <T> boolean hasCreated(Class<T> 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 <A> TestCondition<A> test(A x) {
+ return new TestCondition<A>(x);
+ }
+
+ public <T> void insertAll(List<T> 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 <T> List<Long> insertAllAndGetKeys(List<T> list) {
+ List<Long> identities = new ArrayList<Long>();
+ 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 <T> void updateAll(List<T> 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 <T> void deleteAll(List<T> 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")
+ <T> TableDefinition<T> getTableDefinition(Class<T> clazz) {
+ return (TableDefinition<T>) classMap.get(clazz);
+ }
+
+ /**
+ * Run a SQL query directly against the database.
+ * <p>
+ * Be sure to close the ResultSet with
+ * <p>
+ * <pre>
+ * JdbcUtils.closeSilently(rs, true);
+ * </pre>
+ *
+ * @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.
+ * <p>
+ * Be sure to close the ResultSet with
+ * <p>
+ * <pre>
+ * JdbcUtils.closeSilently(rs, true);
+ * </pre>
+ *
+ * @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 <T> List<T> executeQuery(Class<? extends T> 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 <T> List<T> executeQuery(Class<? extends T> 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<? extends java.util.Date> 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<? extends java.util.Date> 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<String> generateModel(String schema, String table, String packageName,
- boolean annotateSchema, boolean trimStrings) {
- try {
- List<String> models = Utils.newArrayList();
- List<TableInspector> 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 <T> List<ValidationRemark> validateModel(T model, boolean throwOnError) {
- try {
- TableInspector inspector = getTable(model);
- inspector.read(metaData);
- @SuppressWarnings("unchecked")
- Class<T> clazz = (Class<T>) model.getClass();
- TableDefinition<T> 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 <T> TableInspector getTable(T model) throws SQLException {
- @SuppressWarnings("unchecked")
- Class<T> clazz = (Class<T>) model.getClass();
- TableDefinition<T> 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<TableInspector> 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<TableInspector> getTables(String schema, String table) throws SQLException {
- ResultSet rs = null;
- try {
- rs = getMetaData().getSchemas();
- ArrayList<String> schemaList = Utils.newArrayList();
- while (rs.next()) {
- schemaList.add(rs.getString("TABLE_SCHEM"));
- }
- JdbcUtils.closeSilently(rs);
-
- String iciqlTables = DbVersion.class.getAnnotation(IQTable.class).name();
-
- List<TableInspector> 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<TableInspector> 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<? extends java.util.Date> 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<? extends java.util.Date> 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<String> generateModel(String schema, String table, String packageName,
+ boolean annotateSchema, boolean trimStrings) {
+ try {
+ List<String> models = Utils.newArrayList();
+ List<TableInspector> 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 <T> List<ValidationRemark> validateModel(T model, boolean throwOnError) {
+ try {
+ TableInspector inspector = getTable(model);
+ inspector.read(metaData);
+ @SuppressWarnings("unchecked")
+ Class<T> clazz = (Class<T>) model.getClass();
+ TableDefinition<T> 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 <T> TableInspector getTable(T model) throws SQLException {
+ @SuppressWarnings("unchecked")
+ Class<T> clazz = (Class<T>) model.getClass();
+ TableDefinition<T> 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<TableInspector> 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<TableInspector> getTables(String schema, String table) throws SQLException {
+ ResultSet rs = null;
+ try {
+ rs = getMetaData().getSchemas();
+ ArrayList<String> schemaList = Utils.newArrayList();
+ while (rs.next()) {
+ schemaList.add(rs.getString("TABLE_SCHEM"));
+ }
+ JdbcUtils.closeSilently(rs);
+
+ String iciqlTables = DbVersion.class.getAnnotation(IQTable.class).name();
+
+ List<TableInspector> 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<TableInspector> 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 <i>DbUpgrader</i> 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 <i>DbUpgrader</i> 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<? extends DataTypeAdapter<?>> typeAdapter) {
- checkInDefine();
- currentTableDefinition.defineTypeAdapter(column, typeAdapter);
- }
-
- static synchronized <T> void define(TableDefinition<T> 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<? extends DataTypeAdapter<?>> typeAdapter) {
+ checkInDefine();
+ currentTableDefinition.defineTypeAdapter(column, typeAdapter);
+ }
+
+ static synchronized <T> void define(TableDefinition<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 extends Number> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> query) {
- int i = 0;
- for (Object o : x) {
- if (i++ > 0) {
- stat.appendSQL(" AND ");
- }
- query.appendSQL(stat, null, o);
- }
- }
- });
- }
-
- @SuppressWarnings("unchecked")
- public static <X> X min(X x) {
- Class<X> clazz = (Class<X>) x.getClass();
- X o = Utils.newObject(clazz);
- return Db.registerToken(o, new Function("MIN", x));
- }
-
- @SuppressWarnings("unchecked")
- public static <X> X max(X x) {
- Class<X> clazz = (Class<X>) 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 extends Number> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> query) {
+ int i = 0;
+ for (Object o : x) {
+ if (i++ > 0) {
+ stat.appendSQL(" AND ");
+ }
+ query.appendSQL(stat, null, o);
+ }
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <X> X min(X x) {
+ Class<X> clazz = (Class<X>) x.getClass();
+ X o = Utils.newObject(clazz);
+ return Db.registerToken(o, new Function("MIN", x));
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <X> X max(X x) {
+ Class<X> clazz = (Class<X>) 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 <T> void appendSQL(SQLStatement stat, Query<T> 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;
* <p>
* Automatic model generation: you may automatically generate model classes as
* strings with the Db and DbInspector objects:
- *
+ * <p>
* <pre>
* Db db = Db.open(&quot;jdbc:h2:mem:&quot;, &quot;sa&quot;, &quot;sa&quot;);
* DbInspector inspector = new DbInspector(db);
@@ -179,10 +179,10 @@ import java.lang.annotation.Target;
* inspector.generateModel(schema, table, packageName,
* annotateSchema, trimStrings)
* </pre>
- *
+ * <p>
* Or you may use the GenerateModels tool to generate and save your classes to
* the file system:
- *
+ * <p>
* <pre>
* java -jar iciql.jar
* -url &quot;jdbc:h2:mem:&quot;
@@ -190,10 +190,10 @@ import java.lang.annotation.Target;
* -package packageName -folder destination
* -annotateSchema false -trimStrings true
* </pre>
- *
+ * <p>
* Model validation: you may validate your model class with DbInspector object.
* The DbInspector will report errors, warnings, and suggestions:
- *
+ * <p>
* <pre>
* Db db = Db.open(&quot;jdbc:h2:mem:&quot;, &quot;sa&quot;, &quot;sa&quot;);
* DbInspector inspector = new DbInspector(db);
@@ -205,615 +205,613 @@ import java.lang.annotation.Target;
*/
public interface Iciql {
- /**
- * An annotation for an iciql version.
- * <p>
- *
- * @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.
- * <p>
- *
- * @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.
- * <p>
- * <ul>
- * <li>@IQIndex("name")
- * <li>@IQIndex({"street", "city"})
- * <li>@IQIndex(name="streetidx", value={"street", "city"})
- * <li>@IQIndex(name="addressidx", type=IndexType.UNIQUE,
- * value={"house_number", "street", "city"})
- * </ul>
- */
- @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.
- * <ul>
- * <li>com.iciql.iciql.IndexType.STANDARD
- * <li>com.iciql.iciql.IndexType.UNIQUE
- * <li>com.iciql.iciql.IndexType.HASH
- * <li>com.iciql.iciql.IndexType.UNIQUE_HASH
- * </ul>
- *
- * HASH indexes may only be valid for single column indexes.
- *
- */
- IndexType type() default IndexType.STANDARD;
-
- /**
- * Columns to include in index.
- * <ul>
- * <li>single column index: value = "id"
- * <li>multiple column index: value = { "id", "name", "date" }
- * </ul>
- */
- 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.
- * <p>
- * <ul>
- * <li>@IQContraintForeignKey(
- * foreignColumns = { "idaccount"},
- * referenceName = "account",
- * referenceColumns = { "id" },
- * deleteType = ConstrainDeleteType.CASCADE,
- * updateType = ConstraintUpdateType.NO_ACTION )
- * </ul>
- * 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.
- * <ul>
- * <li>com.iciql.iciql.ConstrainDeleteType.CASCADE
- * <li>com.iciql.iciql.ConstrainDeleteType.RESTRICT
- * <li>com.iciql.iciql.ConstrainDeleteType.SET_NULL
- * <li>com.iciql.iciql.ConstrainDeleteType.NO_ACTION
- * <li>com.iciql.iciql.ConstrainDeleteType.SET_DEFAULT
- * </ul>
- */
- ConstraintDeleteType deleteType() default ConstraintDeleteType.UNSET;
-
- /**
- * Type of the action on update, default to unspecified.
- * <ul>
- * <li>com.iciql.iciql.ConstrainUpdateType.CASCADE
- * <li>com.iciql.iciql.ConstrainUpdateType.RESTRICT
- * <li>com.iciql.iciql.ConstrainUpdateType.SET_NULL
- * <li>com.iciql.iciql.ConstrainUpdateType.NO_ACTION
- * <li>com.iciql.iciql.ConstrainUpdateType.SET_DEFAULT
- * </ul>
- */
- ConstraintUpdateType updateType() default ConstraintUpdateType.UNSET;
-
- /**
- * Type of the deferrability mode, default to unspecified
- * <ul>
- * <li>com.iciql.iciql.ConstrainUpdateType.CASCADE
- * <li>ConstraintDeferrabilityType.DEFERRABLE_INITIALLY_DEFERRED
- * <li>ConstraintDeferrabilityType.DEFERRABLE_INITIALLY_IMMEDIATE
- * <li>ConstraintDeferrabilityType.NOT_DEFERRABLE
- * </ul>
- */
- ConstraintDeferrabilityType deferrabilityType() default ConstraintDeferrabilityType.UNSET;
-
- /**
- * The source table for the columns defined as foreign.
- */
- String tableName() default "";
-
- /**
- * Columns defined as 'foreign'.
- * <ul>
- * <li>single column : foreignColumns = "id"
- * <li>multiple column : foreignColumns = { "id", "name", "date" }
- * </ul>
- */
- String[] foreignColumns() default {};
-
- /**
- * The reference table for the columns defined as references.
- */
- String referenceName() default "";
-
- /**
- * Columns defined as 'references'.
- * <ul>
- * <li>single column : referenceColumns = "id"
- * <li>multiple column : referenceColumns = { "id", "name", "date" }
- * </ul>
- */
- 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.
- * <p>
- * <ul>
- * <li>@IQContraintUnique(uniqueColumns = { "street", "city" })
- * <li>@IQContraintUnique(name="streetconstraint", uniqueColumns = { "street", "city" })
- * </ul>
- */
- @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'.
- * <ul>
- * <li>single column : uniqueColumns = "id"
- * <li>multiple column : uniqueColumns = { "id", "name", "date" }
- * </ul>
- */
- 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.
- * <p>
- * 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.
- * <p>
- * 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.
- * <p>
- * IQConstraint("this > 2 AND this <= 7")
- * <p>
- * 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.
- * <p>
- * 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.
- * <ul>
- * <li>single column primaryKey: value = "id"
- * <li>compound primary key: value = { "id", "name" }
- * </ul>
- * 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.
- * <p>
- * 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).
- * <p>
- * 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.
- * <p>
- * 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:
- * <ul>
- * <li>defaultValue="" (null)
- * <li>defaultValue="CURRENT_TIMESTAMP"
- * <li>defaultValue="''" (empty string)
- * <li>defaultValue="'0'"
- * <li>defaultValue="'1970-01-01 00:00:01'"
- * </ul>
- * 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.
- * <p>
- * 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.
- * <p>
- * Default: unspecified (null).
- */
- String defaultValue() default "";
-
- }
-
- /**
- * Interface for using the EnumType.ENUMID enumeration mapping strategy.
- * <p>
- * Enumerations wishing to use EnumType.ENUMID must implement this
- * interface.
- */
- public interface EnumId<X> {
- X enumId();
- Class<X> enumIdClass();
- }
-
-
-
- /**
- * Enumeration representing how to map a java.lang.Enum to a column.
- * <p>
- * <ul>
- * <li>NAME - name() : string
- * <li>ORDINAL - ordinal() : int
- * <li>ENUMID - enumId() : X
- * </ul>
- *
- * @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.
- * <p>
- * This annotation can be used on:
- * <ul>
- * <li>a field instance of an enumeration type
- * <li>on the enumeration class declaration
- * </ul>
- * 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.
- * <p>
- * The default mapping is by NAME.
- *
- * <pre>
- * IQEnum(EnumType.NAME)
- * </pre>
- *
- * 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<? extends DataTypeAdapter<?>> 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.
- * <p><b>NOTE:</b> Data type adapters are not thread-safe!</p>
- *
- * @param <T>
- */
- public interface DataTypeAdapter<T> {
-
- /**
- * 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<T> getJavaType();
-
-
- /**
- * Set the runtime mode.
- * <p>
- * Allows type adapters to adapt type mappings based on the runtime
- * mode.
- * </p>
- *
- * @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.
+ * <p>
+ *
+ * @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.
+ * <p>
+ *
+ * @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.
+ * <p>
+ * <ul>
+ * <li>@IQIndex("name")
+ * <li>@IQIndex({"street", "city"})
+ * <li>@IQIndex(name="streetidx", value={"street", "city"})
+ * <li>@IQIndex(name="addressidx", type=IndexType.UNIQUE,
+ * value={"house_number", "street", "city"})
+ * </ul>
+ */
+ @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.
+ * <ul>
+ * <li>com.iciql.iciql.IndexType.STANDARD
+ * <li>com.iciql.iciql.IndexType.UNIQUE
+ * <li>com.iciql.iciql.IndexType.HASH
+ * <li>com.iciql.iciql.IndexType.UNIQUE_HASH
+ * </ul>
+ * <p>
+ * HASH indexes may only be valid for single column indexes.
+ */
+ IndexType type() default IndexType.STANDARD;
+
+ /**
+ * Columns to include in index.
+ * <ul>
+ * <li>single column index: value = "id"
+ * <li>multiple column index: value = { "id", "name", "date" }
+ * </ul>
+ */
+ 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.
+ * <p>
+ * <ul>
+ * <li>@IQContraintForeignKey(
+ * foreignColumns = { "idaccount"},
+ * referenceName = "account",
+ * referenceColumns = { "id" },
+ * deleteType = ConstrainDeleteType.CASCADE,
+ * updateType = ConstraintUpdateType.NO_ACTION )
+ * </ul>
+ * 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.
+ * <ul>
+ * <li>com.iciql.iciql.ConstrainDeleteType.CASCADE
+ * <li>com.iciql.iciql.ConstrainDeleteType.RESTRICT
+ * <li>com.iciql.iciql.ConstrainDeleteType.SET_NULL
+ * <li>com.iciql.iciql.ConstrainDeleteType.NO_ACTION
+ * <li>com.iciql.iciql.ConstrainDeleteType.SET_DEFAULT
+ * </ul>
+ */
+ ConstraintDeleteType deleteType() default ConstraintDeleteType.UNSET;
+
+ /**
+ * Type of the action on update, default to unspecified.
+ * <ul>
+ * <li>com.iciql.iciql.ConstrainUpdateType.CASCADE
+ * <li>com.iciql.iciql.ConstrainUpdateType.RESTRICT
+ * <li>com.iciql.iciql.ConstrainUpdateType.SET_NULL
+ * <li>com.iciql.iciql.ConstrainUpdateType.NO_ACTION
+ * <li>com.iciql.iciql.ConstrainUpdateType.SET_DEFAULT
+ * </ul>
+ */
+ ConstraintUpdateType updateType() default ConstraintUpdateType.UNSET;
+
+ /**
+ * Type of the deferrability mode, default to unspecified
+ * <ul>
+ * <li>com.iciql.iciql.ConstrainUpdateType.CASCADE
+ * <li>ConstraintDeferrabilityType.DEFERRABLE_INITIALLY_DEFERRED
+ * <li>ConstraintDeferrabilityType.DEFERRABLE_INITIALLY_IMMEDIATE
+ * <li>ConstraintDeferrabilityType.NOT_DEFERRABLE
+ * </ul>
+ */
+ ConstraintDeferrabilityType deferrabilityType() default ConstraintDeferrabilityType.UNSET;
+
+ /**
+ * The source table for the columns defined as foreign.
+ */
+ String tableName() default "";
+
+ /**
+ * Columns defined as 'foreign'.
+ * <ul>
+ * <li>single column : foreignColumns = "id"
+ * <li>multiple column : foreignColumns = { "id", "name", "date" }
+ * </ul>
+ */
+ String[] foreignColumns() default {};
+
+ /**
+ * The reference table for the columns defined as references.
+ */
+ String referenceName() default "";
+
+ /**
+ * Columns defined as 'references'.
+ * <ul>
+ * <li>single column : referenceColumns = "id"
+ * <li>multiple column : referenceColumns = { "id", "name", "date" }
+ * </ul>
+ */
+ 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.
+ * <p>
+ * <ul>
+ * <li>@IQContraintUnique(uniqueColumns = { "street", "city" })
+ * <li>@IQContraintUnique(name="streetconstraint", uniqueColumns = { "street", "city" })
+ * </ul>
+ */
+ @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'.
+ * <ul>
+ * <li>single column : uniqueColumns = "id"
+ * <li>multiple column : uniqueColumns = { "id", "name", "date" }
+ * </ul>
+ */
+ 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * IQConstraint("this > 2 AND this <= 7")
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <ul>
+ * <li>single column primaryKey: value = "id"
+ * <li>compound primary key: value = { "id", "name" }
+ * </ul>
+ * 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.
+ * <p>
+ * 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).
+ * <p>
+ * 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.
+ * <p>
+ * 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:
+ * <ul>
+ * <li>defaultValue="" (null)
+ * <li>defaultValue="CURRENT_TIMESTAMP"
+ * <li>defaultValue="''" (empty string)
+ * <li>defaultValue="'0'"
+ * <li>defaultValue="'1970-01-01 00:00:01'"
+ * </ul>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * Default: unspecified (null).
+ */
+ String defaultValue() default "";
+
+ }
+
+ /**
+ * Interface for using the EnumType.ENUMID enumeration mapping strategy.
+ * <p>
+ * Enumerations wishing to use EnumType.ENUMID must implement this
+ * interface.
+ */
+ public interface EnumId<X> {
+ X enumId();
+
+ Class<X> enumIdClass();
+ }
+
+
+ /**
+ * Enumeration representing how to map a java.lang.Enum to a column.
+ * <p>
+ * <ul>
+ * <li>NAME - name() : string
+ * <li>ORDINAL - ordinal() : int
+ * <li>ENUMID - enumId() : X
+ * </ul>
+ *
+ * @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.
+ * <p>
+ * This annotation can be used on:
+ * <ul>
+ * <li>a field instance of an enumeration type
+ * <li>on the enumeration class declaration
+ * </ul>
+ * 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.
+ * <p>
+ * The default mapping is by NAME.
+ * <p>
+ * <pre>
+ * IQEnum(EnumType.NAME)
+ * </pre>
+ * <p>
+ * 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<? extends DataTypeAdapter<?>> 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.
+ * <p><b>NOTE:</b> Data type adapters are not thread-safe!</p>
+ *
+ * @param <T>
+ */
+ public interface DataTypeAdapter<T> {
+
+ /**
+ * 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<T> getJavaType();
+
+
+ /**
+ * Set the runtime mode.
+ * <p>
+ * Allows type adapters to adapt type mappings based on the runtime
+ * mode.
+ * </p>
+ *
+ * @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<Class<?>, String> SUPPORTED_TYPES = new HashMap<Class<?>, String>();
-
- static {
- Map<Class<?>, 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<String, String> SQL_TYPES = new HashMap<String, String>();
-
- static {
- Map<String, String> 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<String> 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<? extends java.util.Date> 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<? extends Date> 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<Class<?>, String> SUPPORTED_TYPES = new HashMap<Class<?>, String>();
+
+ static {
+ Map<Class<?>, 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<String, String> SQL_TYPES = new HashMap<String, String>();
+
+ static {
+ Map<String, String> 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<String> 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<? extends java.util.Date> 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<? extends Date> 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<T> {
- public static class And<T> extends NestedConditions<T> {
+ public static class And<T> extends NestedConditions<T> {
- public And(Db db, T alias) {
- super(db, alias);
- }
+ public And(Db db, T alias) {
+ super(db, alias);
+ }
- protected QueryCondition<T, Boolean> and(boolean x) {
- return where.and(x);
- }
+ protected QueryCondition<T, Boolean> and(boolean x) {
+ return where.and(x);
+ }
- protected QueryCondition<T, Byte> and(byte x) {
- return where.and(x);
- }
+ protected QueryCondition<T, Byte> and(byte x) {
+ return where.and(x);
+ }
- protected QueryCondition<T, Short> and(short x) {
- return where.and(x);
- }
+ protected QueryCondition<T, Short> and(short x) {
+ return where.and(x);
+ }
- protected QueryCondition<T, Integer> and(int x) {
- return where.and(x);
- }
+ protected QueryCondition<T, Integer> and(int x) {
+ return where.and(x);
+ }
- protected QueryCondition<T, Long> and(long x) {
- return where.and(x);
- }
+ protected QueryCondition<T, Long> and(long x) {
+ return where.and(x);
+ }
- protected QueryCondition<T, Float> and(float x) {
- return where.and(x);
- }
+ protected QueryCondition<T, Float> and(float x) {
+ return where.and(x);
+ }
- protected QueryCondition<T, Double> and(double x) {
- return where.and(x);
- }
+ protected QueryCondition<T, Double> and(double x) {
+ return where.and(x);
+ }
- protected <A> QueryCondition<T, A> and(A x) {
- return where.and(x);
- }
+ protected <A> QueryCondition<T, A> and(A x) {
+ return where.and(x);
+ }
- protected QueryWhere<T> and(And<T> conditions) {
- where.andOpen();
- where.query.addConditionToken(conditions.where.query);
- return where.close();
- }
+ protected QueryWhere<T> and(And<T> conditions) {
+ where.andOpen();
+ where.query.addConditionToken(conditions.where.query);
+ return where.close();
+ }
- protected QueryWhere<T> and(Or<T> conditions) {
- where.andOpen();
- where.query.addConditionToken(conditions.where.query);
- return where.close();
- }
+ protected QueryWhere<T> and(Or<T> conditions) {
+ where.andOpen();
+ where.query.addConditionToken(conditions.where.query);
+ return where.close();
+ }
- }
+ }
- public static class Or<T> extends NestedConditions<T> {
+ public static class Or<T> extends NestedConditions<T> {
- public Or(Db db, T alias) {
- super(db, alias);
- }
+ public Or(Db db, T alias) {
+ super(db, alias);
+ }
- protected QueryCondition<T, Boolean> or(boolean x) {
- return where.or(x);
- }
+ protected QueryCondition<T, Boolean> or(boolean x) {
+ return where.or(x);
+ }
- protected QueryCondition<T, Byte> or(byte x) {
- return where.or(x);
- }
+ protected QueryCondition<T, Byte> or(byte x) {
+ return where.or(x);
+ }
- protected QueryCondition<T, Short> or(short x) {
- return where.or(x);
- }
+ protected QueryCondition<T, Short> or(short x) {
+ return where.or(x);
+ }
- protected QueryCondition<T, Integer> or(int x) {
- return where.or(x);
- }
+ protected QueryCondition<T, Integer> or(int x) {
+ return where.or(x);
+ }
- protected QueryCondition<T, Long> or(long x) {
- return where.or(x);
- }
+ protected QueryCondition<T, Long> or(long x) {
+ return where.or(x);
+ }
- protected QueryCondition<T, Float> or(float x) {
- return where.or(x);
- }
+ protected QueryCondition<T, Float> or(float x) {
+ return where.or(x);
+ }
- protected QueryCondition<T, Double> or(double x) {
- return where.or(x);
- }
+ protected QueryCondition<T, Double> or(double x) {
+ return where.or(x);
+ }
- protected <A> QueryCondition<T, A> or(A x) {
- return where.or(x);
- }
+ protected <A> QueryCondition<T, A> or(A x) {
+ return where.or(x);
+ }
- protected QueryWhere<T> or(And<T> conditions) {
- where.orOpen();
- where.query.addConditionToken(conditions.where.query);
- return where.close();
- }
+ protected QueryWhere<T> or(And<T> conditions) {
+ where.orOpen();
+ where.query.addConditionToken(conditions.where.query);
+ return where.close();
+ }
- protected QueryWhere<T> or(Or<T> conditions) {
- where.orOpen();
- where.query.addConditionToken(conditions.where.query);
- return where.close();
- }
+ protected QueryWhere<T> or(Or<T> conditions) {
+ where.orOpen();
+ where.query.addConditionToken(conditions.where.query);
+ return where.close();
+ }
- }
+ }
- QueryWhere<T> where;
+ QueryWhere<T> where;
- private NestedConditions(Db db, T alias) {
- where = new QueryWhere<T>(Query.rebuild(db, alias));
- }
+ private NestedConditions(Db db, T alias) {
+ where = new QueryWhere<T>(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 <T>
- * the query data type
+ *
+ * @param <T> the query data type
*/
class OrderExpression<T> {
- private Query<T> query;
- private Object expression;
- private boolean desc;
- private boolean nullsFirst;
- private boolean nullsLast;
+ private Query<T> query;
+ private Object expression;
+ private boolean desc;
+ private boolean nullsFirst;
+ private boolean nullsLast;
- OrderExpression(Query<T> 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<T> 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 <T>
- * the return type
+ * @param <T> the return type
*/
public class Query<T> {
- private Db db;
- private SelectTable<T> from;
- private ArrayList<Token> conditions = Utils.newArrayList();
- private ArrayList<UpdateColumn> updateColumnDeclarations = Utils.newArrayList();
- private int conditionDepth = 0;
- private ArrayList<SelectTable<T>> joins = Utils.newArrayList();
- private final IdentityHashMap<Object, SelectColumn<T>> aliasMap = Utils.newIdentityHashMap();
- private ArrayList<OrderExpression<T>> orderByList = Utils.newArrayList();
- private ArrayList<Object> 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 <T> Query<T> from(Db db, T alias) {
- Query<T> query = new Query<T>(db);
- TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
- query.from = new SelectTable<T>(db, query, alias, false);
- def.initSelectObject(query.from, alias, query.aliasMap, false);
- return query;
- }
-
- @SuppressWarnings("unchecked")
- static <T> Query<T> rebuild(Db db, T alias) {
- Query<T> query = new Query<T>(db);
- TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
- query.from = new SelectTable<T>(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<T> select() {
- return select(false);
- }
-
- public T selectFirst() {
- List<T> list = limit(1).select(false);
- return list.isEmpty() ? null : list.get(0);
- }
-
- public List<T> selectDistinct() {
- return select(true);
- }
-
- public <X, Z> X selectFirst(Z x) {
- List<X> list = limit(1).select(x);
- return list.isEmpty() ? null : list.get(0);
- }
-
- public <X> void createView(Class<X> viewClass) {
- TableDefinition<X> 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 <X> void replaceView(Class<X> 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 <K> 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();
- }
-
- <Z> String toSubQuery(Z z) {
- SQLStatement stat = getSelectStatement(false);
- SelectColumn<T> col = aliasMap.get(z);
- String columnName = col.getFieldDefinition().columnName;
- stat.appendColumn(columnName);
- appendFromWhere(stat);
- return stat.toSQL();
- }
-
- private List<T> select(boolean distinct) {
- List<T> result = Utils.newArrayList();
- TableDefinition<T> 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 <A> Query<T> setNull(A field) {
- return set(field).to(null);
- }
-
- public <A> UpdateColumnSet<T, A> set(A field) {
- from.getAliasDefinition().checkMultipleEnums(field);
- return new UpdateColumnSet<T, A>(this, field);
- }
-
- public UpdateColumnSet<T, Boolean> set(boolean field) {
- from.getAliasDefinition().checkMultipleBooleans();
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Byte> set(byte field) {
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Short> set(short field) {
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Integer> set(int field) {
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Long> set(long field) {
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Float> set(float field) {
- return setPrimitive(field);
- }
-
- public UpdateColumnSet<T, Double> set(double field) {
- return setPrimitive(field);
- }
-
- private <A> UpdateColumnSet<T, A> 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 <A> UpdateColumnIncrement<T, A> increment(A field) {
- return new UpdateColumnIncrement<T, A>(this, field);
- }
-
- public UpdateColumnIncrement<T, Byte> increment(byte field) {
- return incrementPrimitive(field);
- }
-
- public UpdateColumnIncrement<T, Short> increment(short field) {
- return incrementPrimitive(field);
- }
-
- public UpdateColumnIncrement<T, Integer> increment(int field) {
- return incrementPrimitive(field);
- }
-
- public UpdateColumnIncrement<T, Long> increment(long field) {
- return incrementPrimitive(field);
- }
-
- public UpdateColumnIncrement<T, Float> increment(float field) {
- return incrementPrimitive(field);
- }
-
- public UpdateColumnIncrement<T, Double> increment(double field) {
- return incrementPrimitive(field);
- }
-
- private <A> UpdateColumnIncrement<T, A> 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 <X, Z> List<X> selectDistinct(Z x) {
- return select(x, true);
- }
-
- public <X, Z> List<X> select(Z x) {
- return select(x, false);
- }
-
- @SuppressWarnings("unchecked")
- private <X, Z> List<X> 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<T> col = getColumnByReference(x);
- if (col == null) {
- col = getColumnByReference(getPrimitiveAliasByValue(x));
- }
- if (col != null) {
- return (List<X>) 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<X>) clazz, (X) x, distinct);
- }
-
- private <X> List<X> select(Class<X> clazz, X x, boolean distinct) {
- List<X> result = Utils.newArrayList();
- TableDefinition<X> 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 <X> List<X> selectFunction(X x, boolean distinct) {
- SQLStatement stat = getSelectStatement(distinct);
- appendSQL(stat, null, x);
- appendFromWhere(stat);
- ResultSet rs = stat.executeQuery();
- List<X> 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 <X> List<X> selectColumn(SelectColumn<T> col, Class<X> clazz, boolean distinct) {
- SQLStatement stat = getSelectStatement(distinct);
- col.appendSQL(stat);
- appendFromWhere(stat);
- ResultSet rs = stat.executeQuery();
- List<X> result = Utils.newArrayList();
- Class<? extends DataTypeAdapter<?>> 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<T, Boolean> 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<T, Byte> 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<T, Short> 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<T, Integer> 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<T, Long> 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<T, Float> 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<T, Double> where(double x) {
- return wherePrimitive(x);
- }
-
- /**
- * Begins a primitive field condition clause.
- *
- * @param value
- * @return a query condition to continue building the condition
- */
- private <A> QueryCondition<T, A> 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 <A> QueryCondition<T, A> where(A x) {
- from.getAliasDefinition().checkMultipleEnums(x);
- return new QueryCondition<T, A>(this, x);
- }
-
- public <A> QueryWhere<T> where(Filter filter) {
- HashMap<String, Object> fieldMap = Utils.newHashMap();
- for (Field f : filter.getClass().getDeclaredFields()) {
- f.setAccessible(true);
- try {
- Object obj = f.get(filter);
- if (obj == from.getAlias()) {
- List<TableDefinition.FieldDefinition> 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<T>(this);
- }
-
- public QueryWhere<T> where(String fragment, List<?> args) {
- return this.where(fragment, args.toArray());
- }
-
- public QueryWhere<T> where(String fragment, Object... args) {
- conditions.add(new RuntimeToken(fragment, args));
- return new QueryWhere<T>(this);
- }
-
- public Query<T> where(And<T> conditions) {
- whereTrue();
- addConditionToken(conditions.where.query);
- return this;
- }
-
- public Query<T> where(Or<T> conditions) {
- whereFalse();
- addConditionToken(conditions.where.query);
- return this;
- }
-
- public QueryWhere<T> whereTrue() {
- return whereTrue(true);
- }
-
- public QueryWhere<T> whereFalse() {
- return whereTrue(false);
- }
-
- public QueryWhere<T> whereTrue(Boolean condition) {
- Token token = new Function("", condition);
- addConditionToken(token);
- return new QueryWhere<T>(this);
- }
-
- /**
- * Sets the Limit and Offset of a query.
- *
- * @return the query
- */
-
- public Query<T> limit(long limit) {
- this.limit = limit;
- return this;
- }
-
- public Query<T> offset(long offset) {
- this.offset = offset;
- return this;
- }
-
- public Query<T> orderBy(boolean field) {
- from.getAliasDefinition().checkMultipleBooleans();
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(byte field) {
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(short field) {
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(int field) {
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(long field) {
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(float field) {
- return orderByPrimitive(field);
- }
-
- public Query<T> orderBy(double field) {
- return orderByPrimitive(field);
- }
-
- Query<T> orderByPrimitive(Object field) {
- Object alias = getPrimitiveAliasByValue(field);
- if (alias == null) {
- return orderBy(field);
- }
- return orderBy(alias);
- }
-
- public Query<T> orderBy(Object expr) {
- from.getAliasDefinition().checkMultipleEnums(expr);
- OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
- addOrderBy(e);
- return this;
- }
-
- /**
- * Order by a number of columns.
- *
- * @param expressions
- * the columns
- * @return the query
- */
-
- public Query<T> orderBy(Object... expressions) {
- for (Object expr : expressions) {
- from.getAliasDefinition().checkMultipleEnums(expr);
- OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
- addOrderBy(e);
- }
- return this;
- }
-
- public Query<T> orderByDesc(byte field) {
- return orderByDescPrimitive(field);
- }
-
- public Query<T> orderByDesc(short field) {
- return orderByDescPrimitive(field);
- }
-
- public Query<T> orderByDesc(int field) {
- return orderByDescPrimitive(field);
- }
-
- public Query<T> orderByDesc(long field) {
- return orderByDescPrimitive(field);
- }
-
- public Query<T> orderByDesc(float field) {
- return orderByDescPrimitive(field);
- }
-
- public Query<T> orderByDesc(double field) {
- return orderByDescPrimitive(field);
- }
-
- Query<T> orderByDescPrimitive(Object field) {
- Object alias = getPrimitiveAliasByValue(field);
- if (alias == null) {
- return orderByDesc(field);
- }
- return orderByDesc(alias);
- }
-
- public Query<T> orderByDesc(Object expr) {
- OrderExpression<T> e = new OrderExpression<T>(this, expr, true, false, false);
- addOrderBy(e);
- return this;
- }
-
- public Query<T> groupBy(boolean field) {
- from.getAliasDefinition().checkMultipleBooleans();
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(byte field) {
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(short field) {
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(int field) {
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(long field) {
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(float field) {
- return groupByPrimitive(field);
- }
-
- public Query<T> groupBy(double field) {
- return groupByPrimitive(field);
- }
-
- Query<T> groupByPrimitive(Object field) {
- Object alias = getPrimitiveAliasByValue(field);
- if (alias == null) {
- return groupBy(field);
- }
- return groupBy(alias);
- }
-
- public Query<T> groupBy(Object expr) {
- from.getAliasDefinition().checkMultipleEnums(expr);
- groupByExpressions.add(expr);
- return this;
- }
-
- public Query<T> 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<T> 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<Object> 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<T> 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<? extends DataTypeAdapter<?>> 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<T> 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<T> 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<T> 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 <A> QueryJoin<T> innerJoin(A alias) {
+ private Db db;
+ private SelectTable<T> from;
+ private ArrayList<Token> conditions = Utils.newArrayList();
+ private ArrayList<UpdateColumn> updateColumnDeclarations = Utils.newArrayList();
+ private int conditionDepth = 0;
+ private ArrayList<SelectTable<T>> joins = Utils.newArrayList();
+ private final IdentityHashMap<Object, SelectColumn<T>> aliasMap = Utils.newIdentityHashMap();
+ private ArrayList<OrderExpression<T>> orderByList = Utils.newArrayList();
+ private ArrayList<Object> 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 <T> Query<T> from(Db db, T alias) {
+ Query<T> query = new Query<T>(db);
+ TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
+ query.from = new SelectTable<T>(db, query, alias, false);
+ def.initSelectObject(query.from, alias, query.aliasMap, false);
+ return query;
+ }
+
+ @SuppressWarnings("unchecked")
+ static <T> Query<T> rebuild(Db db, T alias) {
+ Query<T> query = new Query<T>(db);
+ TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
+ query.from = new SelectTable<T>(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<T> select() {
+ return select(false);
+ }
+
+ public T selectFirst() {
+ List<T> list = limit(1).select(false);
+ return list.isEmpty() ? null : list.get(0);
+ }
+
+ public List<T> selectDistinct() {
+ return select(true);
+ }
+
+ public <X, Z> X selectFirst(Z x) {
+ List<X> list = limit(1).select(x);
+ return list.isEmpty() ? null : list.get(0);
+ }
+
+ public <X> void createView(Class<X> viewClass) {
+ TableDefinition<X> 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 <X> void replaceView(Class<X> 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 <K> 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();
+ }
+
+ <Z> String toSubQuery(Z z) {
+ SQLStatement stat = getSelectStatement(false);
+ SelectColumn<T> col = aliasMap.get(z);
+ String columnName = col.getFieldDefinition().columnName;
+ stat.appendColumn(columnName);
+ appendFromWhere(stat);
+ return stat.toSQL();
+ }
+
+ private List<T> select(boolean distinct) {
+ List<T> result = Utils.newArrayList();
+ TableDefinition<T> 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 <A> Query<T> setNull(A field) {
+ return set(field).to(null);
+ }
+
+ public <A> UpdateColumnSet<T, A> set(A field) {
+ from.getAliasDefinition().checkMultipleEnums(field);
+ return new UpdateColumnSet<T, A>(this, field);
+ }
+
+ public UpdateColumnSet<T, Boolean> set(boolean field) {
+ from.getAliasDefinition().checkMultipleBooleans();
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Byte> set(byte field) {
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Short> set(short field) {
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Integer> set(int field) {
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Long> set(long field) {
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Float> set(float field) {
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Double> set(double field) {
+ return setPrimitive(field);
+ }
+
+ private <A> UpdateColumnSet<T, A> 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 <A> UpdateColumnIncrement<T, A> increment(A field) {
+ return new UpdateColumnIncrement<T, A>(this, field);
+ }
+
+ public UpdateColumnIncrement<T, Byte> increment(byte field) {
+ return incrementPrimitive(field);
+ }
+
+ public UpdateColumnIncrement<T, Short> increment(short field) {
+ return incrementPrimitive(field);
+ }
+
+ public UpdateColumnIncrement<T, Integer> increment(int field) {
+ return incrementPrimitive(field);
+ }
+
+ public UpdateColumnIncrement<T, Long> increment(long field) {
+ return incrementPrimitive(field);
+ }
+
+ public UpdateColumnIncrement<T, Float> increment(float field) {
+ return incrementPrimitive(field);
+ }
+
+ public UpdateColumnIncrement<T, Double> increment(double field) {
+ return incrementPrimitive(field);
+ }
+
+ private <A> UpdateColumnIncrement<T, A> 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 <X, Z> List<X> selectDistinct(Z x) {
+ return select(x, true);
+ }
+
+ public <X, Z> List<X> select(Z x) {
+ return select(x, false);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <X, Z> List<X> 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<T> col = getColumnByReference(x);
+ if (col == null) {
+ col = getColumnByReference(getPrimitiveAliasByValue(x));
+ }
+ if (col != null) {
+ return (List<X>) 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<X>) clazz, (X) x, distinct);
+ }
+
+ private <X> List<X> select(Class<X> clazz, X x, boolean distinct) {
+ List<X> result = Utils.newArrayList();
+ TableDefinition<X> 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 <X> List<X> selectFunction(X x, boolean distinct) {
+ SQLStatement stat = getSelectStatement(distinct);
+ appendSQL(stat, null, x);
+ appendFromWhere(stat);
+ ResultSet rs = stat.executeQuery();
+ List<X> 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 <X> List<X> selectColumn(SelectColumn<T> col, Class<X> clazz, boolean distinct) {
+ SQLStatement stat = getSelectStatement(distinct);
+ col.appendSQL(stat);
+ appendFromWhere(stat);
+ ResultSet rs = stat.executeQuery();
+ List<X> result = Utils.newArrayList();
+ Class<? extends DataTypeAdapter<?>> 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<T, Boolean> 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<T, Byte> 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<T, Short> 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<T, Integer> 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<T, Long> 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<T, Float> 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<T, Double> where(double x) {
+ return wherePrimitive(x);
+ }
+
+ /**
+ * Begins a primitive field condition clause.
+ *
+ * @param value
+ * @return a query condition to continue building the condition
+ */
+ private <A> QueryCondition<T, A> 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 <A> QueryCondition<T, A> where(A x) {
+ from.getAliasDefinition().checkMultipleEnums(x);
+ return new QueryCondition<T, A>(this, x);
+ }
+
+ public <A> QueryWhere<T> where(Filter filter) {
+ HashMap<String, Object> fieldMap = Utils.newHashMap();
+ for (Field f : filter.getClass().getDeclaredFields()) {
+ f.setAccessible(true);
+ try {
+ Object obj = f.get(filter);
+ if (obj == from.getAlias()) {
+ List<TableDefinition.FieldDefinition> 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<T>(this);
+ }
+
+ public QueryWhere<T> where(String fragment, List<?> args) {
+ return this.where(fragment, args.toArray());
+ }
+
+ public QueryWhere<T> where(String fragment, Object... args) {
+ conditions.add(new RuntimeToken(fragment, args));
+ return new QueryWhere<T>(this);
+ }
+
+ public Query<T> where(And<T> conditions) {
+ whereTrue();
+ addConditionToken(conditions.where.query);
+ return this;
+ }
+
+ public Query<T> where(Or<T> conditions) {
+ whereFalse();
+ addConditionToken(conditions.where.query);
+ return this;
+ }
+
+ public QueryWhere<T> whereTrue() {
+ return whereTrue(true);
+ }
+
+ public QueryWhere<T> whereFalse() {
+ return whereTrue(false);
+ }
+
+ public QueryWhere<T> whereTrue(Boolean condition) {
+ Token token = new Function("", condition);
+ addConditionToken(token);
+ return new QueryWhere<T>(this);
+ }
+
+ /**
+ * Sets the Limit and Offset of a query.
+ *
+ * @return the query
+ */
+
+ public Query<T> limit(long limit) {
+ this.limit = limit;
+ return this;
+ }
+
+ public Query<T> offset(long offset) {
+ this.offset = offset;
+ return this;
+ }
+
+ public Query<T> orderBy(boolean field) {
+ from.getAliasDefinition().checkMultipleBooleans();
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(byte field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(short field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(int field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(long field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(float field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(double field) {
+ return orderByPrimitive(field);
+ }
+
+ Query<T> orderByPrimitive(Object field) {
+ Object alias = getPrimitiveAliasByValue(field);
+ if (alias == null) {
+ return orderBy(field);
+ }
+ return orderBy(alias);
+ }
+
+ public Query<T> orderBy(Object expr) {
+ from.getAliasDefinition().checkMultipleEnums(expr);
+ OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
+ addOrderBy(e);
+ return this;
+ }
+
+ /**
+ * Order by a number of columns.
+ *
+ * @param expressions the columns
+ * @return the query
+ */
+
+ public Query<T> orderBy(Object... expressions) {
+ for (Object expr : expressions) {
+ from.getAliasDefinition().checkMultipleEnums(expr);
+ OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
+ addOrderBy(e);
+ }
+ return this;
+ }
+
+ public Query<T> orderByDesc(byte field) {
+ return orderByDescPrimitive(field);
+ }
+
+ public Query<T> orderByDesc(short field) {
+ return orderByDescPrimitive(field);
+ }
+
+ public Query<T> orderByDesc(int field) {
+ return orderByDescPrimitive(field);
+ }
+
+ public Query<T> orderByDesc(long field) {
+ return orderByDescPrimitive(field);
+ }
+
+ public Query<T> orderByDesc(float field) {
+ return orderByDescPrimitive(field);
+ }
+
+ public Query<T> orderByDesc(double field) {
+ return orderByDescPrimitive(field);
+ }
+
+ Query<T> orderByDescPrimitive(Object field) {
+ Object alias = getPrimitiveAliasByValue(field);
+ if (alias == null) {
+ return orderByDesc(field);
+ }
+ return orderByDesc(alias);
+ }
+
+ public Query<T> orderByDesc(Object expr) {
+ OrderExpression<T> e = new OrderExpression<T>(this, expr, true, false, false);
+ addOrderBy(e);
+ return this;
+ }
+
+ public Query<T> groupBy(boolean field) {
+ from.getAliasDefinition().checkMultipleBooleans();
+ return groupByPrimitive(field);
+ }
+
+ public Query<T> groupBy(byte field) {
+ return groupByPrimitive(field);
+ }
+
+ public Query<T> groupBy(short field) {
+ return groupByPrimitive(field);
+ }
+
+ public Query<T> groupBy(int field) {
+ return groupByPrimitive(field);
+ }
+
+ public Query<T> groupBy(long field) {
+ return groupByPrimitive(field);
+ }
+
+ public Query<T> groupBy(float field) {
+ return groupByPrimitive(field);
+ }
+
+ public Query<T> groupBy(double field) {
+ return groupByPrimitive(field);
+ }
+
+ Query<T> groupByPrimitive(Object field) {
+ Object alias = getPrimitiveAliasByValue(field);
+ if (alias == null) {
+ return groupBy(field);
+ }
+ return groupBy(alias);
+ }
+
+ public Query<T> groupBy(Object expr) {
+ from.getAliasDefinition().checkMultipleEnums(expr);
+ groupByExpressions.add(expr);
+ return this;
+ }
+
+ public Query<T> 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<T> 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<Object> 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<T> 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<? extends DataTypeAdapter<?>> 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<T> 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<T> 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<T> 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 <A> QueryJoin<T> innerJoin(A alias) {
return join(alias, false);
- }
+ }
public <A> QueryJoin<T> leftJoin(A alias) {
return join(alias, true);
}
- @SuppressWarnings({ "unchecked", "rawtypes" })
+ @SuppressWarnings({"unchecked", "rawtypes"})
private <A> QueryJoin<T> join(A alias, boolean outerJoin) {
TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
SelectTable<T> join = new SelectTable(db, this, alias, outerJoin);
@@ -1052,63 +1030,63 @@ public class Query<T> {
return new QueryJoin(this, join);
}
- Db getDb() {
- return db;
- }
-
- SelectTable<T> 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<T> getColumnByReference(Object obj) {
- SelectColumn<T> 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> A getPrimitiveAliasByValue(A obj) {
- for (Object alias : aliasMap.keySet()) {
- if (alias.equals(obj)) {
- SelectColumn<T> match = aliasMap.get(alias);
- if (match.getFieldDefinition().isPrimitive) {
- return (A) alias;
- }
- }
- }
- return null;
- }
-
- void addOrderBy(OrderExpression<T> expr) {
- orderByList.add(expr);
- }
+ Db getDb() {
+ return db;
+ }
+
+ SelectTable<T> 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<T> getColumnByReference(Object obj) {
+ SelectColumn<T> 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> A getPrimitiveAliasByValue(A obj) {
+ for (Object alias : aliasMap.keySet()) {
+ if (alias.equals(obj)) {
+ SelectColumn<T> match = aliasMap.get(alias);
+ if (match.getFieldDefinition().isPrimitive) {
+ return (A) alias;
+ }
+ }
+ }
+ return null;
+ }
+
+ void addOrderBy(OrderExpression<T> 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 <T>
- * the return type of the query
- * @param <A>
- * the incomplete condition data type
+ *
+ * @param <T> the return type of the query
+ * @param <A> the incomplete condition data type
*/
public class QueryBetween<T, A> {
- private Query<T> query;
- private A x;
- private A y;
+ private Query<T> 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<T> 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<T> 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<T> and(A z) {
- query.addConditionToken(new Condition<A>(x, y, z, CompareType.BETWEEN));
- return new QueryWhere<T>(query);
- }
+ /**
+ * Set the upper bound of the between condition.
+ *
+ * @param z the upper bound of the between condition
+ * @return the query
+ */
+ public QueryWhere<T> and(A z) {
+ query.addConditionToken(new Condition<A>(x, y, z, CompareType.BETWEEN));
+ return new QueryWhere<T>(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 <T>
- * the return type of the query
- * @param <A>
- * the incomplete condition data type
+ *
+ * @param <T> the return type of the query
+ * @param <A> the incomplete condition data type
*/
public class QueryCondition<T, A> {
- private Query<T> query;
- private A x;
-
- QueryCondition(Query<T> query, A x) {
- this.query = query;
- this.x = x;
- }
-
- public <Q, Z> QueryWhere<T> in(SubQuery<Q, Z> q) {
- query.addConditionToken(new SubQueryCondition<A, Q, Z>(x, q));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> oneOf(A... a) {
- return oneOf(Utils.newArrayIterable(a));
- }
-
- public QueryWhere<T> oneOf(Iterable<A> i) {
- query.addConditionToken(new Condition<A>(x, i, CompareType.IN));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> noneOf(A... a) {
- return noneOf(Utils.newArrayIterable(a));
- }
-
- public QueryWhere<T> noneOf(Iterable<A> i) {
- query.addConditionToken(new Condition<A>(x, i, CompareType.NOT_IN));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> is(A y) {
- query.addConditionToken(new Condition<A>(x, y, CompareType.EQUAL));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> isNot(A y) {
- query.addConditionToken(new Condition<A>(x, y, CompareType.NOT_EQUAL));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> isNull() {
- query.addConditionToken(new Condition<A>(x, CompareType.IS_NULL));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> isNotNull() {
- query.addConditionToken(new Condition<A>(x, CompareType.IS_NOT_NULL));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> exceeds(A y) {
- query.addConditionToken(new Condition<A>(x, y, CompareType.EXCEEDS));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> atLeast(A y) {
- query.addConditionToken(new Condition<A>(x, y, CompareType.AT_LEAST));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> lessThan(A y) {
- query.addConditionToken(new Condition<A>(x, y, CompareType.LESS_THAN));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> atMost(A y) {
- query.addConditionToken(new Condition<A>(x, y, CompareType.AT_MOST));
- return new QueryWhere<T>(query);
- }
-
- public QueryBetween<T, A> between(A y) {
- return new QueryBetween<T, A>(query, x, y);
- }
-
- public QueryWhere<T> like(A pattern) {
- query.addConditionToken(new Condition<A>(x, pattern, CompareType.LIKE));
- return new QueryWhere<T>(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<T> isParameter() {
- query.addConditionToken(new RuntimeParameter<A>(x, CompareType.EQUAL));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> isNotParameter() {
- query.addConditionToken(new RuntimeParameter<A>(x, CompareType.NOT_EQUAL));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> exceedsParameter() {
- query.addConditionToken(new RuntimeParameter<A>(x, CompareType.EXCEEDS));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> lessThanParameter() {
- query.addConditionToken(new RuntimeParameter<A>(x, CompareType.LESS_THAN));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> atMostParameter() {
- query.addConditionToken(new RuntimeParameter<A>(x, CompareType.AT_MOST));
- return new QueryWhere<T>(query);
- }
-
- public QueryWhere<T> likeParameter() {
- query.addConditionToken(new RuntimeParameter<A>(x, CompareType.LIKE));
- return new QueryWhere<T>(query);
- }
+ private Query<T> query;
+ private A x;
+
+ QueryCondition(Query<T> query, A x) {
+ this.query = query;
+ this.x = x;
+ }
+
+ public <Q, Z> QueryWhere<T> in(SubQuery<Q, Z> q) {
+ query.addConditionToken(new SubQueryCondition<A, Q, Z>(x, q));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> oneOf(A... a) {
+ return oneOf(Utils.newArrayIterable(a));
+ }
+
+ public QueryWhere<T> oneOf(Iterable<A> i) {
+ query.addConditionToken(new Condition<A>(x, i, CompareType.IN));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> noneOf(A... a) {
+ return noneOf(Utils.newArrayIterable(a));
+ }
+
+ public QueryWhere<T> noneOf(Iterable<A> i) {
+ query.addConditionToken(new Condition<A>(x, i, CompareType.NOT_IN));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> is(A y) {
+ query.addConditionToken(new Condition<A>(x, y, CompareType.EQUAL));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> isNot(A y) {
+ query.addConditionToken(new Condition<A>(x, y, CompareType.NOT_EQUAL));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> isNull() {
+ query.addConditionToken(new Condition<A>(x, CompareType.IS_NULL));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> isNotNull() {
+ query.addConditionToken(new Condition<A>(x, CompareType.IS_NOT_NULL));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> exceeds(A y) {
+ query.addConditionToken(new Condition<A>(x, y, CompareType.EXCEEDS));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> atLeast(A y) {
+ query.addConditionToken(new Condition<A>(x, y, CompareType.AT_LEAST));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> lessThan(A y) {
+ query.addConditionToken(new Condition<A>(x, y, CompareType.LESS_THAN));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> atMost(A y) {
+ query.addConditionToken(new Condition<A>(x, y, CompareType.AT_MOST));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryBetween<T, A> between(A y) {
+ return new QueryBetween<T, A>(query, x, y);
+ }
+
+ public QueryWhere<T> like(A pattern) {
+ query.addConditionToken(new Condition<A>(x, pattern, CompareType.LIKE));
+ return new QueryWhere<T>(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<T> isParameter() {
+ query.addConditionToken(new RuntimeParameter<A>(x, CompareType.EQUAL));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> isNotParameter() {
+ query.addConditionToken(new RuntimeParameter<A>(x, CompareType.NOT_EQUAL));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> exceedsParameter() {
+ query.addConditionToken(new RuntimeParameter<A>(x, CompareType.EXCEEDS));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> lessThanParameter() {
+ query.addConditionToken(new RuntimeParameter<A>(x, CompareType.LESS_THAN));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> atMostParameter() {
+ query.addConditionToken(new RuntimeParameter<A>(x, CompareType.AT_MOST));
+ return new QueryWhere<T>(query);
+ }
+
+ public QueryWhere<T> likeParameter() {
+ query.addConditionToken(new RuntimeParameter<A>(x, CompareType.LIKE));
+ return new QueryWhere<T>(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<T> {
- private Query<T> query;
- private SelectTable<T> join;
+ private Query<T> query;
+ private SelectTable<T> join;
- QueryJoin(Query<T> query, SelectTable<T> join) {
- this.query = query;
- this.join = join;
- }
+ QueryJoin(Query<T> query, SelectTable<T> join) {
+ this.query = query;
+ this.join = join;
+ }
- public QueryJoinCondition<T, Boolean> on(boolean x) {
- query.getFrom().getAliasDefinition().checkMultipleBooleans();
- return addPrimitive(x);
- }
+ public QueryJoinCondition<T, Boolean> on(boolean x) {
+ query.getFrom().getAliasDefinition().checkMultipleBooleans();
+ return addPrimitive(x);
+ }
- public QueryJoinCondition<T, Byte> on(byte x) {
- return addPrimitive(x);
- }
+ public QueryJoinCondition<T, Byte> on(byte x) {
+ return addPrimitive(x);
+ }
- public QueryJoinCondition<T, Short> on(short x) {
- return addPrimitive(x);
- }
+ public QueryJoinCondition<T, Short> on(short x) {
+ return addPrimitive(x);
+ }
- public QueryJoinCondition<T, Integer> on(int x) {
- return addPrimitive(x);
- }
+ public QueryJoinCondition<T, Integer> on(int x) {
+ return addPrimitive(x);
+ }
- public QueryJoinCondition<T, Long> on(long x) {
- return addPrimitive(x);
- }
+ public QueryJoinCondition<T, Long> on(long x) {
+ return addPrimitive(x);
+ }
- public QueryJoinCondition<T, Float> on(float x) {
- return addPrimitive(x);
- }
+ public QueryJoinCondition<T, Float> on(float x) {
+ return addPrimitive(x);
+ }
- public QueryJoinCondition<T, Double> on(double x) {
- return addPrimitive(x);
- }
+ public QueryJoinCondition<T, Double> on(double x) {
+ return addPrimitive(x);
+ }
- private <A> QueryJoinCondition<T, A> addPrimitive(A x) {
- A alias = query.getPrimitiveAliasByValue(x);
- if (alias == null) {
- // this will result in an unmapped field exception
- return new QueryJoinCondition<T, A>(query, join, x);
- }
- return new QueryJoinCondition<T, A>(query, join, alias);
- }
+ private <A> QueryJoinCondition<T, A> addPrimitive(A x) {
+ A alias = query.getPrimitiveAliasByValue(x);
+ if (alias == null) {
+ // this will result in an unmapped field exception
+ return new QueryJoinCondition<T, A>(query, join, x);
+ }
+ return new QueryJoinCondition<T, A>(query, join, alias);
+ }
- public <A> QueryJoinCondition<T, A> on(A x) {
- return new QueryJoinCondition<T, A>(query, join, x);
- }
+ public <A> QueryJoinCondition<T, A> on(A x) {
+ return new QueryJoinCondition<T, A>(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 <A>
- * the incomplete condition data type
+ *
+ * @param <A> the incomplete condition data type
*/
public class QueryJoinCondition<T, A> {
- private Query<T> query;
- private SelectTable<T> join;
- private A x;
+ private Query<T> query;
+ private SelectTable<T> join;
+ private A x;
+
+ QueryJoinCondition(Query<T> query, SelectTable<T> join, A x) {
+ this.query = query;
+ this.join = join;
+ this.x = x;
+ }
- QueryJoinCondition(Query<T> query, SelectTable<T> join, A x) {
- this.query = query;
- this.join = join;
- this.x = x;
- }
+ public Query<T> is(boolean y) {
+ return addPrimitive(y);
+ }
- public Query<T> is(boolean y) {
- return addPrimitive(y);
- }
+ public Query<T> is(byte y) {
+ return addPrimitive(y);
+ }
- public Query<T> is(byte y) {
- return addPrimitive(y);
- }
+ public Query<T> is(short y) {
+ return addPrimitive(y);
+ }
- public Query<T> is(short y) {
- return addPrimitive(y);
- }
+ public Query<T> is(int y) {
+ return addPrimitive(y);
+ }
- public Query<T> is(int y) {
- return addPrimitive(y);
- }
-
- public Query<T> is(long y) {
- return addPrimitive(y);
- }
+ public Query<T> is(long y) {
+ return addPrimitive(y);
+ }
- public Query<T> is(float y) {
- return addPrimitive(y);
- }
+ public Query<T> is(float y) {
+ return addPrimitive(y);
+ }
- public Query<T> is(double y) {
- return addPrimitive(y);
- }
+ public Query<T> is(double y) {
+ return addPrimitive(y);
+ }
- @SuppressWarnings("unchecked")
- private Query<T> addPrimitive(Object o) {
- A alias = query.getPrimitiveAliasByValue((A) o);
- if (alias == null) {
- join.addConditionToken(new Condition<A>(x, (A) o, CompareType.EQUAL));
- } else {
- join.addConditionToken(new Condition<A>(x, alias, CompareType.EQUAL));
- }
- return query;
- }
+ @SuppressWarnings("unchecked")
+ private Query<T> addPrimitive(Object o) {
+ A alias = query.getPrimitiveAliasByValue((A) o);
+ if (alias == null) {
+ join.addConditionToken(new Condition<A>(x, (A) o, CompareType.EQUAL));
+ } else {
+ join.addConditionToken(new Condition<A>(x, alias, CompareType.EQUAL));
+ }
+ return query;
+ }
- public Query<T> is(A y) {
- join.addConditionToken(new Condition<A>(x, y, CompareType.EQUAL));
- return query;
- }
+ public Query<T> is(A y) {
+ join.addConditionToken(new Condition<A>(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 <T>
- * the return type
+ * @param <T> the return type
*/
public class QueryWhere<T> {
- Query<T> query;
-
- QueryWhere(Query<T> 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<T, Boolean> 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<T, Byte> 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<T, Short> 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<T, Integer> 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<T, Long> 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<T, Float> 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<T, Double> and(double x) {
- return addPrimitive(ConditionAndOr.AND, x);
- }
-
- private <A> QueryCondition<T, A> 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<T, A>(query, x);
- }
- return new QueryCondition<T, A>(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 <A> QueryCondition<T, A> and(A x) {
- query.getFrom().getAliasDefinition().checkMultipleEnums(x);
- query.addConditionToken(ConditionAndOr.AND);
- return new QueryCondition<T, A>(query, x);
- }
-
- public QueryWhere<T> and(And<T> conditions) {
- andOpen();
- query.addConditionToken(conditions.where.query);
- return close();
- }
-
- public QueryWhere<T> and(Or<T> conditions) {
- andOpen();
- query.addConditionToken(conditions.where.query);
- return close();
- }
-
- public QueryWhere<T> 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<T, Boolean> 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<T, Byte> 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<T, Short> 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<T, Integer> 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<T, Long> 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<T, Float> 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<T, Double> 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 <A> QueryCondition<T, A> or(A x) {
- query.getFrom().getAliasDefinition().checkMultipleEnums(x);
- query.addConditionToken(ConditionAndOr.OR);
- return new QueryCondition<T, A>(query, x);
- }
-
- public QueryWhere<T> or(And<T> conditions) {
- orOpen();
- query.addConditionToken(conditions.where.query);
- return close();
- }
-
- public QueryWhere<T> or(Or<T> conditions) {
- orOpen();
- query.addConditionToken(conditions.where.query);
- return close();
- }
-
- public QueryWhere<T> orOpen() {
- return open(ConditionAndOr.OR);
- }
-
- private QueryWhere<T> open(ConditionAndOr andOr) {
- query.addConditionToken(andOr);
- query.addConditionToken(ConditionOpenClose.OPEN);
- return this;
- }
-
- public QueryWhere<T> close() {
- query.addConditionToken(ConditionOpenClose.CLOSE);
- return this;
- }
-
- public QueryWhere<T> limit(long limit) {
- query.limit(limit);
- return this;
- }
-
- public QueryWhere<T> 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 <K> String toSQL(boolean distinct, K k) {
- return query.toSQL(distinct, k);
- }
-
- public <Z> SubQuery<T, Z> subQuery(Z x) {
- return new SubQuery<T, Z>(query, x);
- }
-
- public SubQuery<T, Boolean> subQuery(boolean x) {
- return subQuery(query.getPrimitiveAliasByValue(x));
- }
-
- public SubQuery<T, Byte> subQuery(byte x) {
- return subQuery(query.getPrimitiveAliasByValue(x));
- }
-
- public SubQuery<T, Short> subQuery(short x) {
- return subQuery(query.getPrimitiveAliasByValue(x));
- }
-
- public SubQuery<T, Integer> subQuery(int x) {
- return subQuery(query.getPrimitiveAliasByValue(x));
- }
-
- public SubQuery<T, Long> subQuery(long x) {
- return subQuery(query.getPrimitiveAliasByValue(x));
- }
-
- public SubQuery<T, Float> subQuery(float x) {
- return subQuery(query.getPrimitiveAliasByValue(x));
- }
-
- public SubQuery<T, Double> subQuery(double x) {
- return subQuery(query.getPrimitiveAliasByValue(x));
- }
-
- public <X, Z> List<X> select(Z x) {
- return query.select(x);
- }
-
- public <X, Z> List<X> selectDistinct(Z x) {
- return query.selectDistinct(x);
- }
-
- public <X, Z> X selectFirst(Z x) {
- List<X> list = query.select(x);
- return list.isEmpty() ? null : list.get(0);
- }
-
- public List<T> select() {
- return query.select();
- }
-
- public T selectFirst() {
- List<T> list = select();
- return list.isEmpty() ? null : list.get(0);
- }
-
- public List<T> 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<T> 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<T> orderBy(byte field) {
- return orderByPrimitive(field);
- }
-
- /**
- * Order by primitive short field
- *
- * @param field
- * a primitive short field
- * @return the query
- */
- public QueryWhere<T> orderBy(short field) {
- return orderByPrimitive(field);
- }
-
- public QueryWhere<T> orderBy(int field) {
- return orderByPrimitive(field);
- }
-
- /**
- * Order by primitive long field
- *
- * @param field
- * a primitive long field
- * @return the query
- */
- public QueryWhere<T> orderBy(long field) {
- return orderByPrimitive(field);
- }
-
- /**
- * Order by primitive float field
- *
- * @param field
- * a primitive float field
- * @return the query
- */
- public QueryWhere<T> orderBy(float field) {
- return orderByPrimitive(field);
- }
-
- /**
- * Order by primitive double field
- *
- * @param field
- * a primitive double field
- * @return the query
- */
- public QueryWhere<T> orderBy(double field) {
- return orderByPrimitive(field);
- }
-
- private QueryWhere<T> orderByPrimitive(Object field) {
- query.orderByPrimitive(field);
- return this;
- }
-
- public QueryWhere<T> 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<T> orderBy(Object... expressions) {
- query.orderBy(expressions);
- return this;
- }
-
- public QueryWhere<T> orderByNullsFirst(Object expr) {
- query.getFrom().getAliasDefinition().checkMultipleEnums(expr);
- OrderExpression<T> e = new OrderExpression<T>(query, expr, false, true, false);
- query.addOrderBy(e);
- return this;
- }
-
- public QueryWhere<T> orderByNullsLast(Object expr) {
- query.getFrom().getAliasDefinition().checkMultipleEnums(expr);
- OrderExpression<T> e = new OrderExpression<T>(query, expr, false, false, true);
- query.addOrderBy(e);
- return this;
- }
-
- public QueryWhere<T> orderByDesc(Object expr) {
- query.getFrom().getAliasDefinition().checkMultipleEnums(expr);
- OrderExpression<T> e = new OrderExpression<T>(query, expr, true, false, false);
- query.addOrderBy(e);
- return this;
- }
-
- public QueryWhere<T> orderByDescNullsFirst(Object expr) {
- query.getFrom().getAliasDefinition().checkMultipleEnums(expr);
- OrderExpression<T> e = new OrderExpression<T>(query, expr, true, true, false);
- query.addOrderBy(e);
- return this;
- }
-
- public QueryWhere<T> orderByDescNullsLast(Object expr) {
- query.getFrom().getAliasDefinition().checkMultipleEnums(expr);
- OrderExpression<T> e = new OrderExpression<T>(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<T> 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<T> groupBy(byte field) {
- return groupByPrimitive(field);
- }
-
- /**
- * Group by primitive short field
- *
- * @param field
- * a primitive short field
- * @return the query
- */
- public QueryWhere<T> groupBy(short field) {
- return groupByPrimitive(field);
- }
-
- public QueryWhere<T> groupBy(int field) {
- return groupByPrimitive(field);
- }
-
- /**
- * Group by primitive long field
- *
- * @param field
- * a primitive long field
- * @return the query
- */
- public QueryWhere<T> groupBy(long field) {
- return groupByPrimitive(field);
- }
-
- /**
- * Group by primitive float field
- *
- * @param field
- * a primitive float field
- * @return the query
- */
- public QueryWhere<T> groupBy(float field) {
- return groupByPrimitive(field);
- }
-
- /**
- * Group by primitive double field
- *
- * @param field
- * a primitive double field
- * @return the query
- */
- public QueryWhere<T> groupBy(double field) {
- return groupByPrimitive(field);
- }
-
- private QueryWhere<T> groupByPrimitive(Object field) {
- query.groupByPrimitive(field);
- return this;
- }
-
- public QueryWhere<T> 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<T> 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<T> query;
+
+ QueryWhere(Query<T> 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<T, Boolean> 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<T, Byte> 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<T, Short> 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<T, Integer> 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<T, Long> 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<T, Float> 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<T, Double> and(double x) {
+ return addPrimitive(ConditionAndOr.AND, x);
+ }
+
+ private <A> QueryCondition<T, A> 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<T, A>(query, x);
+ }
+ return new QueryCondition<T, A>(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 <A> QueryCondition<T, A> and(A x) {
+ query.getFrom().getAliasDefinition().checkMultipleEnums(x);
+ query.addConditionToken(ConditionAndOr.AND);
+ return new QueryCondition<T, A>(query, x);
+ }
+
+ public QueryWhere<T> and(And<T> conditions) {
+ andOpen();
+ query.addConditionToken(conditions.where.query);
+ return close();
+ }
+
+ public QueryWhere<T> and(Or<T> conditions) {
+ andOpen();
+ query.addConditionToken(conditions.where.query);
+ return close();
+ }
+
+ public QueryWhere<T> 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<T, Boolean> 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<T, Byte> 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<T, Short> 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<T, Integer> 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<T, Long> 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<T, Float> 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<T, Double> 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 <A> QueryCondition<T, A> or(A x) {
+ query.getFrom().getAliasDefinition().checkMultipleEnums(x);
+ query.addConditionToken(ConditionAndOr.OR);
+ return new QueryCondition<T, A>(query, x);
+ }
+
+ public QueryWhere<T> or(And<T> conditions) {
+ orOpen();
+ query.addConditionToken(conditions.where.query);
+ return close();
+ }
+
+ public QueryWhere<T> or(Or<T> conditions) {
+ orOpen();
+ query.addConditionToken(conditions.where.query);
+ return close();
+ }
+
+ public QueryWhere<T> orOpen() {
+ return open(ConditionAndOr.OR);
+ }
+
+ private QueryWhere<T> open(ConditionAndOr andOr) {
+ query.addConditionToken(andOr);
+ query.addConditionToken(ConditionOpenClose.OPEN);
+ return this;
+ }
+
+ public QueryWhere<T> close() {
+ query.addConditionToken(ConditionOpenClose.CLOSE);
+ return this;
+ }
+
+ public QueryWhere<T> limit(long limit) {
+ query.limit(limit);
+ return this;
+ }
+
+ public QueryWhere<T> 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 <K> String toSQL(boolean distinct, K k) {
+ return query.toSQL(distinct, k);
+ }
+
+ public <Z> SubQuery<T, Z> subQuery(Z x) {
+ return new SubQuery<T, Z>(query, x);
+ }
+
+ public SubQuery<T, Boolean> subQuery(boolean x) {
+ return subQuery(query.getPrimitiveAliasByValue(x));
+ }
+
+ public SubQuery<T, Byte> subQuery(byte x) {
+ return subQuery(query.getPrimitiveAliasByValue(x));
+ }
+
+ public SubQuery<T, Short> subQuery(short x) {
+ return subQuery(query.getPrimitiveAliasByValue(x));
+ }
+
+ public SubQuery<T, Integer> subQuery(int x) {
+ return subQuery(query.getPrimitiveAliasByValue(x));
+ }
+
+ public SubQuery<T, Long> subQuery(long x) {
+ return subQuery(query.getPrimitiveAliasByValue(x));
+ }
+
+ public SubQuery<T, Float> subQuery(float x) {
+ return subQuery(query.getPrimitiveAliasByValue(x));
+ }
+
+ public SubQuery<T, Double> subQuery(double x) {
+ return subQuery(query.getPrimitiveAliasByValue(x));
+ }
+
+ public <X, Z> List<X> select(Z x) {
+ return query.select(x);
+ }
+
+ public <X, Z> List<X> selectDistinct(Z x) {
+ return query.selectDistinct(x);
+ }
+
+ public <X, Z> X selectFirst(Z x) {
+ List<X> list = query.select(x);
+ return list.isEmpty() ? null : list.get(0);
+ }
+
+ public List<T> select() {
+ return query.select();
+ }
+
+ public T selectFirst() {
+ List<T> list = select();
+ return list.isEmpty() ? null : list.get(0);
+ }
+
+ public List<T> 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<T> 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<T> orderBy(byte field) {
+ return orderByPrimitive(field);
+ }
+
+ /**
+ * Order by primitive short field
+ *
+ * @param field a primitive short field
+ * @return the query
+ */
+ public QueryWhere<T> orderBy(short field) {
+ return orderByPrimitive(field);
+ }
+
+ public QueryWhere<T> orderBy(int field) {
+ return orderByPrimitive(field);
+ }
+
+ /**
+ * Order by primitive long field
+ *
+ * @param field a primitive long field
+ * @return the query
+ */
+ public QueryWhere<T> orderBy(long field) {
+ return orderByPrimitive(field);
+ }
+
+ /**
+ * Order by primitive float field
+ *
+ * @param field a primitive float field
+ * @return the query
+ */
+ public QueryWhere<T> orderBy(float field) {
+ return orderByPrimitive(field);
+ }
+
+ /**
+ * Order by primitive double field
+ *
+ * @param field a primitive double field
+ * @return the query
+ */
+ public QueryWhere<T> orderBy(double field) {
+ return orderByPrimitive(field);
+ }
+
+ private QueryWhere<T> orderByPrimitive(Object field) {
+ query.orderByPrimitive(field);
+ return this;
+ }
+
+ public QueryWhere<T> 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<T> orderBy(Object... expressions) {
+ query.orderBy(expressions);
+ return this;
+ }
+
+ public QueryWhere<T> orderByNullsFirst(Object expr) {
+ query.getFrom().getAliasDefinition().checkMultipleEnums(expr);
+ OrderExpression<T> e = new OrderExpression<T>(query, expr, false, true, false);
+ query.addOrderBy(e);
+ return this;
+ }
+
+ public QueryWhere<T> orderByNullsLast(Object expr) {
+ query.getFrom().getAliasDefinition().checkMultipleEnums(expr);
+ OrderExpression<T> e = new OrderExpression<T>(query, expr, false, false, true);
+ query.addOrderBy(e);
+ return this;
+ }
+
+ public QueryWhere<T> orderByDesc(Object expr) {
+ query.getFrom().getAliasDefinition().checkMultipleEnums(expr);
+ OrderExpression<T> e = new OrderExpression<T>(query, expr, true, false, false);
+ query.addOrderBy(e);
+ return this;
+ }
+
+ public QueryWhere<T> orderByDescNullsFirst(Object expr) {
+ query.getFrom().getAliasDefinition().checkMultipleEnums(expr);
+ OrderExpression<T> e = new OrderExpression<T>(query, expr, true, true, false);
+ query.addOrderBy(e);
+ return this;
+ }
+
+ public QueryWhere<T> orderByDescNullsLast(Object expr) {
+ query.getFrom().getAliasDefinition().checkMultipleEnums(expr);
+ OrderExpression<T> e = new OrderExpression<T>(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<T> 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<T> groupBy(byte field) {
+ return groupByPrimitive(field);
+ }
+
+ /**
+ * Group by primitive short field
+ *
+ * @param field a primitive short field
+ * @return the query
+ */
+ public QueryWhere<T> groupBy(short field) {
+ return groupByPrimitive(field);
+ }
+
+ public QueryWhere<T> groupBy(int field) {
+ return groupByPrimitive(field);
+ }
+
+ /**
+ * Group by primitive long field
+ *
+ * @param field a primitive long field
+ * @return the query
+ */
+ public QueryWhere<T> groupBy(long field) {
+ return groupByPrimitive(field);
+ }
+
+ /**
+ * Group by primitive float field
+ *
+ * @param field a primitive float field
+ * @return the query
+ */
+ public QueryWhere<T> groupBy(float field) {
+ return groupByPrimitive(field);
+ }
+
+ /**
+ * Group by primitive double field
+ *
+ * @param field a primitive double field
+ * @return the query
+ */
+ public QueryWhere<T> groupBy(double field) {
+ return groupByPrimitive(field);
+ }
+
+ private QueryWhere<T> groupByPrimitive(Object field) {
+ query.groupByPrimitive(field);
+ return this;
+ }
+
+ public QueryWhere<T> 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<T> 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 <A>
- * the operand type
+ *
+ * @param <A> the operand type
*/
class RuntimeParameter<A> 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 <T> void appendSQL(SQLStatement stat, Query<T> query) {
- query.appendSQL(stat, null, x);
- stat.appendSQL(" ");
- stat.appendSQL(compareType.getString());
- if (compareType.hasRightExpression()) {
- stat.appendSQL(" ");
- query.appendSQL(stat, x, PARAMETER);
- }
- }
+ public <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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<? extends DataTypeAdapter<?>> typeAdapter);
-
- /**
- * Serialize the Java object into a type or format that the database will accept.
- *
- * @param value
- * @param typeAdapter
- * @return the serialized object
- */
- <T> Object serialize(T value, Class<? extends DataTypeAdapter<?>> 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<? extends DataTypeAdapter<?>> 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
- */
- <T> void prepareCreateTable(SQLStatement stat, TableDefinition<T> def);
-
- /**
- * Get the DROP TABLE statement.
- *
- * @param stat
- * @param def
- */
- <T> void prepareDropTable(SQLStatement stat, TableDefinition<T> def);
-
-
- /**
- * Get the CREATE VIEW statement.
- *
- * @param stat
- * return the SQL statement
- * @param def
- * table definition
- */
- <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> def);
-
- /**
- * Get the CREATE VIEW statement.
- *
- * @param stat
- * return the SQL statement
- * @param def
- * table definition
- * @param fromWhere
- */
- <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> def, String fromWhere);
-
- /**
- * Get the DROP VIEW statement.
- *
- * @param stat
- * return the SQL statement
- * @param def
- * table definition
- */
- <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def);
-
- /**
- * Get the CREATE INDEX statement.
- *
- * @param stat
- * return the SQL statement
- * @param schemaName
- * the schema name
- * @param tableName
- * the table name
- * @param index
- * the index definition
- */
- 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
- */
- <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName, TableDefinition<T> 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.
- * <p>
- * Either java.util.Date or java.sql.Timestamp
- *
- * @return preferred DATETIME class
- */
- Class<? extends java.util.Date> 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<? extends DataTypeAdapter<?>> typeAdapter);
+
+ /**
+ * Serialize the Java object into a type or format that the database will accept.
+ *
+ * @param value
+ * @param typeAdapter
+ * @return the serialized object
+ */
+ <T> Object serialize(T value, Class<? extends DataTypeAdapter<?>> 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<? extends DataTypeAdapter<?>> 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
+ */
+ <T> void prepareCreateTable(SQLStatement stat, TableDefinition<T> def);
+
+ /**
+ * Get the DROP TABLE statement.
+ *
+ * @param stat
+ * @param def
+ */
+ <T> void prepareDropTable(SQLStatement stat, TableDefinition<T> def);
+
+
+ /**
+ * Get the CREATE VIEW statement.
+ *
+ * @param stat return the SQL statement
+ * @param def table definition
+ */
+ <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> def);
+
+ /**
+ * Get the CREATE VIEW statement.
+ *
+ * @param stat return the SQL statement
+ * @param def table definition
+ * @param fromWhere
+ */
+ <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> def, String fromWhere);
+
+ /**
+ * Get the DROP VIEW statement.
+ *
+ * @param stat return the SQL statement
+ * @param def table definition
+ */
+ <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def);
+
+ /**
+ * Get the CREATE INDEX statement.
+ *
+ * @param stat return the SQL statement
+ * @param schemaName the schema name
+ * @param tableName the table name
+ * @param index the index definition
+ */
+ 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
+ */
+ <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName, TableDefinition<T> 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.
+ * <p>
+ * Either java.util.Date or java.sql.Timestamp
+ *
+ * @return preferred DATETIME class
+ */
+ Class<? extends java.util.Date> 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<Class<? extends DataTypeAdapter<?>>, DataTypeAdapter<?>> typeAdapters;
-
- public SQLDialectDefault() {
- typeAdapters = new ConcurrentHashMap<Class<? extends DataTypeAdapter<?>>, 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<? extends java.util.Date> 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 <T> void prepareDropTable(SQLStatement stat, TableDefinition<T> def) {
- StatementBuilder buff = new StatementBuilder("DROP TABLE IF EXISTS "
- + prepareTableName(def.schemaName, def.tableName));
- stat.setSQL(buff.toString());
- return;
- }
-
- protected <T> String prepareCreateTable(TableDefinition<T> def) {
- return "CREATE TABLE";
- }
-
- @Override
- public <T> void prepareCreateTable(SQLStatement stat, TableDefinition<T> 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 <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def) {
- StatementBuilder buff = new StatementBuilder("DROP VIEW "
- + prepareTableName(def.schemaName, def.tableName));
- stat.setSQL(buff.toString());
- return;
- }
-
- protected <T> String prepareCreateView(TableDefinition<T> def) {
- return "CREATE VIEW";
- }
-
- @Override
- public <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> 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 <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> 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.
- * <p>
- * Databases that do support a MERGE syntax should override this method.
- * <p>
- * http://stackoverflow.com/questions/407688
- */
- @Override
- public <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
- TableDefinition<T> 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<? extends DataTypeAdapter<?>>) typeAdapter.getClass(), typeAdapter);
- }
-
- @Override
- public DataTypeAdapter<?> getAdapter(Class<? extends DataTypeAdapter<?>> 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 <T> Object serialize(T value, Class<? extends DataTypeAdapter<?>> typeAdapter) {
- if (typeAdapter == null) {
- // pass-through
- return value;
- }
-
- DataTypeAdapter<T> dta = (DataTypeAdapter<T>) getAdapter(typeAdapter);
- return dta.serialize(value);
- }
-
- @Override
- public Object deserialize(ResultSet rs, int columnIndex, Class<?> targetType, Class<? extends DataTypeAdapter<?>> 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<Class<? extends DataTypeAdapter<?>>, DataTypeAdapter<?>> typeAdapters;
+
+ public SQLDialectDefault() {
+ typeAdapters = new ConcurrentHashMap<Class<? extends DataTypeAdapter<?>>, 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<? extends java.util.Date> 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 <T> void prepareDropTable(SQLStatement stat, TableDefinition<T> def) {
+ StatementBuilder buff = new StatementBuilder("DROP TABLE IF EXISTS "
+ + prepareTableName(def.schemaName, def.tableName));
+ stat.setSQL(buff.toString());
+ return;
+ }
+
+ protected <T> String prepareCreateTable(TableDefinition<T> def) {
+ return "CREATE TABLE";
+ }
+
+ @Override
+ public <T> void prepareCreateTable(SQLStatement stat, TableDefinition<T> 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 <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def) {
+ StatementBuilder buff = new StatementBuilder("DROP VIEW "
+ + prepareTableName(def.schemaName, def.tableName));
+ stat.setSQL(buff.toString());
+ return;
+ }
+
+ protected <T> String prepareCreateView(TableDefinition<T> def) {
+ return "CREATE VIEW";
+ }
+
+ @Override
+ public <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> 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 <T> void prepareCreateView(SQLStatement stat, TableDefinition<T> 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.
+ * <p>
+ * Databases that do support a MERGE syntax should override this method.
+ * <p>
+ * http://stackoverflow.com/questions/407688
+ */
+ @Override
+ public <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
+ TableDefinition<T> 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<? extends DataTypeAdapter<?>>) typeAdapter.getClass(), typeAdapter);
+ }
+
+ @Override
+ public DataTypeAdapter<?> getAdapter(Class<? extends DataTypeAdapter<?>> 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 <T> Object serialize(T value, Class<? extends DataTypeAdapter<?>> typeAdapter) {
+ if (typeAdapter == null) {
+ // pass-through
+ return value;
+ }
+
+ DataTypeAdapter<T> dta = (DataTypeAdapter<T>) getAdapter(typeAdapter);
+ return dta.serialize(value);
+ }
+
+ @Override
+ public Object deserialize(ResultSet rs, int columnIndex, Class<?> targetType, Class<? extends DataTypeAdapter<?>> 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<? extends java.util.Date> getDateTimeClass() {
- return java.sql.Timestamp.class;
- }
+ @Override
+ public Class<? extends java.util.Date> 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 <T> void prepareDropTable(SQLStatement stat, TableDefinition<T> def) {
- StatementBuilder buff = new StatementBuilder("DROP TABLE "
- + prepareTableName(def.schemaName, def.tableName));
- stat.setSQL(buff.toString());
- return;
- }
+ @Override
+ public <T> void prepareDropTable(SQLStatement stat, TableDefinition<T> 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 <T> String prepareCreateTable(TableDefinition<T> 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 <T> String prepareCreateTable(TableDefinition<T> def) {
+ if (def.memoryTable) {
+ return "CREATE MEMORY TABLE IF NOT EXISTS";
+ } else {
+ return "CREATE CACHED TABLE IF NOT EXISTS";
+ }
+ }
- @Override
- protected <T> String prepareCreateView(TableDefinition<T> def) {
- return "CREATE VIEW IF NOT EXISTS";
- }
+ @Override
+ protected <T> String prepareCreateView(TableDefinition<T> def) {
+ return "CREATE VIEW IF NOT EXISTS";
+ }
- @Override
- public <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def) {
- StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS "
- + prepareTableName(def.schemaName, def.tableName));
- stat.setSQL(buff.toString());
- return;
- }
+ @Override
+ public <T> void prepareDropView(SQLStatement stat, TableDefinition<T> 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 <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
- TableDefinition<T> 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 <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
+ TableDefinition<T> 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 <T> String prepareCreateTable(TableDefinition<T> 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 <T> String prepareCreateTable(TableDefinition<T> def) {
+ if (def.memoryTable) {
+ return "CREATE MEMORY TABLE IF NOT EXISTS";
+ } else {
+ return "CREATE CACHED TABLE IF NOT EXISTS";
+ }
+ }
- @Override
- public <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def) {
- StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS "
- + prepareTableName(def.schemaName, def.tableName));
- stat.setSQL(buff.toString());
- return;
- }
+ @Override
+ public <T> void prepareDropView(SQLStatement stat, TableDefinition<T> 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 <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
- TableDefinition<T> 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 <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
+ TableDefinition<T> 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 <T> String prepareCreateTable(TableDefinition<T> def) {
- return "CREATE TABLE IF NOT EXISTS";
- }
+ @Override
+ protected <T> String prepareCreateTable(TableDefinition<T> def) {
+ return "CREATE TABLE IF NOT EXISTS";
+ }
- @Override
- public <T> void prepareDropView(SQLStatement stat, TableDefinition<T> def) {
- StatementBuilder buff = new StatementBuilder("DROP VIEW IF EXISTS "
- + prepareTableName(def.schemaName, def.tableName));
- stat.setSQL(buff.toString());
- return;
- }
+ @Override
+ public <T> void prepareDropView(SQLStatement stat, TableDefinition<T> 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 <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
- TableDefinition<T> 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 <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
+ TableDefinition<T> 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<? extends java.util.Date> 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 <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
- TableDefinition<T> 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<? extends java.util.Date> 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 <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
+ TableDefinition<T> 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 <T> String prepareCreateTable(TableDefinition<T> def) {
- return "CREATE TABLE IF NOT EXISTS";
- }
-
- @Override
- protected <T> String prepareCreateView(TableDefinition<T> 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 <T> void prepareDropView(SQLStatement stat, TableDefinition<T> 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 <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
- TableDefinition<T> 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<? extends DataTypeAdapter<?>> 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 <T> String prepareCreateTable(TableDefinition<T> def) {
+ return "CREATE TABLE IF NOT EXISTS";
+ }
+
+ @Override
+ protected <T> String prepareCreateView(TableDefinition<T> 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 <T> void prepareDropView(SQLStatement stat, TableDefinition<T> 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 <T> void prepareMerge(SQLStatement stat, String schemaName, String tableName,
+ TableDefinition<T> 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<? extends DataTypeAdapter<?>> 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<Object> params = new ArrayList<Object>();
-
- 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<Object> params = new ArrayList<Object>();
+
+ 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 <T>
- * the table data type
+ *
+ * @param <T> the table data type
*/
class SelectColumn<T> {
- private SelectTable<T> selectTable;
- private FieldDefinition fieldDef;
-
- SelectColumn(SelectTable<T> 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<T> getSelectTable() {
- return selectTable;
- }
-
- Object getCurrentValue() {
- return fieldDef.getValue(selectTable.getCurrent());
- }
+ private SelectTable<T> selectTable;
+ private FieldDefinition fieldDef;
+
+ SelectColumn(SelectTable<T> 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<T> 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 <T>
- * the table class
+ *
+ * @param <T> the table class
*/
class SelectTable<T> {
- private Query<T> query;
- private Class<T> clazz;
- private T current;
- private String as;
- private TableDefinition<T> aliasDef;
- private boolean outerJoin;
- private ArrayList<Token> joinConditions = Utils.newArrayList();
- private T alias;
-
- @SuppressWarnings("unchecked")
- SelectTable(Db db, Query<T> query, T alias, boolean outerJoin) {
- this.alias = alias;
- this.query = query;
- this.outerJoin = outerJoin;
- aliasDef = (TableDefinition<T>) db.getTableDefinition(alias.getClass());
- clazz = Utils.getClass(alias);
- as = "T" + Utils.nextAsCount();
- }
-
- T getAlias() {
- return alias;
- }
-
- T newObject() {
- return Utils.newObject(clazz);
- }
-
- TableDefinition<T> 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<T> 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<T> 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<T> query;
+ private Class<T> clazz;
+ private T current;
+ private String as;
+ private TableDefinition<T> aliasDef;
+ private boolean outerJoin;
+ private ArrayList<Token> joinConditions = Utils.newArrayList();
+ private T alias;
+
+ @SuppressWarnings("unchecked")
+ SelectTable(Db db, Query<T> query, T alias, boolean outerJoin) {
+ this.alias = alias;
+ this.query = query;
+ this.outerJoin = outerJoin;
+ aliasDef = (TableDefinition<T>) db.getTableDefinition(alias.getClass());
+ clazz = Utils.getClass(alias);
+ as = "T" + Utils.nextAsCount();
+ }
+
+ T getAlias() {
+ return alias;
+ }
+
+ T newObject() {
+ return Utils.newObject(clazz);
+ }
+
+ TableDefinition<T> 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<T> 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<T> 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<T, Z> {
-
- final Query<T> query;
- final Z z;
-
- public SubQuery(Query<T> query, Z x) {
- this.query = query;
- this.z = x;
- }
- public void appendSQL(SQLStatement stat) {
- stat.appendSQL(query.toSubQuery(z));
- }
+ final Query<T> query;
+ final Z z;
+
+ public SubQuery(Query<T> 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 <A>
- * the operand type
+ *
+ * @param <A> the operand type
*/
class SubQueryCondition<A, Y, Z> implements Token {
- A x;
- SubQuery<Y, Z> subquery;
+ A x;
+ SubQuery<Y, Z> subquery;
- SubQueryCondition(A x, SubQuery<Y, Z> subquery) {
- this.x = x;
- this.subquery = subquery;
- }
+ SubQueryCondition(A x, SubQuery<Y, Z> subquery) {
+ this.x = x;
+ this.subquery = subquery;
+ }
- public <T> void appendSQL(SQLStatement stat, Query<T> query) {
- query.appendSQL(stat, null, x);
- stat.appendSQL(" in (");
- subquery.appendSQL(stat);
- stat.appendSQL(")");
- }
+ public <T> void appendSQL(SQLStatement stat, Query<T> 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 <T>
- * the table type
+ * @param <T> the table type
*/
public class TableDefinition<T> {
- /**
- * The meta data of an index.
- */
-
- public static class IndexDefinition {
- public IndexType type;
- public String indexName;
-
- public List<String> columnNames;
- }
-
- /**
- * The meta data of a constraint on foreign key.
- */
-
- public static class ConstraintForeignKeyDefinition {
-
- public String constraintName;
- public List<String> foreignColumns;
- public String referenceTable;
- public List<String> referenceColumns;
- public ConstraintDeleteType deleteType = ConstraintDeleteType.UNSET;
- public ConstraintUpdateType updateType = ConstraintUpdateType.UNSET;
- public ConstraintDeferrabilityType deferrabilityType = ConstraintDeferrabilityType.UNSET;
- }
-
- /**
- * The meta data of a unique constraint.
- */
-
- public static class ConstraintUniqueDefinition {
-
- public String constraintName;
- public List<String> uniqueColumns;
- }
-
-
- /**
- * The meta data of a field.
- */
-
- static class FieldDefinition {
- String columnName;
- Field field;
- String dataType;
- int length;
- int scale;
- boolean isPrimaryKey;
- boolean isAutoIncrement;
- boolean trim;
- boolean nullable;
- String defaultValue;
- EnumType enumType;
- Class<?> enumTypeClass;
- boolean isPrimitive;
- String constraint;
- Class<? extends DataTypeAdapter<?>> typeAdapter;
-
- Object getValue(Object obj) {
- try {
- return field.get(obj);
- } catch (Exception e) {
- throw new IciqlException(e);
- }
- }
-
- private Object initWithNewObject(Object obj) {
- Object o = Utils.newObject(field.getType());
- setValue(obj, o);
- return o;
- }
-
- private void setValue(Object obj, Object o) {
- try {
- if (!field.isAccessible()) {
- field.setAccessible(true);
- }
-
- if (field.getType().isPrimitive() && o == null) {
- // do not attempt to set a primitive to null
- return;
- }
-
- field.set(obj, o);
- } catch (IciqlException e) {
- throw e;
- } catch (Exception e) {
- throw new IciqlException(e);
- }
- }
-
- @Override
- public int hashCode() {
- return columnName.hashCode();
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof FieldDefinition) {
- return o.hashCode() == hashCode();
- }
- return false;
- }
- }
-
- public ArrayList<FieldDefinition> fields = Utils.newArrayList();
- String schemaName;
- String tableName;
- String viewTableName;
- int tableVersion;
- List<String> primaryKeyColumnNames;
- boolean memoryTable;
- boolean multiplePrimitiveBools;
-
- private boolean createIfRequired = true;
- private Class<T> clazz;
- private IdentityHashMap<Object, FieldDefinition> fieldMap = Utils.newIdentityHashMap();
- private ArrayList<IndexDefinition> indexes = Utils.newArrayList();
- ArrayList<ConstraintForeignKeyDefinition> constraintsForeignKey = Utils.newArrayList();
- ArrayList<ConstraintUniqueDefinition> constraintsUnique = Utils.newArrayList();
-
- TableDefinition(Class<T> clazz) {
- this.clazz = clazz;
- schemaName = null;
- tableName = clazz.getSimpleName();
- }
-
- Class<T> getModelClass() {
- return clazz;
- }
-
- List<FieldDefinition> getFields() {
- return fields;
- }
-
- void defineSchemaName(String schemaName) {
- this.schemaName = schemaName;
- }
-
- void defineTableName(String tableName) {
- this.tableName = tableName;
- }
-
- void defineViewTableName(String viewTableName) {
- this.viewTableName = viewTableName;
- }
-
- void defineMemoryTable() {
- this.memoryTable = true;
- }
-
- void defineSkipCreate() {
- this.createIfRequired = false;
- }
-
- /**
- * Define a primary key by the specified model fields.
- *
- * @param modelFields
- * the ordered list of model fields
- */
- void definePrimaryKey(Object[] modelFields) {
- List<String> columnNames = mapColumnNames(modelFields);
- setPrimaryKey(columnNames);
- }
-
- /**
- * Define a primary key by the specified column names.
- *
- * @param columnNames
- * the ordered list of column names
- */
- private void setPrimaryKey(List<String> columnNames) {
- primaryKeyColumnNames = Utils.newArrayList(columnNames);
- List<String> pkNames = Utils.newArrayList();
- for (String name : columnNames) {
- pkNames.add(name.toLowerCase());
- }
- // set isPrimaryKey flag for all field definitions
- for (FieldDefinition fieldDefinition : fieldMap.values()) {
- fieldDefinition.isPrimaryKey = pkNames.contains(fieldDefinition.columnName.toLowerCase());
- }
- }
-
- private <A> String getColumnName(A fieldObject) {
- FieldDefinition def = fieldMap.get(fieldObject);
- return def == null ? null : def.columnName;
- }
-
- private ArrayList<String> mapColumnNames(Object[] columns) {
- ArrayList<String> columnNames = Utils.newArrayList();
- for (Object column : columns) {
- columnNames.add(getColumnName(column));
- }
- return columnNames;
- }
-
- /**
- * Defines an index with the specified model fields.
- *
- * @param name
- * the index name (optional)
- * @param type
- * the index type (STANDARD, HASH, UNIQUE, UNIQUE_HASH)
- * @param modelFields
- * the ordered list of model fields
- */
- void defineIndex(String name, IndexType type, Object[] modelFields) {
- List<String> columnNames = mapColumnNames(modelFields);
- addIndex(name, type, columnNames);
- }
-
- /**
- * Defines an index with the specified column names.
- *
- * @param type
- * the index type (STANDARD, HASH, UNIQUE, UNIQUE_HASH)
- * @param columnNames
- * the ordered list of column names
- */
- private void addIndex(String name, IndexType type, List<String> columnNames) {
- IndexDefinition index = new IndexDefinition();
- if (StringUtils.isNullOrEmpty(name)) {
- index.indexName = tableName + "_idx_" + indexes.size();
- } else {
- index.indexName = name;
- }
- index.columnNames = Utils.newArrayList(columnNames);
- index.type = type;
- indexes.add(index);
- }
-
- /**
- * Defines an unique constraint with the specified model fields.
- *
- * @param name
- * the constraint name (optional)
- * @param modelFields
- * the ordered list of model fields
- */
- void defineConstraintUnique(String name, Object[] modelFields) {
- List<String> columnNames = mapColumnNames(modelFields);
- addConstraintUnique(name, columnNames);
- }
-
- /**
- * Defines an unique constraint.
- *
- * @param name
- * @param columnNames
- */
- private void addConstraintUnique(String name, List<String> columnNames) {
- ConstraintUniqueDefinition constraint = new ConstraintUniqueDefinition();
- if (StringUtils.isNullOrEmpty(name)) {
- constraint.constraintName = tableName + "_unique_" + constraintsUnique.size();
- } else {
- constraint.constraintName = name;
- }
- constraint.uniqueColumns = Utils.newArrayList(columnNames);
- constraintsUnique.add(constraint);
- }
-
- /**
- * Defines a foreign key constraint with the specified model fields.
- *
- * @param name
- * the constraint name (optional)
- * @param modelFields
- * the ordered list of model fields
- */
- void defineForeignKey(String name, Object[] modelFields, String refTableName, Object[] refModelFields,
- ConstraintDeleteType deleteType, ConstraintUpdateType updateType,
- ConstraintDeferrabilityType deferrabilityType) {
- List<String> columnNames = mapColumnNames(modelFields);
- List<String> referenceColumnNames = mapColumnNames(refModelFields);
- addConstraintForeignKey(name, columnNames, refTableName, referenceColumnNames,
- deleteType, updateType, deferrabilityType);
- }
-
- void defineColumnName(Object column, String columnName) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.columnName = columnName;
- }
- }
-
- void defineAutoIncrement(Object column) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.isAutoIncrement = true;
- }
- }
-
- void defineLength(Object column, int length) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.length = length;
- }
- }
-
- void defineScale(Object column, int scale) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.scale = scale;
- }
- }
-
- void defineTrim(Object column) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.trim = true;
- }
- }
-
- void defineNullable(Object column, boolean isNullable) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.nullable = isNullable;
- }
- }
-
- void defineDefaultValue(Object column, String defaultValue) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.defaultValue = defaultValue;
- }
- }
-
- void defineConstraint(Object column, String constraint) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.constraint = constraint;
- }
- }
-
- void defineTypeAdapter(Object column, Class<? extends DataTypeAdapter<?>> typeAdapter) {
- FieldDefinition def = fieldMap.get(column);
- if (def != null) {
- def.typeAdapter = typeAdapter;
- }
- }
-
- void mapFields(Db db) {
- boolean byAnnotationsOnly = false;
- boolean inheritColumns = false;
- if (clazz.isAnnotationPresent(IQTable.class)) {
- IQTable tableAnnotation = clazz.getAnnotation(IQTable.class);
- byAnnotationsOnly = tableAnnotation.annotationsOnly();
- inheritColumns = tableAnnotation.inheritColumns();
- }
-
- if (clazz.isAnnotationPresent(IQView.class)) {
- IQView viewAnnotation = clazz.getAnnotation(IQView.class);
- byAnnotationsOnly = viewAnnotation.annotationsOnly();
- inheritColumns = viewAnnotation.inheritColumns();
- }
-
- List<Field> classFields = classFields(inheritColumns);
-
- Set<FieldDefinition> uniqueFields = new LinkedHashSet<FieldDefinition>();
- T defaultObject = Db.instance(clazz);
- for (Field f : classFields) {
- // check if we should skip this field
- if (f.isAnnotationPresent(IQIgnore.class)) {
- continue;
- }
-
- // default to field name
- String columnName = f.getName();
- boolean isAutoIncrement = false;
- boolean isPrimaryKey = false;
- int length = 0;
- int scale = 0;
- boolean trim = false;
- boolean nullable = !f.getType().isPrimitive();
- String defaultValue = "";
- String constraint = "";
- String dataType = null;
- Class<? extends DataTypeAdapter<?>> typeAdapter = null;
-
- // configure Java -> SQL enum mapping
- EnumType enumType = Utils.getEnumType(f);
- Class<?> enumTypeClass = Utils.getEnumTypeClass(f);
-
- // try using default object
- try {
- f.setAccessible(true);
- Object value = f.get(defaultObject);
- if (value != null) {
- if (value.getClass().isEnum()) {
- // enum default, convert to target type
- Enum<?> anEnum = (Enum<?>) value;
- Object o = Utils.convertEnum(anEnum, enumType);
- defaultValue = ModelUtils.formatDefaultValue(o);
- } else {
- // object default
- defaultValue = ModelUtils.formatDefaultValue(value);
- }
- }
- } catch (IllegalAccessException e) {
- throw new IciqlException(e, "failed to get default object for {0}", columnName);
- }
-
- // identify the type adapter
- typeAdapter = Utils.getDataTypeAdapter(f.getAnnotations());
- if (typeAdapter == null) {
- typeAdapter = Utils.getDataTypeAdapter(f.getType().getAnnotations());
- }
-
- if (typeAdapter != null) {
- DataTypeAdapter<?> dtt = db.getDialect().getAdapter(typeAdapter);
- dataType = dtt.getDataType();
- }
-
- boolean hasAnnotation = f.isAnnotationPresent(IQColumn.class);
- if (hasAnnotation) {
- IQColumn col = f.getAnnotation(IQColumn.class);
- if (!StringUtils.isNullOrEmpty(col.name())) {
- columnName = col.name();
- }
- isAutoIncrement = col.autoIncrement();
- isPrimaryKey = col.primaryKey();
- length = col.length();
- scale = col.scale();
- trim = col.trim();
- nullable = col.nullable();
-
- // annotation overrides
- if (!StringUtils.isNullOrEmpty(col.defaultValue())) {
- defaultValue = col.defaultValue();
- }
- }
-
- boolean hasConstraint = f.isAnnotationPresent(IQConstraint.class);
- if (hasConstraint) {
- IQConstraint con = f.getAnnotation(IQConstraint.class);
- // annotation overrides
- if (!StringUtils.isNullOrEmpty(con.value())) {
- constraint = con.value();
- }
- }
-
- boolean reflectiveMatch = !byAnnotationsOnly;
- if (reflectiveMatch || hasAnnotation || hasConstraint) {
- FieldDefinition fieldDef = new FieldDefinition();
- fieldDef.isPrimitive = f.getType().isPrimitive();
- fieldDef.field = f;
- fieldDef.columnName = columnName;
- fieldDef.isAutoIncrement = isAutoIncrement;
- fieldDef.isPrimaryKey = isPrimaryKey;
- fieldDef.length = length;
- fieldDef.scale = scale;
- fieldDef.trim = trim;
- fieldDef.nullable = nullable;
- fieldDef.defaultValue = defaultValue;
- fieldDef.enumType = enumType;
- fieldDef.enumTypeClass = enumTypeClass;
- fieldDef.dataType = StringUtils.isNullOrEmpty(dataType) ? ModelUtils.getDataType(fieldDef) : dataType;
- fieldDef.typeAdapter = typeAdapter;
- fieldDef.constraint = constraint;
- uniqueFields.add(fieldDef);
- }
- }
- fields.addAll(uniqueFields);
-
- List<String> primaryKey = Utils.newArrayList();
- int primitiveBoolean = 0;
- for (FieldDefinition fieldDef : fields) {
- if (fieldDef.isPrimaryKey) {
- primaryKey.add(fieldDef.columnName);
- }
- if (fieldDef.isPrimitive && fieldDef.field.getType().equals(boolean.class)) {
- primitiveBoolean++;
- }
- }
- if (primitiveBoolean > 1) {
- multiplePrimitiveBools = true;
- IciqlLogger
- .warn("Model {0} has multiple primitive booleans! Possible where,set,join clause problem!", tableName);
- }
- if (primaryKey.size() > 0) {
- setPrimaryKey(primaryKey);
- }
- }
-
- private List<Field> classFields(boolean inheritColumns) {
- List<Field> classFields = Utils.newArrayList();
- classFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
- Class<?> superClass = clazz;
- while (inheritColumns) {
- superClass = superClass.getSuperclass();
- classFields.addAll(Arrays.asList(superClass.getDeclaredFields()));
-
- if (superClass.isAnnotationPresent(IQView.class)) {
- IQView superView = superClass.getAnnotation(IQView.class);
- inheritColumns = superView.inheritColumns();
- } else if (superClass.isAnnotationPresent(IQTable.class)) {
- IQTable superTable = superClass.getAnnotation(IQTable.class);
- inheritColumns = superTable.inheritColumns();
- } else {
- inheritColumns = false;
- }
- }
- return classFields;
- }
-
- void checkMultipleBooleans() {
- if (multiplePrimitiveBools) {
- throw new IciqlException(
- "Can not explicitly reference a primitive boolean if there are multiple boolean fields in your model class!");
- }
- }
-
- void checkMultipleEnums(Object o) {
- if (o == null) {
- return;
- }
- Class<?> clazz = o.getClass();
- if (!clazz.isEnum()) {
- return;
- }
-
- int fieldCount = 0;
- for (FieldDefinition fieldDef : fields) {
- Class<?> targetType = fieldDef.field.getType();
- if (clazz.equals(targetType)) {
- fieldCount++;
- }
- }
-
- if (fieldCount > 1) {
- throw new IciqlException(
- "Can not explicitly reference {0} because there are {1} {0} fields in your model class!",
- clazz.getSimpleName(), fieldCount);
- }
- }
-
- /**
- * Optionally truncates strings to the maximum length and converts
- * java.lang.Enum types to Strings or Integers.
- */
- Object getValue(Object obj, FieldDefinition field) {
- Object value = field.getValue(obj);
- if (value == null) {
- return value;
- }
- if (field.enumType != null) {
- // convert enumeration to INT or STRING
- Enum<?> iqenum = (Enum<?>) value;
- switch (field.enumType) {
- case NAME:
- if (field.trim && field.length > 0) {
- if (iqenum.name().length() > field.length) {
- return iqenum.name().substring(0, field.length);
- }
- }
- return iqenum.name();
- case ORDINAL:
- return iqenum.ordinal();
- case ENUMID:
- if (!EnumId.class.isAssignableFrom(value.getClass())) {
- throw new IciqlException(field.field.getName() + " does not implement EnumId!");
- }
- EnumId<?> enumid = (EnumId<?>) value;
- return enumid.enumId();
- }
- }
-
- if (field.trim && field.length > 0) {
- if (value instanceof String) {
- // clip strings
- String s = (String) value;
- if (s.length() > field.length) {
- return s.substring(0, field.length);
- }
- return s;
- }
- return value;
- }
-
- // return the value unchanged
- return value;
- }
-
- PreparedStatement createInsertStatement(Db db, Object obj, boolean returnKey) {
- SQLStatement stat = new SQLStatement(db);
- StatementBuilder buff = new StatementBuilder("INSERT INTO ");
- buff.append(db.getDialect().prepareTableName(schemaName, tableName)).append('(');
- for (FieldDefinition field : fields) {
- if (skipInsertField(field, obj)) {
- continue;
- }
- buff.appendExceptFirst(", ");
- buff.append(db.getDialect().prepareColumnName(field.columnName));
- }
- buff.append(") VALUES(");
- buff.resetCount();
- for (FieldDefinition field : fields) {
- if (skipInsertField(field, obj)) {
- continue;
- }
- buff.appendExceptFirst(", ");
- buff.append('?');
- Object value = getValue(obj, field);
- if (value == null) {
- 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<Object> query = Query.from(db, alias);
- boolean firstCondition = true;
- for (FieldDefinition field : fields) {
- if (field.isPrimaryKey) {
- Object fieldAlias = field.getValue(alias);
- Object value = field.getValue(obj);
- if (field.isPrimitive) {
- fieldAlias = query.getPrimitiveAliasByValue(fieldAlias);
- }
- if (!firstCondition) {
- query.addConditionToken(ConditionAndOr.AND);
- }
- firstCondition = false;
- query.addConditionToken(new Condition<Object>(fieldAlias, value, CompareType.EQUAL));
- }
- }
- stat.setSQL(buff.toString());
- query.appendWhere(stat);
- IciqlLogger.update(stat.getSQL());
- return stat.executeUpdate();
- }
-
- int delete(Db db, Object obj) {
- if (!StringUtils.isNullOrEmpty(viewTableName)) {
- throw new IciqlException("Iciql does not support deleting rows from views!");
- }
- if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {
- throw new IllegalStateException("No primary key columns defined for table " + obj.getClass()
- + " - no update possible");
- }
- SQLStatement stat = new SQLStatement(db);
- StatementBuilder buff = new StatementBuilder("DELETE FROM ");
- buff.append(db.getDialect().prepareTableName(schemaName, tableName));
- buff.resetCount();
- Object alias = Utils.newObject(obj.getClass());
- Query<Object> query = Query.from(db, alias);
- boolean firstCondition = true;
- for (FieldDefinition field : fields) {
- if (field.isPrimaryKey) {
- Object fieldAlias = field.getValue(alias);
- Object value = field.getValue(obj);
- if (field.isPrimitive) {
- fieldAlias = query.getPrimitiveAliasByValue(fieldAlias);
- }
- if (!firstCondition) {
- query.addConditionToken(ConditionAndOr.AND);
- }
- firstCondition = false;
- query.addConditionToken(new Condition<Object>(fieldAlias, value, CompareType.EQUAL));
- }
- }
- stat.setSQL(buff.toString());
- query.appendWhere(stat);
- IciqlLogger.delete(stat.getSQL());
- return stat.executeUpdate();
- }
-
- TableDefinition<T> createIfRequired(Db db) {
- // globally enable/disable check of create if required
- if (db.getSkipCreate()) {
- return this;
- }
- if (!createIfRequired) {
- // skip table and index creation
- // but still check for upgrades
- db.upgradeTable(this);
- return this;
- }
- if (db.hasCreated(clazz)) {
- return this;
- }
- SQLStatement stat = new SQLStatement(db);
- if (StringUtils.isNullOrEmpty(viewTableName)) {
- db.getDialect().prepareCreateTable(stat, this);
- } else {
- db.getDialect().prepareCreateView(stat, this);
- }
- IciqlLogger.create(stat.getSQL());
- try {
- stat.executeUpdate();
- } catch (IciqlException e) {
- if (e.getIciqlCode() != IciqlException.CODE_OBJECT_ALREADY_EXISTS) {
- throw e;
- }
- }
-
- // create indexes
- for (IndexDefinition index : indexes) {
- stat = new SQLStatement(db);
- db.getDialect().prepareCreateIndex(stat, schemaName, tableName, index);
- IciqlLogger.create(stat.getSQL());
- try {
- stat.executeUpdate();
- } catch (IciqlException e) {
- if (e.getIciqlCode() != IciqlException.CODE_OBJECT_ALREADY_EXISTS
- && e.getIciqlCode() != IciqlException.CODE_DUPLICATE_KEY) {
- throw e;
- }
- }
- }
-
- // tables are created using IF NOT EXISTS
- // but we may still need to upgrade
- db.upgradeTable(this);
- return this;
- }
-
- void mapObject(Object obj) {
- fieldMap.clear();
- initObject(obj, fieldMap);
-
- if (clazz.isAnnotationPresent(IQSchema.class)) {
- IQSchema schemaAnnotation = clazz.getAnnotation(IQSchema.class);
- // setup schema name mapping, if properly annotated
- if (!StringUtils.isNullOrEmpty(schemaAnnotation.value())) {
- schemaName = schemaAnnotation.value();
- }
- }
-
- if (clazz.isAnnotationPresent(IQTable.class)) {
- IQTable tableAnnotation = clazz.getAnnotation(IQTable.class);
-
- // setup table name mapping, if properly annotated
- if (!StringUtils.isNullOrEmpty(tableAnnotation.name())) {
- tableName = tableAnnotation.name();
- }
-
- // allow control over createTableIfRequired()
- createIfRequired = tableAnnotation.create();
-
- // model version
- if (clazz.isAnnotationPresent(IQVersion.class)) {
- IQVersion versionAnnotation = clazz.getAnnotation(IQVersion.class);
- if (versionAnnotation.value() > 0) {
- tableVersion = versionAnnotation.value();
- }
- }
-
- // setup the primary index, if properly annotated
- if (tableAnnotation.primaryKey().length > 0) {
- List<String> primaryKey = Utils.newArrayList();
- primaryKey.addAll(Arrays.asList(tableAnnotation.primaryKey()));
- setPrimaryKey(primaryKey);
- }
- }
-
- if (clazz.isAnnotationPresent(IQView.class)) {
- IQView viewAnnotation = clazz.getAnnotation(IQView.class);
-
- // setup view name mapping, if properly annotated
- // set this as the table name so it fits in seemlessly with iciql
- if (!StringUtils.isNullOrEmpty(viewAnnotation.name())) {
- tableName = viewAnnotation.name();
- } else {
- tableName = clazz.getSimpleName();
- }
-
- // setup source table name mapping, if properly annotated
- if (!StringUtils.isNullOrEmpty(viewAnnotation.tableName())) {
- viewTableName = viewAnnotation.tableName();
- } else {
- // check for IQTable annotation on super class
- Class<?> superClass = clazz.getSuperclass();
- if (superClass.isAnnotationPresent(IQTable.class)) {
- IQTable table = superClass.getAnnotation(IQTable.class);
- if (StringUtils.isNullOrEmpty(table.name())) {
- // super.SimpleClassName
- viewTableName = superClass.getSimpleName();
- } else {
- // super.IQTable.name()
- viewTableName = table.name();
- }
- } else if (superClass.isAnnotationPresent(IQView.class)) {
- // super class is a view
- IQView parentView = superClass.getAnnotation(IQView.class);
- if (StringUtils.isNullOrEmpty(parentView.tableName())) {
- // parent view does not define a tableName, must be inherited
- Class<?> superParent = superClass.getSuperclass();
- if (superParent != null && superParent.isAnnotationPresent(IQTable.class)) {
- IQTable superParentTable = superParent.getAnnotation(IQTable.class);
- if (StringUtils.isNullOrEmpty(superParentTable.name())) {
- // super.super.SimpleClassName
- viewTableName = superParent.getSimpleName();
- } else {
- // super.super.IQTable.name()
- viewTableName = superParentTable.name();
- }
- }
- } else {
- // super.IQView.tableName()
- viewTableName = parentView.tableName();
- }
- }
-
- if (StringUtils.isNullOrEmpty(viewTableName)) {
- // still missing view table name
- throw new IciqlException("View model class \"{0}\" is missing a table name!", tableName);
- }
- }
-
- // allow control over createTableIfRequired()
- createIfRequired = viewAnnotation.create();
- }
-
- if (clazz.isAnnotationPresent(IQIndex.class)) {
- // single table index
- IQIndex index = clazz.getAnnotation(IQIndex.class);
- addIndex(index);
- }
-
- if (clazz.isAnnotationPresent(IQIndexes.class)) {
- // multiple table indexes
- IQIndexes indexes = clazz.getAnnotation(IQIndexes.class);
- for (IQIndex index : indexes.value()) {
- addIndex(index);
- }
- }
-
- if (clazz.isAnnotationPresent(IQContraintUnique.class)) {
- // single table unique constraint
- IQContraintUnique constraint = clazz.getAnnotation(IQContraintUnique.class);
- addConstraintUnique(constraint);
- }
-
- if (clazz.isAnnotationPresent(IQContraintsUnique.class)) {
- // multiple table unique constraints
- IQContraintsUnique constraints = clazz.getAnnotation(IQContraintsUnique.class);
- for (IQContraintUnique constraint : constraints.value()) {
- addConstraintUnique(constraint);
- }
- }
-
- if (clazz.isAnnotationPresent(IQContraintForeignKey.class)) {
- // single table constraint
- IQContraintForeignKey constraint = clazz.getAnnotation(IQContraintForeignKey.class);
- addConstraintForeignKey(constraint);
- }
-
- if (clazz.isAnnotationPresent(IQContraintsForeignKey.class)) {
- // multiple table constraints
- IQContraintsForeignKey constraints = clazz.getAnnotation(IQContraintsForeignKey.class);
- for (IQContraintForeignKey constraint : constraints.value()) {
- addConstraintForeignKey(constraint);
- }
- }
-
- }
-
- private void addConstraintForeignKey(IQContraintForeignKey constraint) {
- List<String> foreignColumns = Arrays.asList(constraint.foreignColumns());
- List<String> referenceColumns = Arrays.asList(constraint.referenceColumns());
- addConstraintForeignKey(constraint.name(), foreignColumns, constraint.referenceName(), referenceColumns, constraint.deleteType(), constraint.updateType(), constraint.deferrabilityType());
- }
-
- private void addConstraintUnique(IQContraintUnique constraint) {
- List<String> uniqueColumns = Arrays.asList(constraint.uniqueColumns());
- addConstraintUnique(constraint.name(), uniqueColumns);
- }
-
- /**
- * Defines a foreign key constraint with the specified parameters.
- *
- * @param name
- * name of the constraint
- * @param foreignColumns
- * list of columns declared as foreign
- * @param referenceName
- * reference table name
- * @param referenceColumns
- * list of columns used in reference table
- * @param deleteType
- * action on delete
- * @param updateType
- * action on update
- * @param deferrabilityType
- * deferrability mode
- */
- private void addConstraintForeignKey(String name,
- List<String> foreignColumns, String referenceName,
- List<String> referenceColumns, ConstraintDeleteType deleteType,
- ConstraintUpdateType updateType, ConstraintDeferrabilityType deferrabilityType) {
- ConstraintForeignKeyDefinition constraint = new ConstraintForeignKeyDefinition();
- if (StringUtils.isNullOrEmpty(name)) {
- constraint.constraintName = tableName + "_fkey_" + constraintsForeignKey.size();
- } else {
- constraint.constraintName = name;
- }
- constraint.foreignColumns = Utils.newArrayList(foreignColumns);
- constraint.referenceColumns = Utils.newArrayList(referenceColumns);
- constraint.referenceTable = referenceName;
- constraint.deleteType = deleteType;
- constraint.updateType = updateType;
- constraint.deferrabilityType = deferrabilityType;
- constraintsForeignKey.add(constraint);
- }
-
- private void addIndex(IQIndex index) {
- List<String> columns = Arrays.asList(index.value());
- addIndex(index.name(), index.type(), columns);
- }
-
- List<IndexDefinition> getIndexes() {
- return indexes;
- }
-
- List<ConstraintUniqueDefinition> getContraintsUnique() {
- return constraintsUnique;
- }
-
- List<ConstraintForeignKeyDefinition> getContraintsForeignKey() {
- return constraintsForeignKey;
- }
-
- private void initObject(Object obj, Map<Object, FieldDefinition> map) {
- for (FieldDefinition def : fields) {
- Object newValue = def.initWithNewObject(obj);
- map.put(newValue, def);
- }
- }
-
- void initSelectObject(SelectTable<T> table, Object obj, Map<Object, SelectColumn<T>> map, boolean reuse) {
- for (FieldDefinition def : fields) {
- Object value;
- if (!reuse) {
- value = def.initWithNewObject(obj);
- } else {
- value = def.getValue(obj);
- }
- SelectColumn<T> column = new SelectColumn<T>(table, def);
- map.put(value, column);
- }
- }
-
- /**
- * Most queries executed by iciql have named select lists (select alpha,
- * beta where...) but sometimes a wildcard select is executed (select *).
- * When a wildcard query is executed on a table that has more columns than
- * are mapped in your model object, this creates a column mapping issue.
- * JaQu assumed that you can always use the integer index of the
- * reflectively mapped field definition to determine position in the result
- * set.
- *
- * This is not always true.
- *
- * iciql identifies when a select * query is executed and maps column names
- * to a column index from the result set. If the select statement is
- * explicit, then the standard assumed column index is used instead.
- *
- * @param rs
- * @return
- */
- int[] mapColumns(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);
- }
- }
-
- <Y, X> void appendSelectList(SQLStatement stat, Query<Y> query, X x) {
- // select t0.col1, t0.col2, t0.col3...
- // select table1.col1, table1.col2, table1.col3...
- String selectDot = "";
- SelectTable<?> sel = query.getSelectTable(x);
- if (sel != null) {
- if (query.isJoin()) {
- selectDot = sel.getAs() + ".";
- } else {
- String sn = sel.getAliasDefinition().schemaName;
- String tn = sel.getAliasDefinition().tableName;
- selectDot = query.getDb().getDialect().prepareTableName(sn, tn) + ".";
- }
- }
-
- for (int i = 0; i < fields.size(); i++) {
- if (i > 0) {
- stat.appendSQL(", ");
- }
- stat.appendSQL(selectDot);
- FieldDefinition def = fields.get(i);
- if (def.isPrimitive) {
- Object obj = def.getValue(x);
- Object alias = query.getPrimitiveAliasByValue(obj);
- query.appendSQL(stat, x, alias);
- } else {
- Object obj = def.getValue(x);
- query.appendSQL(stat, x, obj);
- }
- }
- }
+ /**
+ * The meta data of an index.
+ */
+
+ public static class IndexDefinition {
+ public IndexType type;
+ public String indexName;
+
+ public List<String> columnNames;
+ }
+
+ /**
+ * The meta data of a constraint on foreign key.
+ */
+
+ public static class ConstraintForeignKeyDefinition {
+
+ public String constraintName;
+ public List<String> foreignColumns;
+ public String referenceTable;
+ public List<String> referenceColumns;
+ public ConstraintDeleteType deleteType = ConstraintDeleteType.UNSET;
+ public ConstraintUpdateType updateType = ConstraintUpdateType.UNSET;
+ public ConstraintDeferrabilityType deferrabilityType = ConstraintDeferrabilityType.UNSET;
+ }
+
+ /**
+ * The meta data of a unique constraint.
+ */
+
+ public static class ConstraintUniqueDefinition {
+
+ public String constraintName;
+ public List<String> uniqueColumns;
+ }
+
+
+ /**
+ * The meta data of a field.
+ */
+
+ static class FieldDefinition {
+ String columnName;
+ Field field;
+ String dataType;
+ int length;
+ int scale;
+ boolean isPrimaryKey;
+ boolean isAutoIncrement;
+ boolean trim;
+ boolean nullable;
+ String defaultValue;
+ EnumType enumType;
+ Class<?> enumTypeClass;
+ boolean isPrimitive;
+ String constraint;
+ Class<? extends DataTypeAdapter<?>> typeAdapter;
+
+ Object getValue(Object obj) {
+ try {
+ return field.get(obj);
+ } catch (Exception e) {
+ throw new IciqlException(e);
+ }
+ }
+
+ private Object initWithNewObject(Object obj) {
+ Object o = Utils.newObject(field.getType());
+ setValue(obj, o);
+ return o;
+ }
+
+ private void setValue(Object obj, Object o) {
+ try {
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ }
+
+ if (field.getType().isPrimitive() && o == null) {
+ // do not attempt to set a primitive to null
+ return;
+ }
+
+ field.set(obj, o);
+ } catch (IciqlException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new IciqlException(e);
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return columnName.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof FieldDefinition) {
+ return o.hashCode() == hashCode();
+ }
+ return false;
+ }
+ }
+
+ public ArrayList<FieldDefinition> fields = Utils.newArrayList();
+ String schemaName;
+ String tableName;
+ String viewTableName;
+ int tableVersion;
+ List<String> primaryKeyColumnNames;
+ boolean memoryTable;
+ boolean multiplePrimitiveBools;
+
+ private boolean createIfRequired = true;
+ private Class<T> clazz;
+ private IdentityHashMap<Object, FieldDefinition> fieldMap = Utils.newIdentityHashMap();
+ private ArrayList<IndexDefinition> indexes = Utils.newArrayList();
+ ArrayList<ConstraintForeignKeyDefinition> constraintsForeignKey = Utils.newArrayList();
+ ArrayList<ConstraintUniqueDefinition> constraintsUnique = Utils.newArrayList();
+
+ TableDefinition(Class<T> clazz) {
+ this.clazz = clazz;
+ schemaName = null;
+ tableName = clazz.getSimpleName();
+ }
+
+ Class<T> getModelClass() {
+ return clazz;
+ }
+
+ List<FieldDefinition> getFields() {
+ return fields;
+ }
+
+ void defineSchemaName(String schemaName) {
+ this.schemaName = schemaName;
+ }
+
+ void defineTableName(String tableName) {
+ this.tableName = tableName;
+ }
+
+ void defineViewTableName(String viewTableName) {
+ this.viewTableName = viewTableName;
+ }
+
+ void defineMemoryTable() {
+ this.memoryTable = true;
+ }
+
+ void defineSkipCreate() {
+ this.createIfRequired = false;
+ }
+
+ /**
+ * Define a primary key by the specified model fields.
+ *
+ * @param modelFields the ordered list of model fields
+ */
+ void definePrimaryKey(Object[] modelFields) {
+ List<String> columnNames = mapColumnNames(modelFields);
+ setPrimaryKey(columnNames);
+ }
+
+ /**
+ * Define a primary key by the specified column names.
+ *
+ * @param columnNames the ordered list of column names
+ */
+ private void setPrimaryKey(List<String> columnNames) {
+ primaryKeyColumnNames = Utils.newArrayList(columnNames);
+ List<String> pkNames = Utils.newArrayList();
+ for (String name : columnNames) {
+ pkNames.add(name.toLowerCase());
+ }
+ // set isPrimaryKey flag for all field definitions
+ for (FieldDefinition fieldDefinition : fieldMap.values()) {
+ fieldDefinition.isPrimaryKey = pkNames.contains(fieldDefinition.columnName.toLowerCase());
+ }
+ }
+
+ private <A> String getColumnName(A fieldObject) {
+ FieldDefinition def = fieldMap.get(fieldObject);
+ return def == null ? null : def.columnName;
+ }
+
+ private ArrayList<String> mapColumnNames(Object[] columns) {
+ ArrayList<String> columnNames = Utils.newArrayList();
+ for (Object column : columns) {
+ columnNames.add(getColumnName(column));
+ }
+ return columnNames;
+ }
+
+ /**
+ * Defines an index with the specified model fields.
+ *
+ * @param name the index name (optional)
+ * @param type the index type (STANDARD, HASH, UNIQUE, UNIQUE_HASH)
+ * @param modelFields the ordered list of model fields
+ */
+ void defineIndex(String name, IndexType type, Object[] modelFields) {
+ List<String> columnNames = mapColumnNames(modelFields);
+ addIndex(name, type, columnNames);
+ }
+
+ /**
+ * Defines an index with the specified column names.
+ *
+ * @param type the index type (STANDARD, HASH, UNIQUE, UNIQUE_HASH)
+ * @param columnNames the ordered list of column names
+ */
+ private void addIndex(String name, IndexType type, List<String> columnNames) {
+ IndexDefinition index = new IndexDefinition();
+ if (StringUtils.isNullOrEmpty(name)) {
+ index.indexName = tableName + "_idx_" + indexes.size();
+ } else {
+ index.indexName = name;
+ }
+ index.columnNames = Utils.newArrayList(columnNames);
+ index.type = type;
+ indexes.add(index);
+ }
+
+ /**
+ * Defines an unique constraint with the specified model fields.
+ *
+ * @param name the constraint name (optional)
+ * @param modelFields the ordered list of model fields
+ */
+ void defineConstraintUnique(String name, Object[] modelFields) {
+ List<String> columnNames = mapColumnNames(modelFields);
+ addConstraintUnique(name, columnNames);
+ }
+
+ /**
+ * Defines an unique constraint.
+ *
+ * @param name
+ * @param columnNames
+ */
+ private void addConstraintUnique(String name, List<String> columnNames) {
+ ConstraintUniqueDefinition constraint = new ConstraintUniqueDefinition();
+ if (StringUtils.isNullOrEmpty(name)) {
+ constraint.constraintName = tableName + "_unique_" + constraintsUnique.size();
+ } else {
+ constraint.constraintName = name;
+ }
+ constraint.uniqueColumns = Utils.newArrayList(columnNames);
+ constraintsUnique.add(constraint);
+ }
+
+ /**
+ * Defines a foreign key constraint with the specified model fields.
+ *
+ * @param name the constraint name (optional)
+ * @param modelFields the ordered list of model fields
+ */
+ void defineForeignKey(String name, Object[] modelFields, String refTableName, Object[] refModelFields,
+ ConstraintDeleteType deleteType, ConstraintUpdateType updateType,
+ ConstraintDeferrabilityType deferrabilityType) {
+ List<String> columnNames = mapColumnNames(modelFields);
+ List<String> referenceColumnNames = mapColumnNames(refModelFields);
+ addConstraintForeignKey(name, columnNames, refTableName, referenceColumnNames,
+ deleteType, updateType, deferrabilityType);
+ }
+
+ void defineColumnName(Object column, String columnName) {
+ FieldDefinition def = fieldMap.get(column);
+ if (def != null) {
+ def.columnName = columnName;
+ }
+ }
+
+ void defineAutoIncrement(Object column) {
+ FieldDefinition def = fieldMap.get(column);
+ if (def != null) {
+ def.isAutoIncrement = true;
+ }
+ }
+
+ void defineLength(Object column, int length) {
+ FieldDefinition def = fieldMap.get(column);
+ if (def != null) {
+ def.length = length;
+ }
+ }
+
+ void defineScale(Object column, int scale) {
+ FieldDefinition def = fieldMap.get(column);
+ if (def != null) {
+ def.scale = scale;
+ }
+ }
+
+ void defineTrim(Object column) {
+ FieldDefinition def = fieldMap.get(column);
+ if (def != null) {
+ def.trim = true;
+ }
+ }
+
+ void defineNullable(Object column, boolean isNullable) {
+ FieldDefinition def = fieldMap.get(column);
+ if (def != null) {
+ def.nullable = isNullable;
+ }
+ }
+
+ void defineDefaultValue(Object column, String defaultValue) {
+ FieldDefinition def = fieldMap.get(column);
+ if (def != null) {
+ def.defaultValue = defaultValue;
+ }
+ }
+
+ void defineConstraint(Object column, String constraint) {
+ FieldDefinition def = fieldMap.get(column);
+ if (def != null) {
+ def.constraint = constraint;
+ }
+ }
+
+ void defineTypeAdapter(Object column, Class<? extends DataTypeAdapter<?>> typeAdapter) {
+ FieldDefinition def = fieldMap.get(column);
+ if (def != null) {
+ def.typeAdapter = typeAdapter;
+ }
+ }
+
+ void mapFields(Db db) {
+ boolean byAnnotationsOnly = false;
+ boolean inheritColumns = false;
+ if (clazz.isAnnotationPresent(IQTable.class)) {
+ IQTable tableAnnotation = clazz.getAnnotation(IQTable.class);
+ byAnnotationsOnly = tableAnnotation.annotationsOnly();
+ inheritColumns = tableAnnotation.inheritColumns();
+ }
+
+ if (clazz.isAnnotationPresent(IQView.class)) {
+ IQView viewAnnotation = clazz.getAnnotation(IQView.class);
+ byAnnotationsOnly = viewAnnotation.annotationsOnly();
+ inheritColumns = viewAnnotation.inheritColumns();
+ }
+
+ List<Field> classFields = classFields(inheritColumns);
+
+ Set<FieldDefinition> uniqueFields = new LinkedHashSet<FieldDefinition>();
+ T defaultObject = Db.instance(clazz);
+ for (Field f : classFields) {
+ // check if we should skip this field
+ if (f.isAnnotationPresent(IQIgnore.class)) {
+ continue;
+ }
+
+ // default to field name
+ String columnName = f.getName();
+ boolean isAutoIncrement = false;
+ boolean isPrimaryKey = false;
+ int length = 0;
+ int scale = 0;
+ boolean trim = false;
+ boolean nullable = !f.getType().isPrimitive();
+ String defaultValue = "";
+ String constraint = "";
+ String dataType = null;
+ Class<? extends DataTypeAdapter<?>> typeAdapter = null;
+
+ // configure Java -> SQL enum mapping
+ EnumType enumType = Utils.getEnumType(f);
+ Class<?> enumTypeClass = Utils.getEnumTypeClass(f);
+
+ // try using default object
+ try {
+ f.setAccessible(true);
+ Object value = f.get(defaultObject);
+ if (value != null) {
+ if (value.getClass().isEnum()) {
+ // enum default, convert to target type
+ Enum<?> anEnum = (Enum<?>) value;
+ Object o = Utils.convertEnum(anEnum, enumType);
+ defaultValue = ModelUtils.formatDefaultValue(o);
+ } else {
+ // object default
+ defaultValue = ModelUtils.formatDefaultValue(value);
+ }
+ }
+ } catch (IllegalAccessException e) {
+ throw new IciqlException(e, "failed to get default object for {0}", columnName);
+ }
+
+ // identify the type adapter
+ typeAdapter = Utils.getDataTypeAdapter(f.getAnnotations());
+ if (typeAdapter == null) {
+ typeAdapter = Utils.getDataTypeAdapter(f.getType().getAnnotations());
+ }
+
+ if (typeAdapter != null) {
+ DataTypeAdapter<?> dtt = db.getDialect().getAdapter(typeAdapter);
+ dataType = dtt.getDataType();
+ }
+
+ boolean hasAnnotation = f.isAnnotationPresent(IQColumn.class);
+ if (hasAnnotation) {
+ IQColumn col = f.getAnnotation(IQColumn.class);
+ if (!StringUtils.isNullOrEmpty(col.name())) {
+ columnName = col.name();
+ }
+ isAutoIncrement = col.autoIncrement();
+ isPrimaryKey = col.primaryKey();
+ length = col.length();
+ scale = col.scale();
+ trim = col.trim();
+ nullable = col.nullable();
+
+ // annotation overrides
+ if (!StringUtils.isNullOrEmpty(col.defaultValue())) {
+ defaultValue = col.defaultValue();
+ }
+ }
+
+ boolean hasConstraint = f.isAnnotationPresent(IQConstraint.class);
+ if (hasConstraint) {
+ IQConstraint con = f.getAnnotation(IQConstraint.class);
+ // annotation overrides
+ if (!StringUtils.isNullOrEmpty(con.value())) {
+ constraint = con.value();
+ }
+ }
+
+ boolean reflectiveMatch = !byAnnotationsOnly;
+ if (reflectiveMatch || hasAnnotation || hasConstraint) {
+ FieldDefinition fieldDef = new FieldDefinition();
+ fieldDef.isPrimitive = f.getType().isPrimitive();
+ fieldDef.field = f;
+ fieldDef.columnName = columnName;
+ fieldDef.isAutoIncrement = isAutoIncrement;
+ fieldDef.isPrimaryKey = isPrimaryKey;
+ fieldDef.length = length;
+ fieldDef.scale = scale;
+ fieldDef.trim = trim;
+ fieldDef.nullable = nullable;
+ fieldDef.defaultValue = defaultValue;
+ fieldDef.enumType = enumType;
+ fieldDef.enumTypeClass = enumTypeClass;
+ fieldDef.dataType = StringUtils.isNullOrEmpty(dataType) ? ModelUtils.getDataType(fieldDef) : dataType;
+ fieldDef.typeAdapter = typeAdapter;
+ fieldDef.constraint = constraint;
+ uniqueFields.add(fieldDef);
+ }
+ }
+ fields.addAll(uniqueFields);
+
+ List<String> primaryKey = Utils.newArrayList();
+ int primitiveBoolean = 0;
+ for (FieldDefinition fieldDef : fields) {
+ if (fieldDef.isPrimaryKey) {
+ primaryKey.add(fieldDef.columnName);
+ }
+ if (fieldDef.isPrimitive && fieldDef.field.getType().equals(boolean.class)) {
+ primitiveBoolean++;
+ }
+ }
+ if (primitiveBoolean > 1) {
+ multiplePrimitiveBools = true;
+ IciqlLogger
+ .warn("Model {0} has multiple primitive booleans! Possible where,set,join clause problem!", tableName);
+ }
+ if (primaryKey.size() > 0) {
+ setPrimaryKey(primaryKey);
+ }
+ }
+
+ private List<Field> classFields(boolean inheritColumns) {
+ List<Field> classFields = Utils.newArrayList();
+ classFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
+ Class<?> superClass = clazz;
+ while (inheritColumns) {
+ superClass = superClass.getSuperclass();
+ classFields.addAll(Arrays.asList(superClass.getDeclaredFields()));
+
+ if (superClass.isAnnotationPresent(IQView.class)) {
+ IQView superView = superClass.getAnnotation(IQView.class);
+ inheritColumns = superView.inheritColumns();
+ } else if (superClass.isAnnotationPresent(IQTable.class)) {
+ IQTable superTable = superClass.getAnnotation(IQTable.class);
+ inheritColumns = superTable.inheritColumns();
+ } else {
+ inheritColumns = false;
+ }
+ }
+ return classFields;
+ }
+
+ void checkMultipleBooleans() {
+ if (multiplePrimitiveBools) {
+ throw new IciqlException(
+ "Can not explicitly reference a primitive boolean if there are multiple boolean fields in your model class!");
+ }
+ }
+
+ void checkMultipleEnums(Object o) {
+ if (o == null) {
+ return;
+ }
+ Class<?> clazz = o.getClass();
+ if (!clazz.isEnum()) {
+ return;
+ }
+
+ int fieldCount = 0;
+ for (FieldDefinition fieldDef : fields) {
+ Class<?> targetType = fieldDef.field.getType();
+ if (clazz.equals(targetType)) {
+ fieldCount++;
+ }
+ }
+
+ if (fieldCount > 1) {
+ throw new IciqlException(
+ "Can not explicitly reference {0} because there are {1} {0} fields in your model class!",
+ clazz.getSimpleName(), fieldCount);
+ }
+ }
+
+ /**
+ * Optionally truncates strings to the maximum length and converts
+ * java.lang.Enum types to Strings or Integers.
+ */
+ Object getValue(Object obj, FieldDefinition field) {
+ Object value = field.getValue(obj);
+ if (value == null) {
+ return value;
+ }
+ if (field.enumType != null) {
+ // convert enumeration to INT or STRING
+ Enum<?> iqenum = (Enum<?>) value;
+ switch (field.enumType) {
+ case NAME:
+ if (field.trim && field.length > 0) {
+ if (iqenum.name().length() > field.length) {
+ return iqenum.name().substring(0, field.length);
+ }
+ }
+ return iqenum.name();
+ case ORDINAL:
+ return iqenum.ordinal();
+ case ENUMID:
+ if (!EnumId.class.isAssignableFrom(value.getClass())) {
+ throw new IciqlException(field.field.getName() + " does not implement EnumId!");
+ }
+ EnumId<?> enumid = (EnumId<?>) value;
+ return enumid.enumId();
+ }
+ }
+
+ if (field.trim && field.length > 0) {
+ if (value instanceof String) {
+ // clip strings
+ String s = (String) value;
+ if (s.length() > field.length) {
+ return s.substring(0, field.length);
+ }
+ return s;
+ }
+ return value;
+ }
+
+ // return the value unchanged
+ return value;
+ }
+
+ PreparedStatement createInsertStatement(Db db, Object obj, boolean returnKey) {
+ SQLStatement stat = new SQLStatement(db);
+ StatementBuilder buff = new StatementBuilder("INSERT INTO ");
+ buff.append(db.getDialect().prepareTableName(schemaName, tableName)).append('(');
+ for (FieldDefinition field : fields) {
+ if (skipInsertField(field, obj)) {
+ continue;
+ }
+ buff.appendExceptFirst(", ");
+ buff.append(db.getDialect().prepareColumnName(field.columnName));
+ }
+ buff.append(") VALUES(");
+ buff.resetCount();
+ for (FieldDefinition field : fields) {
+ if (skipInsertField(field, obj)) {
+ continue;
+ }
+ buff.appendExceptFirst(", ");
+ buff.append('?');
+ Object value = getValue(obj, field);
+ if (value == null) {
+ 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<Object> query = Query.from(db, alias);
+ boolean firstCondition = true;
+ for (FieldDefinition field : fields) {
+ if (field.isPrimaryKey) {
+ Object fieldAlias = field.getValue(alias);
+ Object value = field.getValue(obj);
+ if (field.isPrimitive) {
+ fieldAlias = query.getPrimitiveAliasByValue(fieldAlias);
+ }
+ if (!firstCondition) {
+ query.addConditionToken(ConditionAndOr.AND);
+ }
+ firstCondition = false;
+ query.addConditionToken(new Condition<Object>(fieldAlias, value, CompareType.EQUAL));
+ }
+ }
+ stat.setSQL(buff.toString());
+ query.appendWhere(stat);
+ IciqlLogger.update(stat.getSQL());
+ return stat.executeUpdate();
+ }
+
+ int delete(Db db, Object obj) {
+ if (!StringUtils.isNullOrEmpty(viewTableName)) {
+ throw new IciqlException("Iciql does not support deleting rows from views!");
+ }
+ if (primaryKeyColumnNames == null || primaryKeyColumnNames.size() == 0) {
+ throw new IllegalStateException("No primary key columns defined for table " + obj.getClass()
+ + " - no update possible");
+ }
+ SQLStatement stat = new SQLStatement(db);
+ StatementBuilder buff = new StatementBuilder("DELETE FROM ");
+ buff.append(db.getDialect().prepareTableName(schemaName, tableName));
+ buff.resetCount();
+ Object alias = Utils.newObject(obj.getClass());
+ Query<Object> query = Query.from(db, alias);
+ boolean firstCondition = true;
+ for (FieldDefinition field : fields) {
+ if (field.isPrimaryKey) {
+ Object fieldAlias = field.getValue(alias);
+ Object value = field.getValue(obj);
+ if (field.isPrimitive) {
+ fieldAlias = query.getPrimitiveAliasByValue(fieldAlias);
+ }
+ if (!firstCondition) {
+ query.addConditionToken(ConditionAndOr.AND);
+ }
+ firstCondition = false;
+ query.addConditionToken(new Condition<Object>(fieldAlias, value, CompareType.EQUAL));
+ }
+ }
+ stat.setSQL(buff.toString());
+ query.appendWhere(stat);
+ IciqlLogger.delete(stat.getSQL());
+ return stat.executeUpdate();
+ }
+
+ TableDefinition<T> createIfRequired(Db db) {
+ // globally enable/disable check of create if required
+ if (db.getSkipCreate()) {
+ return this;
+ }
+ if (!createIfRequired) {
+ // skip table and index creation
+ // but still check for upgrades
+ db.upgradeTable(this);
+ return this;
+ }
+ if (db.hasCreated(clazz)) {
+ return this;
+ }
+ SQLStatement stat = new SQLStatement(db);
+ if (StringUtils.isNullOrEmpty(viewTableName)) {
+ db.getDialect().prepareCreateTable(stat, this);
+ } else {
+ db.getDialect().prepareCreateView(stat, this);
+ }
+ IciqlLogger.create(stat.getSQL());
+ try {
+ stat.executeUpdate();
+ } catch (IciqlException e) {
+ if (e.getIciqlCode() != IciqlException.CODE_OBJECT_ALREADY_EXISTS) {
+ throw e;
+ }
+ }
+
+ // create indexes
+ for (IndexDefinition index : indexes) {
+ stat = new SQLStatement(db);
+ db.getDialect().prepareCreateIndex(stat, schemaName, tableName, index);
+ IciqlLogger.create(stat.getSQL());
+ try {
+ stat.executeUpdate();
+ } catch (IciqlException e) {
+ if (e.getIciqlCode() != IciqlException.CODE_OBJECT_ALREADY_EXISTS
+ && e.getIciqlCode() != IciqlException.CODE_DUPLICATE_KEY) {
+ throw e;
+ }
+ }
+ }
+
+ // tables are created using IF NOT EXISTS
+ // but we may still need to upgrade
+ db.upgradeTable(this);
+ return this;
+ }
+
+ void mapObject(Object obj) {
+ fieldMap.clear();
+ initObject(obj, fieldMap);
+
+ if (clazz.isAnnotationPresent(IQSchema.class)) {
+ IQSchema schemaAnnotation = clazz.getAnnotation(IQSchema.class);
+ // setup schema name mapping, if properly annotated
+ if (!StringUtils.isNullOrEmpty(schemaAnnotation.value())) {
+ schemaName = schemaAnnotation.value();
+ }
+ }
+
+ if (clazz.isAnnotationPresent(IQTable.class)) {
+ IQTable tableAnnotation = clazz.getAnnotation(IQTable.class);
+
+ // setup table name mapping, if properly annotated
+ if (!StringUtils.isNullOrEmpty(tableAnnotation.name())) {
+ tableName = tableAnnotation.name();
+ }
+
+ // allow control over createTableIfRequired()
+ createIfRequired = tableAnnotation.create();
+
+ // model version
+ if (clazz.isAnnotationPresent(IQVersion.class)) {
+ IQVersion versionAnnotation = clazz.getAnnotation(IQVersion.class);
+ if (versionAnnotation.value() > 0) {
+ tableVersion = versionAnnotation.value();
+ }
+ }
+
+ // setup the primary index, if properly annotated
+ if (tableAnnotation.primaryKey().length > 0) {
+ List<String> primaryKey = Utils.newArrayList();
+ primaryKey.addAll(Arrays.asList(tableAnnotation.primaryKey()));
+ setPrimaryKey(primaryKey);
+ }
+ }
+
+ if (clazz.isAnnotationPresent(IQView.class)) {
+ IQView viewAnnotation = clazz.getAnnotation(IQView.class);
+
+ // setup view name mapping, if properly annotated
+ // set this as the table name so it fits in seemlessly with iciql
+ if (!StringUtils.isNullOrEmpty(viewAnnotation.name())) {
+ tableName = viewAnnotation.name();
+ } else {
+ tableName = clazz.getSimpleName();
+ }
+
+ // setup source table name mapping, if properly annotated
+ if (!StringUtils.isNullOrEmpty(viewAnnotation.tableName())) {
+ viewTableName = viewAnnotation.tableName();
+ } else {
+ // check for IQTable annotation on super class
+ Class<?> superClass = clazz.getSuperclass();
+ if (superClass.isAnnotationPresent(IQTable.class)) {
+ IQTable table = superClass.getAnnotation(IQTable.class);
+ if (StringUtils.isNullOrEmpty(table.name())) {
+ // super.SimpleClassName
+ viewTableName = superClass.getSimpleName();
+ } else {
+ // super.IQTable.name()
+ viewTableName = table.name();
+ }
+ } else if (superClass.isAnnotationPresent(IQView.class)) {
+ // super class is a view
+ IQView parentView = superClass.getAnnotation(IQView.class);
+ if (StringUtils.isNullOrEmpty(parentView.tableName())) {
+ // parent view does not define a tableName, must be inherited
+ Class<?> superParent = superClass.getSuperclass();
+ if (superParent != null && superParent.isAnnotationPresent(IQTable.class)) {
+ IQTable superParentTable = superParent.getAnnotation(IQTable.class);
+ if (StringUtils.isNullOrEmpty(superParentTable.name())) {
+ // super.super.SimpleClassName
+ viewTableName = superParent.getSimpleName();
+ } else {
+ // super.super.IQTable.name()
+ viewTableName = superParentTable.name();
+ }
+ }
+ } else {
+ // super.IQView.tableName()
+ viewTableName = parentView.tableName();
+ }
+ }
+
+ if (StringUtils.isNullOrEmpty(viewTableName)) {
+ // still missing view table name
+ throw new IciqlException("View model class \"{0}\" is missing a table name!", tableName);
+ }
+ }
+
+ // allow control over createTableIfRequired()
+ createIfRequired = viewAnnotation.create();
+ }
+
+ if (clazz.isAnnotationPresent(IQIndex.class)) {
+ // single table index
+ IQIndex index = clazz.getAnnotation(IQIndex.class);
+ addIndex(index);
+ }
+
+ if (clazz.isAnnotationPresent(IQIndexes.class)) {
+ // multiple table indexes
+ IQIndexes indexes = clazz.getAnnotation(IQIndexes.class);
+ for (IQIndex index : indexes.value()) {
+ addIndex(index);
+ }
+ }
+
+ if (clazz.isAnnotationPresent(IQContraintUnique.class)) {
+ // single table unique constraint
+ IQContraintUnique constraint = clazz.getAnnotation(IQContraintUnique.class);
+ addConstraintUnique(constraint);
+ }
+
+ if (clazz.isAnnotationPresent(IQContraintsUnique.class)) {
+ // multiple table unique constraints
+ IQContraintsUnique constraints = clazz.getAnnotation(IQContraintsUnique.class);
+ for (IQContraintUnique constraint : constraints.value()) {
+ addConstraintUnique(constraint);
+ }
+ }
+
+ if (clazz.isAnnotationPresent(IQContraintForeignKey.class)) {
+ // single table constraint
+ IQContraintForeignKey constraint = clazz.getAnnotation(IQContraintForeignKey.class);
+ addConstraintForeignKey(constraint);
+ }
+
+ if (clazz.isAnnotationPresent(IQContraintsForeignKey.class)) {
+ // multiple table constraints
+ IQContraintsForeignKey constraints = clazz.getAnnotation(IQContraintsForeignKey.class);
+ for (IQContraintForeignKey constraint : constraints.value()) {
+ addConstraintForeignKey(constraint);
+ }
+ }
+
+ }
+
+ private void addConstraintForeignKey(IQContraintForeignKey constraint) {
+ List<String> foreignColumns = Arrays.asList(constraint.foreignColumns());
+ List<String> referenceColumns = Arrays.asList(constraint.referenceColumns());
+ addConstraintForeignKey(constraint.name(), foreignColumns, constraint.referenceName(), referenceColumns, constraint.deleteType(), constraint.updateType(), constraint.deferrabilityType());
+ }
+
+ private void addConstraintUnique(IQContraintUnique constraint) {
+ List<String> uniqueColumns = Arrays.asList(constraint.uniqueColumns());
+ addConstraintUnique(constraint.name(), uniqueColumns);
+ }
+
+ /**
+ * Defines a foreign key constraint with the specified parameters.
+ *
+ * @param name name of the constraint
+ * @param foreignColumns list of columns declared as foreign
+ * @param referenceName reference table name
+ * @param referenceColumns list of columns used in reference table
+ * @param deleteType action on delete
+ * @param updateType action on update
+ * @param deferrabilityType deferrability mode
+ */
+ private void addConstraintForeignKey(String name,
+ List<String> foreignColumns, String referenceName,
+ List<String> referenceColumns, ConstraintDeleteType deleteType,
+ ConstraintUpdateType updateType, ConstraintDeferrabilityType deferrabilityType) {
+ ConstraintForeignKeyDefinition constraint = new ConstraintForeignKeyDefinition();
+ if (StringUtils.isNullOrEmpty(name)) {
+ constraint.constraintName = tableName + "_fkey_" + constraintsForeignKey.size();
+ } else {
+ constraint.constraintName = name;
+ }
+ constraint.foreignColumns = Utils.newArrayList(foreignColumns);
+ constraint.referenceColumns = Utils.newArrayList(referenceColumns);
+ constraint.referenceTable = referenceName;
+ constraint.deleteType = deleteType;
+ constraint.updateType = updateType;
+ constraint.deferrabilityType = deferrabilityType;
+ constraintsForeignKey.add(constraint);
+ }
+
+ private void addIndex(IQIndex index) {
+ List<String> columns = Arrays.asList(index.value());
+ addIndex(index.name(), index.type(), columns);
+ }
+
+ List<IndexDefinition> getIndexes() {
+ return indexes;
+ }
+
+ List<ConstraintUniqueDefinition> getContraintsUnique() {
+ return constraintsUnique;
+ }
+
+ List<ConstraintForeignKeyDefinition> getContraintsForeignKey() {
+ return constraintsForeignKey;
+ }
+
+ private void initObject(Object obj, Map<Object, FieldDefinition> map) {
+ for (FieldDefinition def : fields) {
+ Object newValue = def.initWithNewObject(obj);
+ map.put(newValue, def);
+ }
+ }
+
+ void initSelectObject(SelectTable<T> table, Object obj, Map<Object, SelectColumn<T>> map, boolean reuse) {
+ for (FieldDefinition def : fields) {
+ Object value;
+ if (!reuse) {
+ value = def.initWithNewObject(obj);
+ } else {
+ value = def.getValue(obj);
+ }
+ SelectColumn<T> column = new SelectColumn<T>(table, def);
+ map.put(value, column);
+ }
+ }
+
+ /**
+ * Most queries executed by iciql have named select lists (select alpha,
+ * beta where...) but sometimes a wildcard select is executed (select *).
+ * When a wildcard query is executed on a table that has more columns than
+ * are mapped in your model object, this creates a column mapping issue.
+ * JaQu assumed that you can always use the integer index of the
+ * reflectively mapped field definition to determine position in the result
+ * set.
+ * <p>
+ * This is not always true.
+ * <p>
+ * 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);
+ }
+ }
+
+ <Y, X> void appendSelectList(SQLStatement stat, Query<Y> query, X x) {
+ // select t0.col1, t0.col2, t0.col3...
+ // select table1.col1, table1.col2, table1.col3...
+ String selectDot = "";
+ SelectTable<?> sel = query.getSelectTable(x);
+ if (sel != null) {
+ if (query.isJoin()) {
+ selectDot = sel.getAs() + ".";
+ } else {
+ String sn = sel.getAliasDefinition().schemaName;
+ String tn = sel.getAliasDefinition().tableName;
+ selectDot = query.getDb().getDialect().prepareTableName(sn, tn) + ".";
+ }
+ }
+
+ for (int i = 0; i < fields.size(); i++) {
+ if (i > 0) {
+ stat.appendSQL(", ");
+ }
+ stat.appendSQL(selectDot);
+ FieldDefinition def = fields.get(i);
+ if (def.isPrimitive) {
+ Object obj = def.getValue(x);
+ Object alias = query.getPrimitiveAliasByValue(obj);
+ query.appendSQL(stat, x, alias);
+ } else {
+ Object obj = def.getValue(x);
+ query.appendSQL(stat, x, obj);
+ }
+ }
+ }
}
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<? extends java.util.Date> dateTimeClass;
- private List<String> primaryKeys = Utils.newArrayList();
- private Map<String, IndexInspector> indexes;
- private Map<String, ColumnInspector> columns;
- private final String eol = "\n";
-
- TableInspector(String schema, String table, Class<? extends java.util.Date> dateTimeClass) {
- this.schema = schema;
- this.table = table;
- this.dateTimeClass = dateTimeClass;
- }
-
- /**
- * Tests to see if this TableInspector represents schema.table.
- * <p>
- *
- * @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<? extends java.util.Date> dateTimeClass;
+ private List<String> primaryKeys = Utils.newArrayList();
+ private Map<String, IndexInspector> indexes;
+ private Map<String, ColumnInspector> columns;
+ private final String eol = "\n";
+
+ TableInspector(String schema, String table, Class<? extends java.util.Date> dateTimeClass) {
+ this.schema = schema;
+ this.table = table;
+ this.dateTimeClass = dateTimeClass;
+ }
+
+ /**
+ * Tests to see if this TableInspector represents schema.table.
+ * <p>
+ *
+ * @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.
- * <p>
- * 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.
- * <p>
- *
- * @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<String> 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<ColumnInspector> 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<String> sortedImports = new ArrayList<String>(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<String> 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
- */
- <T> List<ValidationRemark> validate(TableDefinition<T> def, boolean throwError) {
- List<ValidationRemark> 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 <T> void validate(List<ValidationRemark> remarks, TableDefinition<T> def, IndexInspector index,
- boolean throwError) {
- List<IndexDefinition> 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<ConstraintUniqueDefinition> defContraintsU = def.getContraintsUnique();
- List<ConstraintForeignKeyDefinition> 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<ValidationRemark> 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<String> columns = new ArrayList<String>();
-
- 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<ColumnInspector> {
- 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);
- }
-
- <T> 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.
+ * <p>
+ * 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.
+ * <p>
+ *
+ * @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<String> 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<ColumnInspector> 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<String> sortedImports = new ArrayList<String>(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<String> 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
+ */
+ <T> List<ValidationRemark> validate(TableDefinition<T> def, boolean throwError) {
+ List<ValidationRemark> 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 <T> void validate(List<ValidationRemark> remarks, TableDefinition<T> def, IndexInspector index,
+ boolean throwError) {
+ List<IndexDefinition> 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<ConstraintUniqueDefinition> defContraintsU = def.getContraintsUnique();
+ List<ConstraintForeignKeyDefinition> 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<ValidationRemark> 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<String> columns = new ArrayList<String>();
+
+ 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<ColumnInspector> {
+ 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);
+ }
+
+ <T> 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 <A>
- * the incomplete condition data type
+ *
+ * @param <A> the incomplete condition data type
*/
public class TestCondition<A> {
- 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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
+ */
- <T> void appendSQL(SQLStatement stat, Query<T> query);
+ <T> void appendSQL(SQLStatement stat, Query<T> 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 <T>
- * the query type
- * @param <A>
- * the new value data type
+ *
+ * @param <T> the query type
+ * @param <A> the new value data type
*/
public class UpdateColumnIncrement<T, A> implements UpdateColumn {
- private Query<T> query;
- private A x;
- private A y;
-
- UpdateColumnIncrement(Query<T> query, A x) {
- this.query = query;
- this.x = x;
- }
-
- public Query<T> 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<T> query;
+ private A x;
+ private A y;
+
+ UpdateColumnIncrement(Query<T> query, A x) {
+ this.query = query;
+ this.x = x;
+ }
+
+ public Query<T> 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 <T>
- * the query type
- * @param <A>
- * the new value data type
+ *
+ * @param <T> the query type
+ * @param <A> the new value data type
*/
public class UpdateColumnSet<T, A> implements UpdateColumn {
- private Query<T> query;
- private A x;
- private A y;
- private boolean isParameter;
-
- UpdateColumnSet(Query<T> query, A x) {
- this.query = query;
- this.x = x;
- }
-
- public Query<T> to(A y) {
- query.addUpdateColumnDeclaration(this);
- this.y = y;
- return query;
- }
-
- public Query<T> 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<T> query;
+ private A x;
+ private A y;
+ private boolean isParameter;
+
+ UpdateColumnSet(Query<T> query, A x) {
+ this.query = query;
+ this.x = x;
+ }
+
+ public Query<T> to(A y) {
+ query.addUpdateColumnDeclaration(this);
+ this.y = y;
+ return query;
+ }
+
+ public Query<T> 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;
* <p>
* You use this by creating a subclass which defines your object class.
* </p>
- *
+ * <p>
* <pre>
* public class CustomObjectAdapter extends GsonTypeAdapter&lt;CustomObject&gt; {
*
* public Class&lt;CustomObject&gt; getJavaType() {
* return CustomObject.class;
- * }
+ * }
* }
* </pre>
*
@@ -40,32 +40,32 @@ import com.iciql.Iciql.Mode;
*/
public abstract class GsonTypeAdapter<T> implements DataTypeAdapter<T> {
- 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.
* <p>You use this by creating a subclass which defines your object class.</p>
@@ -40,68 +40,69 @@ import com.iciql.IciqlException;
* }
* }
* </pre>
+ *
* @param <T>
*/
public abstract class JavaSerializationTypeAdapter<T> implements DataTypeAdapter<T> {
- 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<T> implements DataTypeAdapter<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<T> 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<T> 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<Object> {
- 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<Object> 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<Object> 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 <T>
+ * @author James Moger
*/
public abstract class JsonObjectAdapter<T> extends GsonTypeAdapter<T> {
- @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<String> {
- protected Mode mode;
-
- @Override
- public void setMode(Mode mode) {
- this.mode = mode;
- }
-
- @Override
- public String getDataType() {
- return "json";
- }
-
- @Override
- public Class<String> 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<String> 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 <T>
+ * @author James Moger
*/
public abstract class JsonbObjectAdapter<T> extends GsonTypeAdapter<T> {
- @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<String> {
- protected Mode mode;
-
- @Override
- public void setMode(Mode mode) {
- this.mode = mode;
- }
-
- @Override
- public String getDataType() {
- return "jsonb";
- }
-
- @Override
- public Class<String> 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<String> 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<String> {
- protected Mode mode;
-
- @Override
- public void setMode(Mode mode) {
- this.mode = mode;
- }
-
- @Override
- public String getDataType() {
- return "xml";
- }
-
- @Override
- public Class<String> 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<String> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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<Token> stack = new Stack<Token>();
- private ArrayList<Token> variables = new ArrayList<Token>();
- private boolean endOfMethod;
- private boolean condition;
- private int nextPc;
- private Map<String, Object> fieldMap = new HashMap<String, Object>();
+ private String convertMethodName;
+ private Token result;
+ private Stack<Token> stack = new Stack<Token>();
+ private ArrayList<Token> variables = new ArrayList<Token>();
+ private boolean endOfMethod;
+ private boolean condition;
+ private int nextPc;
+ private Map<String, Object> fieldMap = new HashMap<String, Object>();
- 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<String, Object> 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<String, Object> 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<Token> currentStack = new Stack<Token>();
- currentStack.addAll(stack);
- ArrayList<Token> currentVariables = new ArrayList<Token>();
- 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<Token> currentStack = new Stack<Token>();
+ currentStack.addAll(stack);
+ ArrayList<Token> currentVariables = new ArrayList<Token>();
+ 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 <T> void appendSQL(SQLStatement stat, Query<T> query) {
- stat.appendSQL(toString());
- }
+ public <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> query) {
- stat.appendSQL(StringUtils.quoteStringSQL(value));
- }
+ public <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> query) {
- // untested
- stat.appendSQL(name + "(");
- expr.appendSQL(stat, query);
- stat.appendSQL(")");
- }
+ public <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> query) {
- // untested
- stat.appendSQL("NULL");
- }
+ public <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> 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 <T> void appendSQL(SQLStatement stat, Query<T> query) {
- query.appendSQL(stat, null, obj);
- }
+ public <T> void appendSQL(SQLStatement stat, Query<T> 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.
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
-<title>Javadoc package documentation</title>
+<head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
+ <title>Javadoc package documentation</title>
</head>
<body>
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.
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
-<title>Javadoc package documentation</title>
+<head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
+ <title>Javadoc package documentation</title>
</head>
<body>
<i>iciql</i> (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<String> 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<String> 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 <string>");
- out.println(" -password <string>");
- out.println(" -schema <string>");
- out.println(" -table <string>");
- out.println(" -package <string>");
- out.println(" -folder <string>");
- out.println(" -annotateSchema <boolean>");
- out.println(" -trimStrings <boolean>");
- }
+ protected void showUsage() {
+ out.println("GenerateModels");
+ out.println("Usage:");
+ out.println();
+ out.println("(*) -url jdbc:h2:~test");
+ out.println(" -user <string>");
+ out.println(" -password <string>");
+ out.println(" -schema <string>");
+ out.println(" -table <string>");
+ out.println(" -package <string>");
+ out.println(" -folder <string>");
+ out.println(" -annotateSchema <boolean>");
+ out.println(" -trimStrings <boolean>");
+ }
}
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.<br>
* Statement logging is disabled by default.
* <p>
* 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<IciqlListener> 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<IciqlListener> 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<StatementType, Level> levels;
+ private final Map<StatementType, Level> levels;
- public Slf4jIciqlListener() {
- this(Level.TRACE);
- }
+ public Slf4jIciqlListener() {
+ this(Level.TRACE);
+ }
- public Slf4jIciqlListener(Level defaultLevel) {
- this.defaultLevel = defaultLevel;
- levels = new HashMap<StatementType, Level>();
- for (StatementType type : StatementType.values()) {
- levels.put(type, defaultLevel);
- }
- }
+ public Slf4jIciqlListener(Level defaultLevel) {
+ this.defaultLevel = defaultLevel;
+ levels = new HashMap<StatementType, Level>();
+ 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:
- *
+ * <p>
* <pre>
* StringBuilder buff = new StringBuilder();
* for (int i = 0; i &lt; args.length; i++) {
* if (i &gt; 0) {
* buff.append(&quot;, &quot;);
- * }
+ * }
* buff.append(args[i]);
* }
* </pre>
- *
+ * <p>
* to
- *
+ * <p>
* <pre>
* 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<String> 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<String> 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("&amp;");
- } else if (inStr.charAt(i) == '<') {
- retStr.append("&lt;");
- } else if (inStr.charAt(i) == '>') {
- retStr.append("&gt;");
- } else if (inStr.charAt(i) == '\"') {
- retStr.append("&quot;");
- } else if (changeSpace && inStr.charAt(i) == ' ') {
- retStr.append("&nbsp;");
- } else if (changeSpace && inStr.charAt(i) == '\t') {
- retStr.append(" &nbsp; &nbsp;");
- } 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("&amp;");
+ } else if (inStr.charAt(i) == '<') {
+ retStr.append("&lt;");
+ } else if (inStr.charAt(i) == '>') {
+ retStr.append("&gt;");
+ } else if (inStr.charAt(i) == '\"') {
+ retStr.append("&quot;");
+ } else if (changeSpace && inStr.charAt(i) == ' ') {
+ retStr.append("&nbsp;");
+ } else if (changeSpace && inStr.charAt(i) == '\t') {
+ retStr.append(" &nbsp; &nbsp;");
+ } 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", "<br/>").replace("\r", "<br/>").replace("\n", "<br/>");
- }
+ /**
+ * 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", "<br/>").replace("\r", "<br/>").replace("\n", "<br/>");
+ }
- /**
- * 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 <X> Class<X> getClass(X x) {
- return (Class<X>) x.getClass();
- }
-
- public static Class<?> loadClass(String className) {
- try {
- return Class.forName(className);
- } catch (Exception e) {
- throw new IciqlException(e);
- }
- }
-
- public static <T> Iterable<T> newArrayIterable(final T[] a) {
- return Arrays.asList(a);
- }
-
- public static <T> ArrayList<T> newArrayList() {
- return new ArrayList<T>();
- }
-
- public static <T> ArrayList<T> newArrayList(Collection<T> c) {
- return new ArrayList<T>(c);
- }
-
- public static <T> HashSet<T> newHashSet() {
- return new HashSet<T>();
- }
-
- public static <T> HashSet<T> newHashSet(Collection<T> list) {
- return new HashSet<T>(list);
- }
-
- public static <A, B> HashMap<A, B> newHashMap() {
- return new HashMap<A, B>();
- }
-
- public static <A, B> Map<A, B> newSynchronizedHashMap() {
- HashMap<A, B> map = newHashMap();
- return Collections.synchronizedMap(map);
- }
-
- public static <A, B> IdentityHashMap<A, B> newIdentityHashMap() {
- return new IdentityHashMap<A, B>();
- }
-
- public static <T> ThreadLocal<T> newThreadLocal(final Class<? extends T> clazz) {
- return new ThreadLocal<T>() {
- @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> T newObject(Class<T> 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 <T> boolean isSimpleType(Class<T> 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<? extends DataTypeAdapter<?>> getDataTypeAdapter(Annotation [] annotations) {
- Class<? extends DataTypeAdapter<?>> 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 <X> Class<X> getClass(X x) {
+ return (Class<X>) x.getClass();
+ }
+
+ public static Class<?> loadClass(String className) {
+ try {
+ return Class.forName(className);
+ } catch (Exception e) {
+ throw new IciqlException(e);
+ }
+ }
+
+ public static <T> Iterable<T> newArrayIterable(final T[] a) {
+ return Arrays.asList(a);
+ }
+
+ public static <T> ArrayList<T> newArrayList() {
+ return new ArrayList<T>();
+ }
+
+ public static <T> ArrayList<T> newArrayList(Collection<T> c) {
+ return new ArrayList<T>(c);
+ }
+
+ public static <T> HashSet<T> newHashSet() {
+ return new HashSet<T>();
+ }
+
+ public static <T> HashSet<T> newHashSet(Collection<T> list) {
+ return new HashSet<T>(list);
+ }
+
+ public static <A, B> HashMap<A, B> newHashMap() {
+ return new HashMap<A, B>();
+ }
+
+ public static <A, B> Map<A, B> newSynchronizedHashMap() {
+ HashMap<A, B> map = newHashMap();
+ return Collections.synchronizedMap(map);
+ }
+
+ public static <A, B> IdentityHashMap<A, B> newIdentityHashMap() {
+ return new IdentityHashMap<A, B>();
+ }
+
+ public static <T> ThreadLocal<T> newThreadLocal(final Class<? extends T> clazz) {
+ return new ThreadLocal<T>() {
+ @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> T newObject(Class<T> 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 <T> boolean isSimpleType(Class<T> 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<? extends DataTypeAdapter<?>> getDataTypeAdapter(Annotation[] annotations) {
+ Class<? extends DataTypeAdapter<?>> 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 <K>
- * the keys
- * @param <V>
- * the value
+ *
+ * @param <K> the keys
+ * @param <V> the value
*/
public class WeakIdentityHashMap<K, V> implements Map<K, V> {
- private static final int MAX_LOAD = 90;
- private static final WeakReference<Object> DELETED_KEY = new WeakReference<Object>(null);
- private int mask, len, size, deletedCount, level;
- private int maxSize, minSize, maxDeleted;
- private WeakReference<K>[] 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> k = keys[index];
- if (k == null) {
- // found an empty record
- if (deleted >= 0) {
- index = deleted;
- deletedCount--;
- }
- size++;
- keys[index] = new WeakReference<K>(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> 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<K>) DELETED_KEY;
- values[index] = null;
- deletedCount++;
- size--;
- }
-
- private void rehash(int newLevel) {
- WeakReference<K>[] oldKeys = keys;
- V[] oldValues = values;
- reset(newLevel);
- for (int i = 0; i < oldKeys.length; i++) {
- WeakReference<K> 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> 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<java.util.Map.Entry<K, V>> entrySet() {
- throw new UnsupportedOperationException();
- }
-
- public boolean isEmpty() {
- return size == 0;
- }
-
- public Set<K> keySet() {
- throw new UnsupportedOperationException();
- }
-
- public void putAll(Map<? extends K, ? extends V> m) {
- throw new UnsupportedOperationException();
- }
-
- public Collection<V> values() {
- throw new UnsupportedOperationException();
- }
+ private static final int MAX_LOAD = 90;
+ private static final WeakReference<Object> DELETED_KEY = new WeakReference<Object>(null);
+ private int mask, len, size, deletedCount, level;
+ private int maxSize, minSize, maxDeleted;
+ private WeakReference<K>[] 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> k = keys[index];
+ if (k == null) {
+ // found an empty record
+ if (deleted >= 0) {
+ index = deleted;
+ deletedCount--;
+ }
+ size++;
+ keys[index] = new WeakReference<K>(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> 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<K>) DELETED_KEY;
+ values[index] = null;
+ deletedCount++;
+ size--;
+ }
+
+ private void rehash(int newLevel) {
+ WeakReference<K>[] oldKeys = keys;
+ V[] oldValues = values;
+ reset(newLevel);
+ for (int i = 0; i < oldKeys.length; i++) {
+ WeakReference<K> 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> 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<java.util.Map.Entry<K, V>> entrySet() {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ public Set<K> keySet() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void putAll(Map<? extends K, ? extends V> m) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Collection<V> 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.
-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
-<head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
-<title>Javadoc package documentation</title>
+<head>
+ <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
+ <title>Javadoc package documentation</title>
</head>
<body>
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.
- *
+ * <p>
* <p>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).
- *
+ * <p>
* <p>Note that unlike the {@link java.io.Closeable#close close}
* method of {@link java.io.Closeable}, this {@code close} method
* is <em>not</em> 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.
- *
+ * <p>
* 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<String> 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<ProductInheritedAnnotation> inserted = ProductInheritedAnnotation.getData();
- db.insertAll(inserted);
-
- List<ProductInheritedAnnotation> 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<String> 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<ProductInheritedAnnotation> inserted = ProductInheritedAnnotation.getData();
+ db.insertAll(inserted);
+
+ List<ProductInheritedAnnotation> 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<BooleanAsIntModel> 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<BooleanModel> 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<BooleanAsPrimitiveShortModel> 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<BooleanAsIntModel> 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<BooleanModel> 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<BooleanAsPrimitiveShortModel> 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<StringRecord> 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<StringRecord> 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<Product> query1 = db.from(p);
- Query<Product> 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<Thread> 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<Thread> threads = Utils.newArrayList();
- final AtomicInteger failures = new AtomicInteger(0);
- final ThreadLocal<Product> 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<Product> 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<Product> query1 = db.from(p);
+ Query<Product> 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<Thread> 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<Thread> threads = Utils.newArrayList();
+ final AtomicInteger failures = new AtomicInteger(0);
+ final ThreadLocal<Product> 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<Product> 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<SupportedTypes> {
+ /**
+ * Maps a SupportedType instance to a BLOB using Java Object serialization.
+ */
+ public static class SupportedTypesAdapterImpl extends JavaSerializationTypeAdapter<SupportedTypes> {
- @Override
- public Class<SupportedTypes> getJavaType() {
- return SupportedTypes.class;
- }
+ @Override
+ public Class<SupportedTypes> 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<ValidationRemark> 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<ValidationRemark> 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<EnumModels> 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<EnumModels> 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<EnumModels> 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<EnumModels> 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<EnumModels> 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<EnumModels> 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.
- *
+ * <p>
* By default this test suite will run against the H2 database. You can change
* this by switching the DEFAULT_TEST_DB value.
* <p>
@@ -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<String, PoolableConnectionFactory> connectionFactories = Utils
- .newSynchronizedHashMap();
-
- private static Map<String, PoolingDataSource> 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<TestDb> 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<TestDb> {
- 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<String, PoolableConnectionFactory> connectionFactories = Utils
+ .newSynchronizedHashMap();
+
+ private static Map<String, PoolingDataSource> 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<TestDb> 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<TestDb> {
+ 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<UserNote> 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<UserId> 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<UserNote> 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<UserId> 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<UserId> 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<UserNote> 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<UserId> 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<UserNote> 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<ValidationRemark> 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<SupportedTypes> original = SupportedTypes.createList();
- db.insertAll(original);
- List<SupportedTypes> 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<SupportedTypes> original = SupportedTypes.createList();
- db.insertAll(original);
- DbInspector inspector = new DbInspector(db);
- List<String> 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<SupportedTypes> 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<SupportedTypes> original = SupportedTypes.createList();
- db.insertAll(original);
- List<String> myStrings = db.from(SupportedTypes.SAMPLE)
- .select(SupportedTypes.SAMPLE.myString);
- assertEquals(10, myStrings.size());
-
- List<Integer> 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<ValidationRemark> 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<SupportedTypes> original = SupportedTypes.createList();
+ db.insertAll(original);
+ List<SupportedTypes> 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<SupportedTypes> original = SupportedTypes.createList();
+ db.insertAll(original);
+ DbInspector inspector = new DbInspector(db);
+ List<String> 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<SupportedTypes> 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<SupportedTypes> original = SupportedTypes.createList();
+ db.insertAll(original);
+ List<String> myStrings = db.from(SupportedTypes.SAMPLE)
+ .select(SupportedTypes.SAMPLE.myString);
+ assertEquals(10, myStrings.size());
+
+ List<Integer> 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<Customer> 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<Customer>(db, model) {{
-
- and(model.customerId).is("0001");
- and(new Or<Customer>(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<Customer>(db, model) {{
-
- or(model.customerId).is("0001");
-
- or(new And<Customer>(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<Customer>(db, model) {{
-
- or(new And<Customer>(db, model) {{
- and(model.customerId).is("0001");
- and(model.region).is("WA");
- }});
-
- or(new And<Customer>(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<Customer>(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<Customer> 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<Customer>(db, model) {{
+
+ and(model.customerId).is("0001");
+ and(new Or<Customer>(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<Customer>(db, model) {{
+
+ or(model.customerId).is("0001");
+
+ or(new And<Customer>(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<Customer>(db, model) {{
+
+ or(new And<Customer>(db, model) {{
+ and(model.customerId).is("0001");
+ and(model.region).is("WA");
+ }});
+
+ or(new And<Customer>(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<Customer>(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<Customer>(db, model) {{
- or(model.region).is("LA");
- or(model.region).is("CA");
- or(model.region).is("WA");
- }})
+ .and(new Or<Customer>(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<Customer> matches = db.from(c)
- .where(c.customerId).like("A%")
- .and(c.region).isNotNull()
- .and(new Or<Customer>(db, c) {{
- or(c.region).is("LA");
- or(c.region).is("CA");
- }}).select();
+ @Test
+ public void compoundConditionsTest() {
+ final Customer c = new Customer();
+ List<Customer> matches = db.from(c)
+ .where(c.customerId).like("A%")
+ .and(c.region).isNotNull()
+ .and(new Or<Customer>(db, c) {{
+ or(c.region).is("LA");
+ or(c.region).is("CA");
+ }}).select();
- assertEquals(2, matches.size());
+ assertEquals(2, matches.size());
- Set<String> ids = new TreeSet<String>();
- for (Customer customer : matches) {
- ids.add(customer.customerId);
- }
- assertEquals("[ANTON, ASLAN]", ids.toString());
+ Set<String> ids = new TreeSet<String>();
+ 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<String>() {{
- 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<String>() {{
- 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<String>() {{
- 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<String>() {{
- this.add("a");
- this.add("b");
- }})
- .toSQL());
- }
-
- public void noneOfTest() {
- Customer c = new Customer();
- List<Customer> meAndny = db.from(c).where(c.region).noneOf("WA", "CA", "LA").select();
- assertEquals(2, meAndny.size());
-
- Set<String> regions = new TreeSet<String>();
- for (Customer customer : meAndny) {
- regions.add(customer.region);
- }
- assertEquals("[ME, NY]", regions.toString());
- }
-
- public void oneOfTest() {
- Customer c = new Customer();
- List<Customer> meAndny = db.from(c).where(c.region).oneOf("ME", "NY").select();
- assertEquals(2, meAndny.size());
-
- Set<String> regions = new TreeSet<String>();
- 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<String>() {{
+ 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<String>() {{
+ 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<String>() {{
+ 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<String>() {{
+ this.add("a");
+ this.add("b");
+ }})
+ .toSQL());
+ }
+
+ public void noneOfTest() {
+ Customer c = new Customer();
+ List<Customer> meAndny = db.from(c).where(c.region).noneOf("WA", "CA", "LA").select();
+ assertEquals(2, meAndny.size());
+
+ Set<String> regions = new TreeSet<String>();
+ for (Customer customer : meAndny) {
+ regions.add(customer.region);
+ }
+ assertEquals("[ME, NY]", regions.toString());
+ }
+
+ public void oneOfTest() {
+ Customer c = new Customer();
+ List<Customer> meAndny = db.from(c).where(c.region).oneOf("ME", "NY").select();
+ assertEquals(2, meAndny.size());
+
+ Set<String> regions = new TreeSet<String>();
+ 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<PrimitivesModel> 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<PrimitivesModel> 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<PrimitivesModel> models = PrimitivesModel.getList();
- Collections.reverse(models);
- // insert them in reverse order
- db.insertAll(models);
-
- PrimitivesModel p = new PrimitivesModel();
- List<Long> 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<PrimitivesModel> 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<PrimitivesModel> 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<PrimitivesModel> models = PrimitivesModel.getList();
+ Collections.reverse(models);
+ // insert them in reverse order
+ db.insertAll(models);
+
+ PrimitivesModel p = new PrimitivesModel();
+ List<Long> 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<Product> getWithIllegalCollection();
+ @SqlQuery("select * from Product")
+ List<Product> 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<Product> products = db.from(p).where(unitsInStock + "=?", 120).orderBy(p.productId).select();
- assertEquals(1, products.size());
+ Product p = new Product();
+ List<Product> 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<Product> 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<Product> 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<Product> 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<Product> 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<Product> soldOutProducts = db.from(p).where(p.unitsInStock).is(0).orderBy(p.productId).select();
- List<Product> 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<Product> 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<Customer> 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<String> productNames = db.from(p).orderBy(p.productId).select(p.productName);
-
- List<Product> 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<ProductPrice> productInfos = db.from(p).orderBy(p.productId).select(new ProductPrice() {
- {
- productName = p.productName;
- category = p.category;
- price = p.unitPrice;
- }
- });
-
- List<Product> 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<CustOrder> 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<Integer> 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<Product> 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<Integer> ids = new HashSet<Integer>();
- Product p = new Product();
- for (int i = 0; i < 5; i++) {
- List<Product> 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<SupportedTypes> list = SupportedTypes.createList();
- List<Long> keys = db.insertAllAndGetKeys(list);
- Set<Long> uniqueKeys = new HashSet<Long>();
- 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<ProductGroup> 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<Product> soldOutProducts = db.from(p).where(p.unitsInStock).is(0).orderBy(p.productId).select();
+ List<Product> 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<Product> 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<Customer> 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<String> productNames = db.from(p).orderBy(p.productId).select(p.productName);
+
+ List<Product> 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<ProductPrice> productInfos = db.from(p).orderBy(p.productId).select(new ProductPrice() {
+ {
+ productName = p.productName;
+ category = p.category;
+ price = p.unitPrice;
+ }
+ });
+
+ List<Product> 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<CustOrder> 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<Integer> 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<Product> 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<Integer> ids = new HashSet<Integer>();
+ Product p = new Product();
+ for (int i = 0; i < 5; i++) {
+ List<Product> 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<SupportedTypes> list = SupportedTypes.createList();
+ List<Long> keys = db.insertAllAndGetKeys(list);
+ Set<Long> uniqueKeys = new HashSet<Long>();
+ 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<ProductGroup> 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<UUIDRecord> originals = UUIDRecord.getList();
- db.insertAll(originals);
- UUIDRecord u = new UUIDRecord();
- List<UUIDRecord> 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<UUIDRecord> getList() {
- List<UUIDRecord> list = new ArrayList<UUIDRecord>();
- 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<UUIDRecord> originals = UUIDRecord.getList();
+ db.insertAll(originals);
+ UUIDRecord u = new UUIDRecord();
+ List<UUIDRecord> 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<UUIDRecord> getList() {
+ List<UUIDRecord> list = new ArrayList<UUIDRecord>();
+ 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<Product> 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<Product> 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<SupportedTypes> 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<SupportedTypes2> 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<Product> 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<Product> 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<SupportedTypes> 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<SupportedTypes2> 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<ProductView> products = db.from(view).select();
- assertEquals(5, products.size());
- for (int i = 0; i < products.size(); i++) {
- assertEquals(3 + i, products.get(i).productId.intValue());
- }
- }
-
- @Test
- public void testProductViewInherited() {
- ProductViewInherited view = new ProductViewInherited();
- List<ProductViewInherited> products = db.from(view).select();
- assertEquals(5, products.size());
- for (int i = 0; i < products.size(); i++) {
- assertEquals(3 + i, products.get(i).productId.intValue());
- }
- }
-
- @Test
- public void testComplexInheritance() {
- ProductViewInheritedComplex view = new ProductViewInheritedComplex();
- List<ProductViewInheritedComplex> products = db.from(view).select();
- assertEquals(5, products.size());
- for (int i = 0; i < products.size(); i++) {
- assertEquals(3 + i, products.get(i).productId.intValue());
- assertTrue(!StringUtils.isNullOrEmpty(products.get(i).productName));
- }
- }
-
- @Test
- public void testCreateViewFromQuery() {
- // create view from query
- ProductAnnotationOnly product = new ProductAnnotationOnly();
- db.from(product).where(product.productId).exceeds(2L).and(product.productId).atMost(7L).createView(ProductViewFromQuery.class);
-
- // select from the created view
- ProductViewFromQuery view = new ProductViewFromQuery();
- List<ProductViewFromQuery> products = db.from(view).select();
- assertEquals(5, products.size());
- for (int i = 0; i < products.size(); i++) {
- assertEquals(3 + i, products.get(i).productId.intValue());
- }
-
- // replace the view
- db.from(product).where(product.productId).exceeds(3L).and(product.productId).atMost(8L).replaceView(ProductViewFromQuery.class);
-
- // select from the replaced view
- products = db.from(view).select();
- assertEquals(5, products.size());
- for (int i = 0; i < products.size(); i++) {
- assertEquals(4 + i, products.get(i).productId.intValue());
- }
- }
+ /**
+ * This object represents a database (actually a connection to the
+ * database).
+ */
+
+ private Db db;
+
+ @Before
+ public void setUp() {
+ db = IciqlSuite.openNewDb();
+ db.insertAll(ProductAnnotationOnly.getList());
+ }
+
+ @After
+ public void tearDown() {
+ db.close();
+ }
+
+ @Test
+ public void testProductView() {
+ ProductView view = new ProductView();
+ List<ProductView> products = db.from(view).select();
+ assertEquals(5, products.size());
+ for (int i = 0; i < products.size(); i++) {
+ assertEquals(3 + i, products.get(i).productId.intValue());
+ }
+ }
+
+ @Test
+ public void testProductViewInherited() {
+ ProductViewInherited view = new ProductViewInherited();
+ List<ProductViewInherited> products = db.from(view).select();
+ assertEquals(5, products.size());
+ for (int i = 0; i < products.size(); i++) {
+ assertEquals(3 + i, products.get(i).productId.intValue());
+ }
+ }
+
+ @Test
+ public void testComplexInheritance() {
+ ProductViewInheritedComplex view = new ProductViewInheritedComplex();
+ List<ProductViewInheritedComplex> products = db.from(view).select();
+ assertEquals(5, products.size());
+ for (int i = 0; i < products.size(); i++) {
+ assertEquals(3 + i, products.get(i).productId.intValue());
+ assertTrue(!StringUtils.isNullOrEmpty(products.get(i).productName));
+ }
+ }
+
+ @Test
+ public void testCreateViewFromQuery() {
+ // create view from query
+ ProductAnnotationOnly product = new ProductAnnotationOnly();
+ db.from(product).where(product.productId).exceeds(2L).and(product.productId).atMost(7L).createView(ProductViewFromQuery.class);
+
+ // select from the created view
+ ProductViewFromQuery view = new ProductViewFromQuery();
+ List<ProductViewFromQuery> products = db.from(view).select();
+ assertEquals(5, products.size());
+ for (int i = 0; i < products.size(); i++) {
+ assertEquals(3 + i, products.get(i).productId.intValue());
+ }
+
+ // replace the view
+ db.from(product).where(product.productId).exceeds(3L).and(product.productId).atMost(8L).replaceView(ProductViewFromQuery.class);
+
+ // select from the replaced view
+ products = db.from(view).select();
+ assertEquals(5, products.size());
+ for (int i = 0; i < products.size(); i++) {
+ assertEquals(4 + i, products.get(i).productId.intValue());
+ }
+ }
}
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<BooleanModel> 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<BooleanAsIntModel> 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<BooleanAsPrimitiveShortModel> 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<BooleanModel> 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<BooleanAsIntModel> 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<BooleanAsPrimitiveShortModel> 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<CategoryAnnotationOnly> getList() {
- CategoryAnnotationOnly[] list = {
- create(1, "Beverages"),
- create(2, "Condiments"),
- create(3, "Produce"),
- create(4, "Meat/Poultry"),
- create(5,"Seafood")
- };
- return Arrays.asList(list);
- }
+ public static List<CategoryAnnotationOnly> getList() {
+ CategoryAnnotationOnly[] list = {
+ create(1, "Beverages"),
+ create(2, "Condiments"),
+ create(3, "Produce"),
+ create(4, "Meat/Poultry"),
+ create(5, "Seafood")
+ };
+ return Arrays.asList(list);
+ }
- @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<ComplexObject> getList() {
- return Arrays.asList(new ComplexObject[] { build(0, true), build(1, false) });
- }
+ public static List<ComplexObject> 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<Customer> 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<Customer> 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<Integer> {
- 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<Integer> enumIdClass() {
- return Integer.class;
- }
-
- }
-
- /**
- * Enum for testing custom ENUMID mapping.
- */
- @IQEnum(EnumType.ENUMID)
- public enum Genus implements EnumId<String> {
- 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<String> 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<EnumIdModel> 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<EnumOrdinalModel> 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<EnumStringModel> 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.
+ * <p>
+ * Individual Tree field declarations can override this strategy by
+ * specifying a different @IQEnum annotation.
+ * <p>
+ * Here ORDINAL specifies that this enum will be mapped to an INT column.
+ */
+ @IQEnum(EnumType.ENUMID)
+ public enum Tree implements EnumId<Integer> {
+ 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<Integer> enumIdClass() {
+ return Integer.class;
+ }
+
+ }
+
+ /**
+ * Enum for testing custom ENUMID mapping.
+ */
+ @IQEnum(EnumType.ENUMID)
+ public enum Genus implements EnumId<String> {
+ 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<String> 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<EnumIdModel> 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<EnumOrdinalModel> 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<EnumStringModel> 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<MultipleBoolsModel> getList() {
- return Arrays.asList(new MultipleBoolsModel(true, true), new MultipleBoolsModel(true, false),
- new MultipleBoolsModel(true, false), new MultipleBoolsModel(false, false));
- }
+ public static List<MultipleBoolsModel> 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<Order> 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<Order> 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<PrimitivesModel> getList() {
- List<PrimitivesModel> list = new ArrayList<PrimitivesModel>();
- for (int i = 1; i <= 10; i++) {
- PrimitivesModel p = new PrimitivesModel();
- p.myLong = i;
- list.add(p);
- }
- return list;
- }
+ public static List<PrimitivesModel> getList() {
+ List<PrimitivesModel> list = new ArrayList<PrimitivesModel>();
+ 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<Product> 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<Product> 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<ProductAnnotationOnly> 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<ProductAnnotationOnly> 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<ProductAnnotationOnlyWithForeignKey> getList() {
- String unmappedField = "unmapped";
- ProductAnnotationOnlyWithForeignKey[] list = { create(1, "Chai", "Beverages", 18, 39, unmappedField),
- create(2, "Chang", "Beverages", 19.0, 17, unmappedField),
- create(3, "Aniseed Syrup", "Condiments", 10.0, 13, unmappedField),
- create(4, "Chef Anton's Cajun Seasoning", "Condiments", 22.0, 53, unmappedField),
- create(5, "Chef Anton's Gumbo Mix", "Condiments", 21.3500, 0, unmappedField),
- create(6, "Grandma's Boysenberry Spread", "Condiments", 25.0, 120, unmappedField),
- create(7, "Uncle Bob's Organic Dried Pears", "Produce", 30.0, 15, unmappedField),
- create(8, "Northwoods Cranberry Sauce", "Condiments", 40.0, 6, unmappedField),
- create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29, unmappedField),
- create(10, "Ikura", "Seafood", 31.0, 31, unmappedField), };
- return Arrays.asList(list);
- }
-
- public String toString() {
- return productName + ": " + unitsInStock;
- }
+ public String unmappedField;
+
+ @IQColumn(name = "id", autoIncrement = true)
+ public Long productId;
+
+ @IQColumn(name = "cat", length = 15, trim = true)
+ public String category;
+
+ @IQColumn(name = "name", length = 50)
+ public String productName;
+
+ @SuppressWarnings("unused")
+ @IQColumn
+ private Double unitPrice;
+
+ @IQColumn
+ private Integer unitsInStock;
+
+ public ProductAnnotationOnlyWithForeignKey() {
+ // public constructor
+ }
+
+ private ProductAnnotationOnlyWithForeignKey(long productId, String productName, String category, double unitPrice,
+ int unitsInStock, String unmappedField) {
+ this.productId = productId;
+ this.productName = productName;
+ this.category = category;
+ this.unitPrice = unitPrice;
+ this.unitsInStock = unitsInStock;
+ this.unmappedField = unmappedField;
+ }
+
+ private static ProductAnnotationOnlyWithForeignKey create(int productId, String productName, String category,
+ double unitPrice, int unitsInStock, String unmappedField) {
+ return new ProductAnnotationOnlyWithForeignKey(productId, productName, category, unitPrice, unitsInStock,
+ unmappedField);
+ }
+
+ public static List<ProductAnnotationOnlyWithForeignKey> getList() {
+ String unmappedField = "unmapped";
+ ProductAnnotationOnlyWithForeignKey[] list = {create(1, "Chai", "Beverages", 18, 39, unmappedField),
+ create(2, "Chang", "Beverages", 19.0, 17, unmappedField),
+ create(3, "Aniseed Syrup", "Condiments", 10.0, 13, unmappedField),
+ create(4, "Chef Anton's Cajun Seasoning", "Condiments", 22.0, 53, unmappedField),
+ create(5, "Chef Anton's Gumbo Mix", "Condiments", 21.3500, 0, unmappedField),
+ create(6, "Grandma's Boysenberry Spread", "Condiments", 25.0, 120, unmappedField),
+ create(7, "Uncle Bob's Organic Dried Pears", "Produce", 30.0, 15, unmappedField),
+ create(8, "Northwoods Cranberry Sauce", "Condiments", 40.0, 6, unmappedField),
+ create(9, "Mishi Kobe Niku", "Meat/Poultry", 97.0, 29, unmappedField),
+ create(10, "Ikura", "Seafood", 31.0, 31, unmappedField),};
+ return Arrays.asList(list);
+ }
+
+ public String toString() {
+ return productName + ": " + unitsInStock;
+ }
}
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<ProductInheritedAnnotation> 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<ProductInheritedAnnotation> 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<ProductMixedAnnotation> 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<ProductMixedAnnotation> 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<ProductNoCreateTable> getList() {
- ProductNoCreateTable[] list = { create(1, "Chai"), create(2, "Chang") };
- return Arrays.asList(list);
- }
+ public static List<ProductNoCreateTable> 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<SupportedTypes> createList() {
- List<SupportedTypes> 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.
+ * <p>
+ * 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<SupportedTypes> createList() {
+ List<SupportedTypes> 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'