diff options
author | James Moger <james.moger@gitblit.com> | 2016-04-05 12:27:55 -0400 |
---|---|---|
committer | James Moger <james.moger@gitblit.com> | 2016-04-05 12:27:55 -0400 |
commit | bb87e621eefd97872aa1a619d38c166b2f07db84 (patch) | |
tree | d7199be13f0015e218ae2ded4ebd8af11b955845 /src | |
parent | 98bb5dc01796728de5b18f84e19766276d12d1db (diff) | |
download | iciql-bb87e621eefd97872aa1a619d38c166b2f07db84.tar.gz iciql-bb87e621eefd97872aa1a619d38c166b2f07db84.zip |
Reformat project with default IntelliJ settings
Diffstat (limited to 'src')
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("jdbc:h2:mem:", "sa", "sa");
* DbInspector inspector = new DbInspector(db);
@@ -179,10 +179,10 @@ import java.lang.annotation.Target; * inspector.generateModel(schema, table, packageName,
* annotateSchema, trimStrings)
* </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 "jdbc:h2:mem:"
@@ -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("jdbc:h2:mem:", "sa", "sa");
* DbInspector inspector = new DbInspector(db);
@@ -205,615 +205,613 @@ import java.lang.annotation.Target; */
public interface Iciql {
- /**
- * An annotation for an iciql version.
- * <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<CustomObject> { * * public Class<CustomObject> 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 < args.length; i++) { * if (i > 0) { * buff.append(", "); - * } + * } * 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("&"); - } else if (inStr.charAt(i) == '<') { - retStr.append("<"); - } else if (inStr.charAt(i) == '>') { - retStr.append(">"); - } else if (inStr.charAt(i) == '\"') { - retStr.append("""); - } else if (changeSpace && inStr.charAt(i) == ' ') { - retStr.append(" "); - } else if (changeSpace && inStr.charAt(i) == '\t') { - retStr.append(" "); - } else { - retStr.append(inStr.charAt(i)); - } - i++; - } - return retStr.toString(); - } + /** + * Prepare text for html presentation. Replace sensitive characters with + * html entities. + * + * @param inStr + * @param changeSpace + * @return plain text escaped for html + */ + public static String escapeForHtml(String inStr, boolean changeSpace) { + StringBuffer retStr = new StringBuffer(); + int i = 0; + while (i < inStr.length()) { + if (inStr.charAt(i) == '&') { + retStr.append("&"); + } else if (inStr.charAt(i) == '<') { + retStr.append("<"); + } else if (inStr.charAt(i) == '>') { + retStr.append(">"); + } else if (inStr.charAt(i) == '\"') { + retStr.append("""); + } else if (changeSpace && inStr.charAt(i) == ' ') { + retStr.append(" "); + } else if (changeSpace && inStr.charAt(i) == '\t') { + retStr.append(" "); + } else { + retStr.append(inStr.charAt(i)); + } + i++; + } + return retStr.toString(); + } - /** - * Replaces carriage returns and line feeds with html line breaks. - * - * @param string - * @return plain text with html line breaks - */ - public static String breakLinesForHtml(String string) { - return string.replace("\r\n", "<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' |