Browse Source

Reformat project with default IntelliJ settings

tags/release-2.1.0
James Moger 8 years ago
parent
commit
bb87e621ee
100 changed files with 14704 additions and 14974 deletions
  1. 2
    1
      pom.xml
  2. 15
    15
      src/main/java/com/iciql/CompareType.java
  3. 42
    43
      src/main/java/com/iciql/Condition.java
  4. 8
    8
      src/main/java/com/iciql/ConditionAndOr.java
  5. 8
    8
      src/main/java/com/iciql/ConditionOpenClose.java
  6. 34
    34
      src/main/java/com/iciql/Constants.java
  7. 130
    131
      src/main/java/com/iciql/Dao.java
  8. 65
    66
      src/main/java/com/iciql/DaoClasspathStatementProvider.java
  9. 625
    626
      src/main/java/com/iciql/DaoProxy.java
  10. 8
    9
      src/main/java/com/iciql/DaoStatementProvider.java
  11. 829
    841
      src/main/java/com/iciql/Db.java
  12. 158
    169
      src/main/java/com/iciql/DbInspector.java
  13. 37
    45
      src/main/java/com/iciql/DbUpgrader.java
  14. 25
    26
      src/main/java/com/iciql/DbVersion.java
  15. 105
    105
      src/main/java/com/iciql/Define.java
  16. 1
    1
      src/main/java/com/iciql/Filter.java
  17. 121
    121
      src/main/java/com/iciql/Function.java
  18. 613
    615
      src/main/java/com/iciql/Iciql.java
  19. 166
    166
      src/main/java/com/iciql/IciqlException.java
  20. 459
    468
      src/main/java/com/iciql/ModelUtils.java
  21. 82
    82
      src/main/java/com/iciql/NestedConditions.java
  22. 26
    27
      src/main/java/com/iciql/OrderExpression.java
  23. 1041
    1063
      src/main/java/com/iciql/Query.java
  24. 28
    34
      src/main/java/com/iciql/QueryBetween.java
  25. 118
    120
      src/main/java/com/iciql/QueryCondition.java
  26. 39
    39
      src/main/java/com/iciql/QueryJoin.java
  27. 46
    47
      src/main/java/com/iciql/QueryJoinCondition.java
  28. 577
    611
      src/main/java/com/iciql/QueryWhere.java
  29. 20
    21
      src/main/java/com/iciql/RuntimeParameter.java
  30. 27
    30
      src/main/java/com/iciql/RuntimeToken.java
  31. 174
    196
      src/main/java/com/iciql/SQLDialect.java
  32. 492
    492
      src/main/java/com/iciql/SQLDialectDefault.java
  33. 41
    41
      src/main/java/com/iciql/SQLDialectDerby.java
  34. 103
    103
      src/main/java/com/iciql/SQLDialectH2.java
  35. 115
    115
      src/main/java/com/iciql/SQLDialectHSQL.java
  36. 32
    32
      src/main/java/com/iciql/SQLDialectMSSQL.java
  37. 62
    62
      src/main/java/com/iciql/SQLDialectMySQL.java
  38. 132
    132
      src/main/java/com/iciql/SQLDialectPostgreSQL.java
  39. 150
    150
      src/main/java/com/iciql/SQLDialectSQLite.java
  40. 158
    158
      src/main/java/com/iciql/SQLStatement.java
  41. 29
    30
      src/main/java/com/iciql/SelectColumn.java
  42. 82
    83
      src/main/java/com/iciql/SelectTable.java
  43. 11
    11
      src/main/java/com/iciql/SubQuery.java
  44. 14
    15
      src/main/java/com/iciql/SubQueryCondition.java
  45. 1143
    1181
      src/main/java/com/iciql/TableDefinition.java
  46. 673
    680
      src/main/java/com/iciql/TableInspector.java
  47. 78
    79
      src/main/java/com/iciql/TestCondition.java
  48. 7
    9
      src/main/java/com/iciql/Token.java
  49. 6
    7
      src/main/java/com/iciql/UpdateColumn.java
  50. 26
    28
      src/main/java/com/iciql/UpdateColumnIncrement.java
  51. 34
    36
      src/main/java/com/iciql/UpdateColumnSet.java
  52. 95
    95
      src/main/java/com/iciql/ValidationRemark.java
  53. 25
    25
      src/main/java/com/iciql/adapter/GsonTypeAdapter.java
  54. 60
    59
      src/main/java/com/iciql/adapter/JavaSerializationTypeAdapter.java
  55. 33
    34
      src/main/java/com/iciql/adapter/SnakeYamlTypeAdapter.java
  56. 33
    33
      src/main/java/com/iciql/adapter/XStreamTypeAdapter.java
  57. 19
    21
      src/main/java/com/iciql/adapter/postgresql/JsonObjectAdapter.java
  58. 36
    37
      src/main/java/com/iciql/adapter/postgresql/JsonStringAdapter.java
  59. 19
    21
      src/main/java/com/iciql/adapter/postgresql/JsonbObjectAdapter.java
  60. 36
    37
      src/main/java/com/iciql/adapter/postgresql/JsonbStringAdapter.java
  61. 4
    5
      src/main/java/com/iciql/adapter/postgresql/XmlObjectAdapter.java
  62. 36
    37
      src/main/java/com/iciql/adapter/postgresql/XmlStringAdapter.java
  63. 16
    16
      src/main/java/com/iciql/bytecode/And.java
  64. 19
    19
      src/main/java/com/iciql/bytecode/ArrayGet.java
  65. 32
    32
      src/main/java/com/iciql/bytecode/CaseWhen.java
  66. 1400
    1400
      src/main/java/com/iciql/bytecode/ClassReader.java
  67. 8
    8
      src/main/java/com/iciql/bytecode/Constant.java
  68. 32
    32
      src/main/java/com/iciql/bytecode/ConstantNumber.java
  69. 19
    19
      src/main/java/com/iciql/bytecode/ConstantString.java
  70. 15
    15
      src/main/java/com/iciql/bytecode/Function.java
  71. 25
    25
      src/main/java/com/iciql/bytecode/Not.java
  72. 11
    11
      src/main/java/com/iciql/bytecode/Null.java
  73. 81
    81
      src/main/java/com/iciql/bytecode/Operation.java
  74. 17
    17
      src/main/java/com/iciql/bytecode/Or.java
  75. 16
    16
      src/main/java/com/iciql/bytecode/Variable.java
  76. 3
    2
      src/main/java/com/iciql/bytecode/package.html
  77. 3
    2
      src/main/java/com/iciql/package.html
  78. 140
    150
      src/main/java/com/iciql/util/GenerateModels.java
  79. 179
    180
      src/main/java/com/iciql/util/IciqlLogger.java
  80. 205
    221
      src/main/java/com/iciql/util/JdbcUtils.java
  81. 56
    57
      src/main/java/com/iciql/util/Slf4jIciqlListener.java
  82. 117
    123
      src/main/java/com/iciql/util/StatementBuilder.java
  83. 320
    336
      src/main/java/com/iciql/util/StringUtils.java
  84. 568
    572
      src/main/java/com/iciql/util/Utils.java
  85. 206
    208
      src/main/java/com/iciql/util/WeakIdentityHashMap.java
  86. 3
    2
      src/main/java/com/iciql/util/package.html
  87. 3
    3
      src/main/java/java/lang/AutoCloseable.java
  88. 100
    101
      src/test/java/com/iciql/test/AliasMapTest.java
  89. 160
    161
      src/test/java/com/iciql/test/AnnotationsTest.java
  90. 137
    138
      src/test/java/com/iciql/test/BooleanModelTest.java
  91. 83
    84
      src/test/java/com/iciql/test/ClobTest.java
  92. 168
    169
      src/test/java/com/iciql/test/ConcurrencyTest.java
  93. 54
    55
      src/test/java/com/iciql/test/DataTypeAdapterTest.java
  94. 31
    32
      src/test/java/com/iciql/test/DefaultValuesTest.java
  95. 121
    122
      src/test/java/com/iciql/test/EnumsTest.java
  96. 47
    48
      src/test/java/com/iciql/test/ForeignKeyTest.java
  97. 633
    635
      src/test/java/com/iciql/test/IciqlSuite.java
  98. 120
    122
      src/test/java/com/iciql/test/JoinTest.java
  99. 143
    144
      src/test/java/com/iciql/test/ModelsTest.java
  100. 0
    0
      src/test/java/com/iciql/test/NestedConditionsTest.java

+ 2
- 1
pom.xml View File

@@ -1,4 +1,5 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>


+ 15
- 15
src/main/java/com/iciql/CompareType.java View File

@@ -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;
}
}

+ 42
- 43
src/main/java/com/iciql/Condition.java View File

@@ -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);
}
}
}
}

+ 8
- 8
src/main/java/com/iciql/ConditionAndOr.java View File

@@ -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);
}
}

+ 8
- 8
src/main/java/com/iciql/ConditionOpenClose.java View File

@@ -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);
}

}

+ 34
- 34
src/main/java/com/iciql/Constants.java View File

@@ -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;
}
}

+ 130
- 131
src/main/java/com/iciql/Dao.java View File

@@ -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) {
}
}
}

+ 65
- 66
src/main/java/com/iciql/DaoClasspathStatementProvider.java View File

@@ -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;
}

}

+ 625
- 626
src/main/java/com/iciql/DaoProxy.java
File diff suppressed because it is too large
View File


+ 8
- 9
src/main/java/com/iciql/DaoStatementProvider.java View File

@@ -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);
}

+ 829
- 841
src/main/java/com/iciql/Db.java
File diff suppressed because it is too large
View File


+ 158
- 169
src/main/java/com/iciql/DbInspector.java View File

@@ -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);
}
}

}

+ 37
- 45
src/main/java/com/iciql/DbUpgrader.java View File

@@ -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.");
}

}
}

}

+ 25
- 26
src/main/java/com/iciql/DbVersion.java View File

@@ -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;
}

}

+ 105
- 105
src/main/java/com/iciql/Define.java View File

@@ -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.");
}
}
}

+ 1
- 1
src/main/java/com/iciql/Filter.java View File

@@ -21,5 +21,5 @@ package com.iciql;
* Represents the WHERE clause of a query.
*/
public interface Filter {
boolean where();
boolean where();
}

+ 121
- 121
src/main/java/com/iciql/Function.java View File

@@ -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(")");
}
});
}
}

+ 613
- 615
src/main/java/com/iciql/Iciql.java
File diff suppressed because it is too large
View File


+ 166
- 166
src/main/java/com/iciql/IciqlException.java View File

@@ -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();
}
}

+ 459
- 468
src/main/java/com/iciql/ModelUtils.java View File

@@ -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;
}
}

+ 82
- 82
src/main/java/com/iciql/NestedConditions.java View File

@@ -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));
}

}

+ 26
- 27
src/main/java/com/iciql/OrderExpression.java View File

@@ -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");
}
}
}

+ 1041
- 1063
src/main/java/com/iciql/Query.java
File diff suppressed because it is too large
View File


+ 28
- 34
src/main/java/com/iciql/QueryBetween.java View File

@@ -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);
}
}

+ 118
- 120
src/main/java/com/iciql/QueryCondition.java View File

@@ -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);
}
}

+ 39
- 39
src/main/java/com/iciql/QueryJoin.java View File

@@ -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);
}
}

+ 46
- 47
src/main/java/com/iciql/QueryJoinCondition.java View File

@@ -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;
}
}

+ 577
- 611
src/main/java/com/iciql/QueryWhere.java
File diff suppressed because it is too large
View File


+ 20
- 21
src/main/java/com/iciql/RuntimeParameter.java View File

@@ -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);
}
}
}

+ 27
- 30
src/main/java/com/iciql/RuntimeToken.java View File

@@ -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);
}
}
}

+ 174
- 196
src/main/java/com/iciql/SQLDialect.java View File

@@ -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);

}

+ 492
- 492
src/main/java/com/iciql/SQLDialectDefault.java
File diff suppressed because it is too large
View File


+ 41
- 41
src/main/java/com/iciql/SQLDialectDerby.java View File

@@ -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;
}
}

+ 103
- 103
src/main/java/com/iciql/SQLDialectH2.java View File

@@ -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());
}
}

+ 115
- 115
src/main/java/com/iciql/SQLDialectHSQL.java View File

@@ -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());
}
}

+ 32
- 32
src/main/java/com/iciql/SQLDialectMSSQL.java View File

@@ -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());
}
}
}
}
}

+ 62
- 62
src/main/java/com/iciql/SQLDialectMySQL.java View File

@@ -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());
}
}

+ 132
- 132
src/main/java/com/iciql/SQLDialectPostgreSQL.java View File

@@ -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());
}
}

+ 150
- 150
src/main/java/com/iciql/SQLDialectSQLite.java View File

@@ -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);
}
}

+ 158
- 158
src/main/java/com/iciql/SQLStatement.java View File

@@ -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;
}
}

+ 29
- 30
src/main/java/com/iciql/SelectColumn.java View File

@@ -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());
}
}

+ 82
- 83
src/main/java/com/iciql/SelectTable.java View File

@@ -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;
}
}

+ 11
- 11
src/main/java/com/iciql/SubQuery.java View File

@@ -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));
}
}

+ 14
- 15
src/main/java/com/iciql/SubQueryCondition.java View File

@@ -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(")");
}
}

+ 1143
- 1181
src/main/java/com/iciql/TableDefinition.java
File diff suppressed because it is too large
View File


+ 673
- 680
src/main/java/com/iciql/TableInspector.java
File diff suppressed because it is too large
View File


+ 78
- 79
src/main/java/com/iciql/TestCondition.java View File

@@ -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(")");
}
});
}
}

+ 7
- 9
src/main/java/com/iciql/Token.java View File

@@ -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);
}

+ 6
- 7
src/main/java/com/iciql/UpdateColumn.java View File

@@ -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);

}

+ 26
- 28
src/main/java/com/iciql/UpdateColumnIncrement.java View File

@@ -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(")");
}

}

+ 34
- 36
src/main/java/com/iciql/UpdateColumnSet.java View File

@@ -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);
}
}

}

+ 95
- 95
src/main/java/com/iciql/ValidationRemark.java View File

@@ -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();
}

}

+ 25
- 25
src/main/java/com/iciql/adapter/GsonTypeAdapter.java View File

@@ -26,13 +26,13 @@ import com.iciql.Iciql.Mode;
* <p>
* You use this by creating a subclass which defines your object class.
* </p>
*
* <p>
* <pre>
* public class CustomObjectAdapter extends GsonTypeAdapter&lt;CustomObject&gt; {
*
* public Class&lt;CustomObject&gt; getJavaType() {
* return CustomObject.class;
* }
* }
* }
* </pre>
*
@@ -40,32 +40,32 @@ import com.iciql.Iciql.Mode;
*/
public abstract class GsonTypeAdapter<T> implements DataTypeAdapter<T> {

protected Mode mode;
protected Mode mode;

protected Gson gson() {
return new GsonBuilder().create();
}
protected Gson gson() {
return new GsonBuilder().create();
}

@Override
public void setMode(Mode mode) {
this.mode = mode;
}
@Override
public void setMode(Mode mode) {
this.mode = mode;
}

@Override
public String getDataType() {
return "TEXT";
}
@Override
public String getDataType() {
return "TEXT";
}

@Override
public Object serialize(T value) {
return gson().toJson(value);
}
@Override
public Object serialize(T value) {
return gson().toJson(value);
}

@Override
public T deserialize(Object value) {
String json = value.toString();
Gson gson = gson();
T t = gson.fromJson(json, getJavaType());
return t;
}
@Override
public T deserialize(Object value) {
String json = value.toString();
Gson gson = gson();
T t = gson.fromJson(json, getJavaType());
return t;
}
}

+ 60
- 59
src/main/java/com/iciql/adapter/JavaSerializationTypeAdapter.java View File

@@ -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);
}
}
}
}
}

+ 33
- 34
src/main/java/com/iciql/adapter/SnakeYamlTypeAdapter.java View File

@@ -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;
}

}

+ 33
- 33
src/main/java/com/iciql/adapter/XStreamTypeAdapter.java View File

@@ -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;
}

}

+ 19
- 21
src/main/java/com/iciql/adapter/postgresql/JsonObjectAdapter.java View File

@@ -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;
}
}

+ 36
- 37
src/main/java/com/iciql/adapter/postgresql/JsonStringAdapter.java View File

@@ -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();
}
}

+ 19
- 21
src/main/java/com/iciql/adapter/postgresql/JsonbObjectAdapter.java View File

@@ -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;
}
}

+ 36
- 37
src/main/java/com/iciql/adapter/postgresql/JsonbStringAdapter.java View File

@@ -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();
}
}

+ 4
- 5
src/main/java/com/iciql/adapter/postgresql/XmlObjectAdapter.java View File

@@ -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";
}

}

+ 36
- 37
src/main/java/com/iciql/adapter/postgresql/XmlStringAdapter.java View File

@@ -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();
}
}

+ 16
- 16
src/main/java/com/iciql/bytecode/And.java View File

@@ -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);
}

}

+ 19
- 19
src/main/java/com/iciql/bytecode/ArrayGet.java View File

@@ -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("]");
}

}

+ 32
- 32
src/main/java/com/iciql/bytecode/CaseWhen.java View File

@@ -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");
}

}

+ 1400
- 1400
src/main/java/com/iciql/bytecode/ClassReader.java
File diff suppressed because it is too large
View File


+ 8
- 8
src/main/java/com/iciql/bytecode/Constant.java View File

@@ -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();

}

+ 32
- 32
src/main/java/com/iciql/bytecode/ConstantNumber.java View File

@@ -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;
}

}

+ 19
- 19
src/main/java/com/iciql/bytecode/ConstantString.java View File

@@ -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;
}

}

+ 15
- 15
src/main/java/com/iciql/bytecode/Function.java View File

@@ -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(")");
}
}

+ 25
- 25
src/main/java/com/iciql/bytecode/Not.java View File

@@ -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(")");
}

}

+ 11
- 11
src/main/java/com/iciql/bytecode/Null.java View File

@@ -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");
}

}

+ 81
- 81
src/main/java/com/iciql/bytecode/Operation.java View File

@@ -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);
}

}

+ 17
- 17
src/main/java/com/iciql/bytecode/Or.java View File

@@ -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);
}

}

+ 16
- 16
src/main/java/com/iciql/bytecode/Variable.java View File

@@ -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);
}

}

+ 3
- 2
src/main/java/com/iciql/bytecode/package.html View File

@@ -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.

+ 3
- 2
src/main/java/com/iciql/package.html View File

@@ -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

+ 140
- 150
src/main/java/com/iciql/util/GenerateModels.java View File

@@ -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>");
}

}

+ 179
- 180
src/main/java/com/iciql/util/IciqlLogger.java View File

@@ -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));
}
}
}

+ 205
- 221
src/main/java/com/iciql/util/JdbcUtils.java View File

@@ -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);
}
}

}

+ 56
- 57
src/main/java/com/iciql/util/Slf4jIciqlListener.java View File

@@ -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;
}
}
}

+ 117
- 123
src/main/java/com/iciql/util/StatementBuilder.java View File

@@ -21,19 +21,19 @@ package com.iciql.util;
* A utility class to build a statement. In addition to the methods supported by
* StringBuilder, it allows to add a text only in the second iteration. This
* simplified constructs such as:
*
* <p>
* <pre>
* StringBuilder buff = new StringBuilder();
* for (int i = 0; i &lt; args.length; i++) {
* if (i &gt; 0) {
* buff.append(&quot;, &quot;);
* }
* }
* buff.append(args[i]);
* }
* </pre>
*
* <p>
* to
*
* <p>
* <pre>
* StatementBuilder buff = new StatementBuilder();
* for (String s : args) {
@@ -44,123 +44,117 @@ package com.iciql.util;
*/
public class StatementBuilder {

private final StringBuilder builder = new StringBuilder();
private int index;

/**
* Create a new builder.
*/
public StatementBuilder() {
// nothing to do
}

/**
* Create a new builder.
*
* @param string
* the initial string
*/
public StatementBuilder(String string) {
builder.append(string);
}

/**
* Append a text.
*
* @param s
* the text to append
* @return itself
*/
public StatementBuilder append(String s) {
builder.append(s);
return this;
}

/**
* Append a character.
*
* @param c
* the character to append
* @return itself
*/
public StatementBuilder append(char c) {
builder.append(c);
return this;
}

/**
* Append a number.
*
* @param x
* the number to append
* @return itself
*/
public StatementBuilder append(long x) {
builder.append(x);
return this;
}

/**
* Returns the current value of the loop counter.
*
* @return the loop counter
*/
public int getCount() {
return index;
}

/**
* Reset the loop counter.
*
* @return itself
*/
public StatementBuilder resetCount() {
index = 0;
return this;
}

/**
* Append a text, but only if appendExceptFirst was never called.
*
* @param s
* the text to append
*/
public void appendOnlyFirst(String s) {
if (index == 0) {
builder.append(s);
}
}

/**
* Append a text, except when this method is called the first time.
*
* @param s
* the text to append
*/
public void appendExceptFirst(String s) {
if (index++ > 0) {
builder.append(s);
}
}

public void append(StatementBuilder sb) {
builder.append(sb);
}

public void insert(int offset, char c) {
builder.insert(offset, c);
}

public String toString() {
return builder.toString();
}

/**
* Get the length.
*
* @return the length
*/
public int length() {
return builder.length();
}
private final StringBuilder builder = new StringBuilder();
private int index;

/**
* Create a new builder.
*/
public StatementBuilder() {
// nothing to do
}

/**
* Create a new builder.
*
* @param string the initial string
*/
public StatementBuilder(String string) {
builder.append(string);
}

/**
* Append a text.
*
* @param s the text to append
* @return itself
*/
public StatementBuilder append(String s) {
builder.append(s);
return this;
}

/**
* Append a character.
*
* @param c the character to append
* @return itself
*/
public StatementBuilder append(char c) {
builder.append(c);
return this;
}

/**
* Append a number.
*
* @param x the number to append
* @return itself
*/
public StatementBuilder append(long x) {
builder.append(x);
return this;
}

/**
* Returns the current value of the loop counter.
*
* @return the loop counter
*/
public int getCount() {
return index;
}

/**
* Reset the loop counter.
*
* @return itself
*/
public StatementBuilder resetCount() {
index = 0;
return this;
}

/**
* Append a text, but only if appendExceptFirst was never called.
*
* @param s the text to append
*/
public void appendOnlyFirst(String s) {
if (index == 0) {
builder.append(s);
}
}

/**
* Append a text, except when this method is called the first time.
*
* @param s the text to append
*/
public void appendExceptFirst(String s) {
if (index++ > 0) {
builder.append(s);
}
}

public void append(StatementBuilder sb) {
builder.append(sb);
}

public void insert(int offset, char c) {
builder.insert(offset, c);
}

public String toString() {
return builder.toString();
}

/**
* Get the length.
*
* @return the length
*/
public int length() {
return builder.length();
}
}

+ 320
- 336
src/main/java/com/iciql/util/StringUtils.java View File

@@ -29,354 +29,338 @@ import java.util.ArrayList;

/**
* Common string utilities.
*
*/
public class StringUtils {

/**
* Replace all occurrences of the before string with the after string.
*
* @param s
* the string
* @param before
* the old text
* @param after
* the new text
* @return the string with the before string replaced
*/
public static String replaceAll(String s, String before, String after) {
int next = s.indexOf(before);
if (next < 0) {
return s;
}
StringBuilder buff = new StringBuilder(s.length() - before.length() + after.length());
int index = 0;
while (true) {
buff.append(s.substring(index, next)).append(after);
index = next + before.length();
next = s.indexOf(before, index);
if (next < 0) {
buff.append(s.substring(index));
break;
}
}
return buff.toString();
}
/**
* Replace all occurrences of the before string with the after string.
*
* @param s the string
* @param before the old text
* @param after the new text
* @return the string with the before string replaced
*/
public static String replaceAll(String s, String before, String after) {
int next = s.indexOf(before);
if (next < 0) {
return s;
}
StringBuilder buff = new StringBuilder(s.length() - before.length() + after.length());
int index = 0;
while (true) {
buff.append(s.substring(index, next)).append(after);
index = next + before.length();
next = s.indexOf(before, index);
if (next < 0) {
buff.append(s.substring(index));
break;
}
}
return buff.toString();
}

/**
* Check if a String is null or empty (the length is null).
*
* @param s
* the string to check
* @return true if it is null or empty
*/
public static boolean isNullOrEmpty(String s) {
return s == null || s.length() == 0;
}
/**
* Check if a String is null or empty (the length is null).
*
* @param s the string to check
* @return true if it is null or empty
*/
public static boolean isNullOrEmpty(String s) {
return s == null || s.length() == 0;
}

/**
* Convert a string to a Java literal using the correct escape sequences.
* The literal is not enclosed in double quotes. The result can be used in
* properties files or in Java source code.
*
* @param s
* the text to convert
* @return the Java representation
*/
public static String javaEncode(String s) {
int length = s.length();
StringBuilder buff = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
switch (c) {
// case '\b':
// // BS backspace
// // not supported in properties files
// buff.append("\\b");
// break;
case '\t':
// HT horizontal tab
buff.append("\\t");
break;
case '\n':
// LF linefeed
buff.append("\\n");
break;
case '\f':
// FF form feed
buff.append("\\f");
break;
case '\r':
// CR carriage return
buff.append("\\r");
break;
case '"':
// double quote
buff.append("\\\"");
break;
case '\\':
// backslash
buff.append("\\\\");
break;
default:
int ch = c & 0xffff;
if (ch >= ' ' && (ch < 0x80)) {
buff.append(c);
// not supported in properties files
// } else if(ch < 0xff) {
// buff.append("\\");
// // make sure it's three characters (0x200 is octal 1000)
// buff.append(Integer.toOctalString(0x200 |
// ch).substring(1));
} else {
buff.append("\\u");
// make sure it's four characters
buff.append(Integer.toHexString(0x10000 | ch).substring(1));
}
}
}
return buff.toString();
}
/**
* Convert a string to a Java literal using the correct escape sequences.
* The literal is not enclosed in double quotes. The result can be used in
* properties files or in Java source code.
*
* @param s the text to convert
* @return the Java representation
*/
public static String javaEncode(String s) {
int length = s.length();
StringBuilder buff = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
switch (c) {
// case '\b':
// // BS backspace
// // not supported in properties files
// buff.append("\\b");
// break;
case '\t':
// HT horizontal tab
buff.append("\\t");
break;
case '\n':
// LF linefeed
buff.append("\\n");
break;
case '\f':
// FF form feed
buff.append("\\f");
break;
case '\r':
// CR carriage return
buff.append("\\r");
break;
case '"':
// double quote
buff.append("\\\"");
break;
case '\\':
// backslash
buff.append("\\\\");
break;
default:
int ch = c & 0xffff;
if (ch >= ' ' && (ch < 0x80)) {
buff.append(c);
// not supported in properties files
// } else if(ch < 0xff) {
// buff.append("\\");
// // make sure it's three characters (0x200 is octal 1000)
// buff.append(Integer.toOctalString(0x200 |
// ch).substring(1));
} else {
buff.append("\\u");
// make sure it's four characters
buff.append(Integer.toHexString(0x10000 | ch).substring(1));
}
}
}
return buff.toString();
}

/**
* Pad a string. This method is used for the SQL function RPAD and LPAD.
*
* @param string
* the original string
* @param n
* the target length
* @param padding
* the padding string
* @param right
* true if the padding should be appended at the end
* @return the padded string
*/
public static String pad(String string, int n, String padding, boolean right) {
if (n < 0) {
n = 0;
}
if (n < string.length()) {
return string.substring(0, n);
} else if (n == string.length()) {
return string;
}
char paddingChar;
if (padding == null || padding.length() == 0) {
paddingChar = ' ';
} else {
paddingChar = padding.charAt(0);
}
StringBuilder buff = new StringBuilder(n);
n -= string.length();
if (right) {
buff.append(string);
}
for (int i = 0; i < n; i++) {
buff.append(paddingChar);
}
if (!right) {
buff.append(string);
}
return buff.toString();
}
/**
* Pad a string. This method is used for the SQL function RPAD and LPAD.
*
* @param string the original string
* @param n the target length
* @param padding the padding string
* @param right true if the padding should be appended at the end
* @return the padded string
*/
public static String pad(String string, int n, String padding, boolean right) {
if (n < 0) {
n = 0;
}
if (n < string.length()) {
return string.substring(0, n);
} else if (n == string.length()) {
return string;
}
char paddingChar;
if (padding == null || padding.length() == 0) {
paddingChar = ' ';
} else {
paddingChar = padding.charAt(0);
}
StringBuilder buff = new StringBuilder(n);
n -= string.length();
if (right) {
buff.append(string);
}
for (int i = 0; i < n; i++) {
buff.append(paddingChar);
}
if (!right) {
buff.append(string);
}
return buff.toString();
}

/**
* Convert a string to a SQL literal. Null is converted to NULL. The text is
* enclosed in single quotes. If there are any special characters, the
* method STRINGDECODE is used.
*
* @param s
* the text to convert.
* @return the SQL literal
*/
public static String quoteStringSQL(String s) {
if (s == null) {
return "NULL";
}
int length = s.length();
StringBuilder buff = new StringBuilder(length + 2);
buff.append('\'');
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (c == '\'') {
buff.append(c);
} else if (c < ' ' || c > 127) {
// need to start from the beginning because maybe there was a \
// that was not quoted
return "STRINGDECODE(" + quoteStringSQL(javaEncode(s)) + ")";
}
buff.append(c);
}
buff.append('\'');
return buff.toString();
}
/**
* Convert a string to a SQL literal. Null is converted to NULL. The text is
* enclosed in single quotes. If there are any special characters, the
* method STRINGDECODE is used.
*
* @param s the text to convert.
* @return the SQL literal
*/
public static String quoteStringSQL(String s) {
if (s == null) {
return "NULL";
}
int length = s.length();
StringBuilder buff = new StringBuilder(length + 2);
buff.append('\'');
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (c == '\'') {
buff.append(c);
} else if (c < ' ' || c > 127) {
// need to start from the beginning because maybe there was a \
// that was not quoted
return "STRINGDECODE(" + quoteStringSQL(javaEncode(s)) + ")";
}
buff.append(c);
}
buff.append('\'');
return buff.toString();
}

/**
* Split a string into an array of strings using the given separator. A null
* string will result in a null array, and an empty string in a zero element
* array.
*
* @param s
* the string to split
* @param separatorChar
* the separator character
* @param trim
* whether each element should be trimmed
* @return the array list
*/
public static String[] arraySplit(String s, char separatorChar, boolean trim) {
if (s == null) {
return null;
}
int length = s.length();
if (length == 0) {
return new String[0];
}
ArrayList<String> list = Utils.newArrayList();
StringBuilder buff = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (c == separatorChar) {
String e = buff.toString();
list.add(trim ? e.trim() : e);
buff.setLength(0);
} else if (c == '\\' && i < length - 1) {
buff.append(s.charAt(++i));
} else {
buff.append(c);
}
}
String e = buff.toString();
list.add(trim ? e.trim() : e);
String[] array = new String[list.size()];
list.toArray(array);
return array;
}
/**
* Split a string into an array of strings using the given separator. A null
* string will result in a null array, and an empty string in a zero element
* array.
*
* @param s the string to split
* @param separatorChar the separator character
* @param trim whether each element should be trimmed
* @return the array list
*/
public static String[] arraySplit(String s, char separatorChar, boolean trim) {
if (s == null) {
return null;
}
int length = s.length();
if (length == 0) {
return new String[0];
}
ArrayList<String> list = Utils.newArrayList();
StringBuilder buff = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (c == separatorChar) {
String e = buff.toString();
list.add(trim ? e.trim() : e);
buff.setLength(0);
} else if (c == '\\' && i < length - 1) {
buff.append(s.charAt(++i));
} else {
buff.append(c);
}
}
String e = buff.toString();
list.add(trim ? e.trim() : e);
String[] array = new String[list.size()];
list.toArray(array);
return array;
}

/**
* Calculates the SHA1 of the string.
*
* @param text
* @return sha1 of the string
*/
public static String calculateSHA1(String text) {
try {
byte[] bytes = text.getBytes("iso-8859-1");
return calculateSHA1(bytes);
} catch (UnsupportedEncodingException u) {
throw new RuntimeException(u);
}
}
/**
* Calculates the SHA1 of the string.
*
* @param text
* @return sha1 of the string
*/
public static String calculateSHA1(String text) {
try {
byte[] bytes = text.getBytes("iso-8859-1");
return calculateSHA1(bytes);
} catch (UnsupportedEncodingException u) {
throw new RuntimeException(u);
}
}

/**
* Calculates the SHA1 of the byte array.
*
* @param bytes
* @return sha1 of the byte array
*/
public static String calculateSHA1(byte[] bytes) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(bytes, 0, bytes.length);
byte[] digest = md.digest();
StringBuilder sb = new StringBuilder(digest.length * 2);
for (int i = 0; i < digest.length; i++) {
if (((int) digest[i] & 0xff) < 0x10) {
sb.append('0');
}
sb.append(Integer.toHexString((int) digest[i] & 0xff));
}
return sb.toString();
} catch (NoSuchAlgorithmException t) {
throw new RuntimeException(t);
}
}
/**
* Calculates the SHA1 of the byte array.
*
* @param bytes
* @return sha1 of the byte array
*/
public static String calculateSHA1(byte[] bytes) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-1");
md.update(bytes, 0, bytes.length);
byte[] digest = md.digest();
StringBuilder sb = new StringBuilder(digest.length * 2);
for (int i = 0; i < digest.length; i++) {
if (((int) digest[i] & 0xff) < 0x10) {
sb.append('0');
}
sb.append(Integer.toHexString((int) digest[i] & 0xff));
}
return sb.toString();
} catch (NoSuchAlgorithmException t) {
throw new RuntimeException(t);
}
}

/**
* Counts the occurrences of char c in the given string.
*
* @param c
* the character to count
* @param value
* the source string
* @return the count of c in value
*/
public static int count(char c, String value) {
int count = 0;
for (char cv : value.toCharArray()) {
if (cv == c) {
count++;
}
}
return count;
}
/**
* Counts the occurrences of char c in the given string.
*
* @param c the character to count
* @param value the source string
* @return the count of c in value
*/
public static int count(char c, String value) {
int count = 0;
for (char cv : value.toCharArray()) {
if (cv == c) {
count++;
}
}
return count;
}

/**
* Prepare text for html presentation. Replace sensitive characters with
* html entities.
*
* @param inStr
* @param changeSpace
* @return plain text escaped for html
*/
public static String escapeForHtml(String inStr, boolean changeSpace) {
StringBuffer retStr = new StringBuffer();
int i = 0;
while (i < inStr.length()) {
if (inStr.charAt(i) == '&') {
retStr.append("&amp;");
} else if (inStr.charAt(i) == '<') {
retStr.append("&lt;");
} else if (inStr.charAt(i) == '>') {
retStr.append("&gt;");
} else if (inStr.charAt(i) == '\"') {
retStr.append("&quot;");
} else if (changeSpace && inStr.charAt(i) == ' ') {
retStr.append("&nbsp;");
} else if (changeSpace && inStr.charAt(i) == '\t') {
retStr.append(" &nbsp; &nbsp;");
} else {
retStr.append(inStr.charAt(i));
}
i++;
}
return retStr.toString();
}
/**
* Prepare text for html presentation. Replace sensitive characters with
* html entities.
*
* @param inStr
* @param changeSpace
* @return plain text escaped for html
*/
public static String escapeForHtml(String inStr, boolean changeSpace) {
StringBuffer retStr = new StringBuffer();
int i = 0;
while (i < inStr.length()) {
if (inStr.charAt(i) == '&') {
retStr.append("&amp;");
} else if (inStr.charAt(i) == '<') {
retStr.append("&lt;");
} else if (inStr.charAt(i) == '>') {
retStr.append("&gt;");
} else if (inStr.charAt(i) == '\"') {
retStr.append("&quot;");
} else if (changeSpace && inStr.charAt(i) == ' ') {
retStr.append("&nbsp;");
} else if (changeSpace && inStr.charAt(i) == '\t') {
retStr.append(" &nbsp; &nbsp;");
} else {
retStr.append(inStr.charAt(i));
}
i++;
}
return retStr.toString();
}

/**
* Replaces carriage returns and line feeds with html line breaks.
*
* @param string
* @return plain text with html line breaks
*/
public static String breakLinesForHtml(String string) {
return string.replace("\r\n", "<br/>").replace("\r", "<br/>").replace("\n", "<br/>");
}
/**
* Replaces carriage returns and line feeds with html line breaks.
*
* @param string
* @return plain text with html line breaks
*/
public static String breakLinesForHtml(String string) {
return string.replace("\r\n", "<br/>").replace("\r", "<br/>").replace("\n", "<br/>");
}

/**
* Returns the string content of the specified file.
*
* @param file
* @param lineEnding
* @return the string content of the file
*/
public static String readContent(File file, String lineEnding) {
StringBuilder sb = new StringBuilder();
try {
InputStreamReader is = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(is);
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
if (lineEnding != null) {
sb.append(lineEnding);
}
}
reader.close();
} catch (Throwable t) {
System.err.println("Failed to read content of " + file.getAbsolutePath());
t.printStackTrace();
}
return sb.toString();
}
/**
* Returns the string content of the specified file.
*
* @param file
* @param lineEnding
* @return the string content of the file
*/
public static String readContent(File file, String lineEnding) {
StringBuilder sb = new StringBuilder();
try {
InputStreamReader is = new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"));
BufferedReader reader = new BufferedReader(is);
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line);
if (lineEnding != null) {
sb.append(lineEnding);
}
}
reader.close();
} catch (Throwable t) {
System.err.println("Failed to read content of " + file.getAbsolutePath());
t.printStackTrace();
}
return sb.toString();
}
}

+ 568
- 572
src/main/java/com/iciql/util/Utils.java
File diff suppressed because it is too large
View File


+ 206
- 208
src/main/java/com/iciql/util/WeakIdentityHashMap.java View File

@@ -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();
}
}

+ 3
- 2
src/main/java/com/iciql/util/package.html View File

@@ -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.

+ 3
- 3
src/main/java/java/lang/AutoCloseable.java View File

@@ -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.

+ 100
- 101
src/test/java/com/iciql/test/AliasMapTest.java View File

@@ -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();
}
}

+ 160
- 161
src/test/java/com/iciql/test/AnnotationsTest.java View File

@@ -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());
}
}

}

+ 137
- 138
src/test/java/com/iciql/test/BooleanModelTest.java View File

@@ -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();
}
}

+ 83
- 84
src/test/java/com/iciql/test/ClobTest.java View File

@@ -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;
}
}
}

+ 168
- 169
src/test/java/com/iciql/test/ConcurrencyTest.java View File

@@ -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();
}
}
}

+ 54
- 55
src/test/java/com/iciql/test/DataTypeAdapterTest.java View File

@@ -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 {
}

}

+ 31
- 32
src/test/java/com/iciql/test/DefaultValuesTest.java View File

@@ -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());
}
}
}

+ 121
- 122
src/test/java/com/iciql/test/EnumsTest.java View File

@@ -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;
}
}

+ 47
- 48
src/test/java/com/iciql/test/ForeignKeyTest.java View File

@@ -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());
}
}
}

+ 633
- 635
src/test/java/com/iciql/test/IciqlSuite.java
File diff suppressed because it is too large
View File


+ 120
- 122
src/test/java/com/iciql/test/JoinTest.java View File

@@ -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);
}
}
}

+ 143
- 144
src/test/java/com/iciql/test/ModelsTest.java View File

@@ -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());
}
}

+ 0
- 0
src/test/java/com/iciql/test/NestedConditionsTest.java View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save