diff options
-rw-r--r-- | NOTICE | 8 | ||||
-rw-r--r-- | docs/01_model_classes.mkd | 124 | ||||
-rw-r--r-- | docs/05_releases.mkd | 3 | ||||
-rw-r--r-- | src/com/iciql/Query.java | 293 | ||||
-rw-r--r-- | src/com/iciql/QueryWhere.java | 264 | ||||
-rw-r--r-- | src/com/iciql/TableDefinition.java | 12 | ||||
-rw-r--r-- | src/com/iciql/util/Utils.java | 4 | ||||
-rw-r--r-- | tests/com/iciql/test/AliasMapTest.java | 109 | ||||
-rw-r--r-- | tests/com/iciql/test/PrimitivesTest.java | 46 | ||||
-rw-r--r-- | tests/com/iciql/test/models/PrimitivesModel.java | 17 |
10 files changed, 743 insertions, 137 deletions
@@ -36,6 +36,14 @@ H2 Database Eclipse Public License, Version 1.0.
http://code.google.com/p/h2database
+
+---------------------------------------------------------------------------
+HSQL Database
+---------------------------------------------------------------------------
+ hsqldb, released under the
+ BSD.
+
+ http://hsqldb.org
---------------------------------------------------------------------------
MarkdownPapers
diff --git a/docs/01_model_classes.mkd b/docs/01_model_classes.mkd index cd52c2b..1b370d8 100644 --- a/docs/01_model_classes.mkd +++ b/docs/01_model_classes.mkd @@ -8,64 +8,74 @@ Alternatively, model classes can be automatically generated by iciql using the m ### Configuration Requirements and Limitations
1. Your model class **must** provide a public default constructor.
-2. All columns are assumed NULLABLE unless explicitly set *@IQColumn(nullable = false)*.
-3. Only the specified types are supported. Other types such as arrays and custom types are not supported.
-4. Triggers, views, and other advanced database features are not supported.
+2. All **Object** columns are assumed NULLABLE unless explicitly set *@IQColumn(nullable = false)*.
+3. All **Primitive** columns are assumed NOT NULLABLE unless explicitly set *@IQColumn(nullable = true)*.
+4. Only the specified types are supported. Any other types are not supported.
+5. Triggers, views, and other advanced database features are not supported.
-### Fully Supported Data Types
-The following data types can be used for all iciql expressions.
+### Supported Data Types
<table>
-<tr><th colspan="2">Standard SQL Types</th></tr>
-<tr><td>java.lang.String</td>
+<tr><td colspan="3"><b>Fully Supported Types</b><br/>
+can be used for all iciql expressions
+</tr>
+<tr><th>Object</th><th>Primitive</th><th>SQL Type</th></tr>
+<tr><td>java.lang.String</td><td></td>
<td>VARCHAR *(length > 0)* or CLOB *(length == 0)*</td></tr>
-<tr><td>java.lang.Boolean</td>
-<td>BIT</td></tr>
+<tr><td>java.lang.Boolean</td><td>boolean</td>
+<td>BOOLEAN</td></tr>
-<tr><td>java.lang.Byte</td>
+<tr><td>java.lang.Byte</td><td>byte</td>
<td>TINYINT</td></tr>
-<tr><td>java.lang.Short</td>
+<tr><td>java.lang.Short</td><td>short</td>
<td>SMALLINT</td></tr>
-<tr><td>java.lang.Integer</td>
+<tr><td>java.lang.Integer</td><td>int</td>
<td>INT</td></tr>
-<tr><td>java.lang.Long</td>
+<tr><td>java.lang.Long</td><td>long</td>
<td>BIGINT</td></tr>
-<tr><td>java.lang.Float</td>
+<tr><td>java.lang.Float</td><td>float</td>
<td>REAL</td></tr>
-<tr><td>java.lang.Double</td>
+<tr><td>java.lang.Double</td><td>double</td>
<td>DOUBLE</td></tr>
-<tr><td>java.math.BigDecimal</td>
+<tr><td>java.math.BigDecimal</td><td></td>
<td>DECIMAL *(length == 0)* or DECIMAL(length,scale) *(length > 0)*</td></tr>
-<tr><td>java.sql.Date</td>
+<tr><td>java.sql.Date</td><td></td>
<td>DATE</td></tr>
-<tr><td>java.sql.Time</td>
+<tr><td>java.sql.Time</td><td></td>
<td>TIME</td></tr>
-<tr><td>java.sql.Timestamp</td>
+<tr><td>java.sql.Timestamp</td><td></td>
<td>TIMESTAMP</td></tr>
-<tr><td>java.util.Date</td>
+<tr><td>java.util.Date</td><td></td>
<td>TIMESTAMP</td></tr>
-<tr><td>java.lang.Enum.name()<br/>*default type*</td>
+<tr><td>java.lang.Enum.name()<br/>*default type*</td><td></td>
<td>VARCHAR *(length > 0)* or CLOB *(length == 0)*<br/>*EnumType.NAME*</td></tr>
-<tr><td>java.lang.Enum.ordinal()</td>
+<tr><td>java.lang.Enum.ordinal()</td><td></td>
<td>INT<br/>*EnumType.ORDINAL*</td></tr>
-<tr><td>java.lang.Enum implements<br/>*com.iciql.Iciql.EnumId.enumId()*</td>
+<tr><td>java.lang.Enum implements<br/>*com.iciql.Iciql.EnumId.enumId()*</td><td></td>
<td>INT<br/>*EnumType.ENUMID*</td></tr>
-<tr><th colspan="2">H2 Database Types</th></tr>
-<tr><td>java.util.UUID</td>
+<tr><td colspan="3"><b>Partially Supported Types</b><br/>
+can not be directly referenced in an expression</td></tr>
+<tr><td>byte []</td><td></td>
+<td>BLOB</td><tr/>
+
+<tr><td colspan="3"><b>H2 Database Types</b><br/>
+fully supported when paired with an H2 database
+</td></tr>
+<tr><td>java.util.UUID</td><td></td>
<td>UUID</td><tr/>
</table>
@@ -74,70 +84,6 @@ The following data types can be used for all iciql expressions. The reverse lookup used for model generation, SQL type -> Java type, contains more mappings.<br/>
Please consult the `com.iciql.ModelUtils` class for details.
-### Partially Supported Data Types
-The following data types can be mapped to columns for all general statements <u>BUT</u> these field types may **not** be used to specify **compile-time** clauses or constraints.
-
-<table>
-<tr><td>byte []</td>
-<td>BLOB</td></tr>
-
-<tr><td>boolean</td>
-<td>BIT</td></tr>
-
-<tr><td>byte</td>
-<td>TINYINT</td></tr>
-
-<tr><td>short</td>
-<td>SMALLINT</td></tr>
-
-<tr><td>int</td>
-<td>INT</td></tr>
-
-<tr><td>long</td>
-<td>BIGINT</td></tr>
-
-<tr><td>float</td>
-<td>REAL</td></tr>
-
-<tr><td>double</td>
-<td>DOUBLE</td></tr>
-
-</table>
-
-#### Partially Supported Data Types Example
-%BEGINCODE%
-class Primitives {
- @IQColumn(primaryKey = true)
- int id;
-
- @IQColumn
- String name;
-
- public Primitives() {
- }
-
- public Primitives(int id, String name) {
- this.id = id;
- this.name = name;
- }
-}
-
-Primitives p = new Primitives();
-
-// the following expressions compile, but will throw iciql runtime exceptions
-db.from(p).where(p.id).is(100).selectFirst();
-db.from(p).where(p.id).atLeast(10).select();
-
-// the following expressions will work as expected
-db.from(p).select();
-db.from(p).where("id = ?", 100).selectFirst();
-db.from(p).where("id >= ?", 10).select();
-db.insert(new Primitives(150, "test"));
-db.update(new Primitives(150, "modified"));
-db.delete(new Primitives(150, "test"));
-%ENDCODE%
-
-
## Annotation Configuration
The recommended approach to setup a model class is to annotate the class and field declarations.
diff --git a/docs/05_releases.mkd b/docs/05_releases.mkd index a0905e5..22ebb2e 100644 --- a/docs/05_releases.mkd +++ b/docs/05_releases.mkd @@ -7,6 +7,7 @@ **%VERSION%** ([zip](http://code.google.com/p/iciql/downloads/detail?name=%ZIP%)|[jar](http://code.google.com/p/iciql/downloads/detail?name=%JAR%)) *released %BUILDDATE%*
- api change release (API v4)
+- full support for primitives in all clauses
- DECIMAL(length, scale) support
- unspecified length String fields are now CLOB instead of TEXT. dialects can intercept this and convert to another type. e.g. MySQL dialect can change CLOB to TEXT.
- Boolean now maps to BOOLEAN instead of BIT
@@ -15,7 +16,7 @@ - moved dialects back to main package
- improved automatic dialect determination on pooled connections
- moved create table and create index statement generation into dialects
-- added HSQL dialect. HSQL fails 4 out of 49 unit tests: 2 failures are unimplemented merge, 1 has been filed as a bug in HSQL.
+- added HSQL dialect. HSQL fails 4 out of 49 unit tests: 2 failures are unimplemented merge, and 1 has been filed as a [bug in HSQL](https://sourceforge.net/tracker/?func=detail&aid=3390047&group_id=23316&atid=378131).
- added MySQL dialect. untested.
- renamed <b>_ iq_versions</b> table to *iq_versions* since leading _ character is troublesome for some databases.
- @IQColumn(allowNull=true) -> @IQColumn(nullable=true)
diff --git a/src/com/iciql/Query.java b/src/com/iciql/Query.java index 5c2d225..699574d 100644 --- a/src/com/iciql/Query.java +++ b/src/com/iciql/Query.java @@ -23,6 +23,7 @@ import java.sql.Clob; import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
@@ -49,7 +50,7 @@ public class Query<T> { private ArrayList<SelectTable<T>> joins = Utils.newArrayList();
private final IdentityHashMap<Object, SelectColumn<T>> aliasMap = Utils.newIdentityHashMap();
private ArrayList<OrderExpression<T>> orderByList = Utils.newArrayList();
- private Object[] groupByExpressions;
+ private ArrayList<Object> groupByExpressions = Utils.newArrayList();
private long limit;
private long offset;
@@ -148,10 +149,80 @@ public class Query<T> { return new UpdateColumnSet<T, A>(this, field);
}
+ public UpdateColumnSet<T, Boolean> set(boolean field) {
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Byte> set(byte field) {
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Short> set(short field) {
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Integer> set(int field) {
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Long> set(long field) {
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Float> set(float field) {
+ return setPrimitive(field);
+ }
+
+ public UpdateColumnSet<T, Double> set(double field) {
+ return setPrimitive(field);
+ }
+
+ private <A> UpdateColumnSet<T, A> setPrimitive(A field) {
+ A alias = getPrimitiveAliasByValue(field);
+ if (alias == null) {
+ // this will result in an unmapped field exception
+ return set(field);
+ }
+ return set(alias);
+ }
+
public <A> UpdateColumnIncrement<T, A> increment(A field) {
return new UpdateColumnIncrement<T, A>(this, field);
}
+ public UpdateColumnIncrement<T, Byte> increment(byte field) {
+ return incrementPrimitive(field);
+ }
+
+ public UpdateColumnIncrement<T, Short> increment(short field) {
+ return incrementPrimitive(field);
+ }
+
+ public UpdateColumnIncrement<T, Integer> increment(int field) {
+ return incrementPrimitive(field);
+ }
+
+ public UpdateColumnIncrement<T, Long> increment(long field) {
+ return incrementPrimitive(field);
+ }
+
+ public UpdateColumnIncrement<T, Float> increment(float field) {
+ return incrementPrimitive(field);
+ }
+
+ public UpdateColumnIncrement<T, Double> increment(double field) {
+ return incrementPrimitive(field);
+ }
+
+ private <A> UpdateColumnIncrement<T, A> incrementPrimitive(A field) {
+ A alias = getPrimitiveAliasByValue(field);
+ if (alias == null) {
+ // this will result in an unmapped field exception
+ return increment(field);
+ }
+ return increment(alias);
+ }
+
public int update() {
if (updateColumnDeclarations.size() == 0) {
throw new IciqlException("Missing set or increment call.");
@@ -249,6 +320,105 @@ public class Query<T> { return stat;
}
+ /**
+ * Begin a primitive boolean field condition clause.
+ *
+ * @param x
+ * the primitive boolean field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Boolean> where(boolean x) {
+ return wherePrimitive(x);
+ }
+
+ /**
+ * Begin a primitive short field condition clause.
+ *
+ * @param x
+ * the primitive short field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Byte> where(byte x) {
+ return wherePrimitive(x);
+ }
+
+ /**
+ * Begin a primitive short field condition clause.
+ *
+ * @param x
+ * the primitive short field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Short> where(short x) {
+ return wherePrimitive(x);
+ }
+
+ /**
+ * Begin a primitive int field condition clause.
+ *
+ * @param x
+ * the primitive int field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Integer> where(int x) {
+ return wherePrimitive(x);
+ }
+
+ /**
+ * Begin a primitive long field condition clause.
+ *
+ * @param x
+ * the primitive long field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Long> where(long x) {
+ return wherePrimitive(x);
+ }
+
+ /**
+ * Begin a primitive float field condition clause.
+ *
+ * @param x
+ * the primitive float field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Float> where(float x) {
+ return wherePrimitive(x);
+ }
+
+ /**
+ * Begin a primitive double field condition clause.
+ *
+ * @param x
+ * the primitive double field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Double> where(double x) {
+ return wherePrimitive(x);
+ }
+
+ /**
+ * Begins a primitive field condition clause.
+ *
+ * @param value
+ * @return a query condition to continue building the condition
+ */
+ private <A> QueryCondition<T, A> wherePrimitive(A value) {
+ A alias = getPrimitiveAliasByValue(value);
+ if (alias == null) {
+ // this will result in an unmapped field exception
+ return where(value);
+ }
+ return where(alias);
+ }
+
+ /**
+ * Begin an Object field condition clause.
+ *
+ * @param x
+ * the mapped object to query
+ * @return a query condition to continue building the condition
+ */
public <A> QueryCondition<T, A> where(A x) {
return new QueryCondition<T, A>(this, x);
}
@@ -306,6 +476,48 @@ public class Query<T> { return this;
}
+ public Query<T> orderBy(boolean field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(byte field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(short field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(int field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(long field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(float field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> orderBy(double field) {
+ return orderByPrimitive(field);
+ }
+
+ Query<T> orderByPrimitive(Object field) {
+ Object alias = getPrimitiveAliasByValue(field);
+ if (alias == null) {
+ return orderBy(field);
+ }
+ return orderBy(alias);
+ }
+
+ public Query<T> orderBy(Object expr) {
+ OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false);
+ addOrderBy(e);
+ return this;
+ }
+
/**
* Order by a number of columns.
*
@@ -328,8 +540,49 @@ public class Query<T> { return this;
}
+ public Query<T> groupBy(boolean field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> groupBy(byte field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> groupBy(short field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> groupBy(int field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> groupBy(long field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> groupBy(float field) {
+ return orderByPrimitive(field);
+ }
+
+ public Query<T> groupBy(double field) {
+ return orderByPrimitive(field);
+ }
+
+ Query<T> groupByPrimitive(Object field) {
+ Object alias = getPrimitiveAliasByValue(field);
+ if (alias == null) {
+ return groupBy(field);
+ }
+ return groupBy(alias);
+ }
+
+ public Query<T> groupBy(Object expr) {
+ groupByExpressions.add(expr);
+ return this;
+ }
+
public Query<T> groupBy(Object... groupBy) {
- this.groupByExpressions = groupBy;
+ this.groupByExpressions.addAll(Arrays.asList(groupBy));
return this;
}
@@ -362,7 +615,7 @@ public class Query<T> { addParameter(stat, alias, value);
return;
}
- SelectColumn<T> col = aliasMap.get(value);
+ SelectColumn<T> col = getColumnByReference(value);
if (col != null) {
col.appendSQL(stat);
return;
@@ -402,7 +655,7 @@ public class Query<T> { private void addParameter(SQLStatement stat, Object alias, Object value) {
if (alias != null && value.getClass().isEnum()) {
- SelectColumn<T> col = aliasMap.get(alias);
+ SelectColumn<T> col = getColumnByReference(alias);
EnumType type = col.getFieldDefinition().enumType;
Enum<?> anEnum = (Enum<?>) value;
Object y = Utils.convertEnum(anEnum, type);
@@ -437,7 +690,7 @@ public class Query<T> { join.appendSQLAsJoin(stat, this);
}
appendWhere(stat);
- if (groupByExpressions != null) {
+ if (!groupByExpressions.isEmpty()) {
stat.appendSQL(" GROUP BY ");
int i = 0;
for (Object obj : groupByExpressions) {
@@ -493,8 +746,34 @@ public class Query<T> { return !joins.isEmpty();
}
- SelectColumn<T> getSelectColumn(Object obj) {
- return aliasMap.get(obj);
+ /**
+ * This method returns a mapped Object field by its reference.
+ *
+ * @param obj
+ * @return
+ */
+ private SelectColumn<T> getColumnByReference(Object obj) {
+ SelectColumn<T> col = aliasMap.get(obj);
+ return col;
+ }
+
+ /**
+ * This method returns the alias of a mapped primitive field by its value.
+ *
+ * @param obj
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ <A> A getPrimitiveAliasByValue(A obj) {
+ for (Object alias : aliasMap.keySet()) {
+ if (alias.equals(obj)) {
+ SelectColumn<T> match = aliasMap.get(alias);
+ if (match.getFieldDefinition().isPrimitive) {
+ return (A) alias;
+ }
+ }
+ }
+ return null;
}
void addOrderBy(OrderExpression<T> expr) {
diff --git a/src/com/iciql/QueryWhere.java b/src/com/iciql/QueryWhere.java index 9071b52..727285d 100644 --- a/src/com/iciql/QueryWhere.java +++ b/src/com/iciql/QueryWhere.java @@ -34,11 +34,189 @@ public class QueryWhere<T> { this.query = query;
}
+ /**
+ * Specify an AND condition with a mapped primitive boolean.
+ *
+ * @param x
+ * the primitive boolean field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Boolean> and(boolean x) {
+ return addPrimitive(ConditionAndOr.AND, x);
+ }
+
+ /**
+ * Specify an AND condition with a mapped primitive byte.
+ *
+ * @param x
+ * the primitive byte field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Byte> and(byte x) {
+ return addPrimitive(ConditionAndOr.AND, x);
+ }
+
+ /**
+ * Specify an AND condition with a mapped primitive short.
+ *
+ * @param x
+ * the primitive short field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Short> and(short x) {
+ return addPrimitive(ConditionAndOr.AND, x);
+ }
+
+ /**
+ * Specify an AND condition with a mapped primitive int.
+ *
+ * @param x
+ * the primitive int field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Integer> and(int x) {
+ return addPrimitive(ConditionAndOr.AND, x);
+ }
+
+ /**
+ * Specify an AND condition with a mapped primitive long.
+ *
+ * @param x
+ * the primitive long field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Long> and(long x) {
+ return addPrimitive(ConditionAndOr.AND, x);
+ }
+
+ /**
+ * Specify an AND condition with a mapped primitive float.
+ *
+ * @param x
+ * the primitive float field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Float> and(float x) {
+ return addPrimitive(ConditionAndOr.AND, x);
+ }
+
+ /**
+ * Specify an AND condition with a mapped primitive double.
+ *
+ * @param x
+ * the primitive double field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Double> and(double x) {
+ return addPrimitive(ConditionAndOr.AND, x);
+ }
+
+ private <A> QueryCondition<T, A> addPrimitive(ConditionAndOr condition, A x) {
+ query.addConditionToken(condition);
+ A alias = query.getPrimitiveAliasByValue(x);
+ if (alias == null) {
+ // this will result in an unmapped field exception
+ return new QueryCondition<T, A>(query, x);
+ }
+ return new QueryCondition<T, A>(query, alias);
+ }
+
+ /**
+ * Specify an AND condition with a mapped Object field.
+ *
+ * @param x
+ * the Object field to query
+ * @return a query condition to continue building the condition
+ */
public <A> QueryCondition<T, A> and(A x) {
query.addConditionToken(ConditionAndOr.AND);
return new QueryCondition<T, A>(query, x);
}
+ /**
+ * Specify an OR condition with a mapped primitive boolean.
+ *
+ * @param x
+ * the primitive boolean field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Boolean> or(boolean x) {
+ return addPrimitive(ConditionAndOr.OR, x);
+ }
+
+ /**
+ * Specify an OR condition with a mapped primitive byte.
+ *
+ * @param x
+ * the primitive byte field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Byte> or(byte x) {
+ return addPrimitive(ConditionAndOr.OR, x);
+ }
+
+ /**
+ * Specify an OR condition with a mapped primitive short.
+ *
+ * @param x
+ * the primitive short field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Short> or(short x) {
+ return addPrimitive(ConditionAndOr.OR, x);
+ }
+
+ /**
+ * Specify an OR condition with a mapped primitive int.
+ *
+ * @param x
+ * the primitive int field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Integer> or(int x) {
+ return addPrimitive(ConditionAndOr.OR, x);
+ }
+
+ /**
+ * Specify an OR condition with a mapped primitive long.
+ *
+ * @param x
+ * the primitive long field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Long> or(long x) {
+ return addPrimitive(ConditionAndOr.OR, x);
+ }
+
+ /**
+ * Specify an OR condition with a mapped primitive float.
+ *
+ * @param x
+ * the primitive float field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Float> or(float x) {
+ return addPrimitive(ConditionAndOr.OR, x);
+ }
+
+ /**
+ * Specify an OR condition with a mapped primitive double.
+ *
+ * @param x
+ * the primitive double field to query
+ * @return a query condition to continue building the condition
+ */
+ public QueryCondition<T, Double> or(double x) {
+ return addPrimitive(ConditionAndOr.OR, x);
+ }
+
+ /**
+ * Specify an OR condition with a mapped Object field.
+ *
+ * @param x
+ * the Object field to query
+ * @return a query condition to continue building the condition
+ */
public <A> QueryCondition<T, A> or(A x) {
query.addConditionToken(ConditionAndOr.OR);
return new QueryCondition<T, A>(query, x);
@@ -88,7 +266,86 @@ public class QueryWhere<T> { }
/**
- * Order by a number of columns.
+ * Order by primitive boolean field
+ *
+ * @param field
+ * a primitive boolean field
+ * @return the query
+ */
+ public QueryWhere<T> orderBy(boolean field) {
+ return orderByPrimitive(field);
+ }
+
+ /**
+ * Order by primitive byte field
+ *
+ * @param field
+ * a primitive byte field
+ * @return the query
+ */
+ public QueryWhere<T> orderBy(byte field) {
+ return orderByPrimitive(field);
+ }
+
+ /**
+ * Order by primitive short field
+ *
+ * @param field
+ * a primitive short field
+ * @return the query
+ */
+ public QueryWhere<T> orderBy(short field) {
+ return orderByPrimitive(field);
+ }
+
+ public QueryWhere<T> orderBy(int field) {
+ return orderByPrimitive(field);
+ }
+
+ /**
+ * Order by primitive long field
+ *
+ * @param field
+ * a primitive long field
+ * @return the query
+ */
+ public QueryWhere<T> orderBy(long field) {
+ return orderByPrimitive(field);
+ }
+
+ /**
+ * Order by primitive float field
+ *
+ * @param field
+ * a primitive float field
+ * @return the query
+ */
+ public QueryWhere<T> orderBy(float field) {
+ return orderByPrimitive(field);
+ }
+
+ /**
+ * Order by primitive double field
+ *
+ * @param field
+ * a primitive double field
+ * @return the query
+ */
+ public QueryWhere<T> orderBy(double field) {
+ return orderByPrimitive(field);
+ }
+
+ private QueryWhere<T> orderByPrimitive(Object field) {
+ query.orderByPrimitive(field);
+ return this;
+ }
+
+ public QueryWhere<T> orderBy(Object field) {
+ query.orderBy(field);
+ return this;
+ }
+ /**
+ * Order by a number of Object columns.
*
* @param expressions
* the order by expressions
@@ -96,10 +353,7 @@ public class QueryWhere<T> { */
public QueryWhere<T> orderBy(Object... expressions) {
- for (Object expr : expressions) {
- OrderExpression<T> e = new OrderExpression<T>(query, expr, false, false, false);
- query.addOrderBy(e);
- }
+ query.orderBy(expressions);
return this;
}
diff --git a/src/com/iciql/TableDefinition.java b/src/com/iciql/TableDefinition.java index 571ab1f..687ba53 100644 --- a/src/com/iciql/TableDefinition.java +++ b/src/com/iciql/TableDefinition.java @@ -79,6 +79,7 @@ public class TableDefinition<T> { boolean nullable;
String defaultValue;
EnumType enumType;
+ boolean isPrimitive;
Object getValue(Object obj) {
try {
@@ -345,6 +346,7 @@ public class TableDefinition<T> { boolean reflectiveMatch = isPublic && !byAnnotationsOnly;
if (reflectiveMatch || hasAnnotation) {
FieldDefinition fieldDef = new FieldDefinition();
+ fieldDef.isPrimitive = f.getType().isPrimitive();
fieldDef.field = f;
fieldDef.columnName = columnName;
fieldDef.isAutoIncrement = isAutoIncrement;
@@ -658,14 +660,4 @@ public class TableDefinition<T> { query.appendSQL(stat, x, obj);
}
}
-
- <Y, X> void copyAttributeValues(Query<Y> query, X to, X map) {
- for (FieldDefinition def : fields) {
- Object obj = def.getValue(map);
- SelectColumn<Y> col = query.getSelectColumn(obj);
- Object value = col.getCurrentValue();
- def.setValue(to, value);
- }
- }
-
}
diff --git a/src/com/iciql/util/Utils.java b/src/com/iciql/util/Utils.java index 6f9746d..d9e065c 100644 --- a/src/com/iciql/util/Utils.java +++ b/src/com/iciql/util/Utils.java @@ -140,6 +140,7 @@ public class Utils { } else if (clazz == double.class || clazz == Double.class) {
return (T) new Double(COUNTER.getAndIncrement());
} else if (clazz == boolean.class || clazz == Boolean.class) {
+ COUNTER.getAndIncrement();
return (T) new Boolean(false);
} else if (clazz == BigDecimal.class) {
return (T) new BigDecimal(COUNTER.getAndIncrement());
@@ -154,12 +155,15 @@ public class Utils { } else if (clazz == java.util.Date.class) {
return (T) new java.util.Date(COUNTER.getAndIncrement());
} else if (clazz == byte[].class) {
+ COUNTER.getAndIncrement();
return (T) new byte[0];
} else if (clazz.isEnum()) {
+ COUNTER.getAndIncrement();
// enums can not be instantiated reflectively
// return first constant as reference
return clazz.getEnumConstants()[0];
} else if (clazz == java.util.UUID.class) {
+ COUNTER.getAndIncrement();
return (T) UUID.randomUUID();
}
try {
diff --git a/tests/com/iciql/test/AliasMapTest.java b/tests/com/iciql/test/AliasMapTest.java index f0d5c15..0f91365 100644 --- a/tests/com/iciql/test/AliasMapTest.java +++ b/tests/com/iciql/test/AliasMapTest.java @@ -18,33 +18,122 @@ package com.iciql.test; import static org.junit.Assert.assertEquals; - -import java.util.List; +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; /** - * 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 + * 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 testAliasMapping() throws Exception { + public void testObjectAliasMapping() throws Exception { Db db = IciqlSuite.openDb(); 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; + Product p = new Product(); - List<Product> products = db.from(p).where(p.unitsInStock).is(9).orderBy(p.productId).select(); + // 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()); + } + + db.close(); + } + + /** + * Confirms that primitive aliases ARE mapped by value. + */ + @Test + public void testPrimitiveAliasMapping() throws Exception { + Db db = IciqlSuite.openDb(); + PrimitivesModel model = new PrimitivesModel(); + model.myLong = 100L; + db.insert(model); + model.myLong = 200L; + db.insert(model); - assertEquals("[]", products.toString()); + // 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(); } }
\ No newline at end of file diff --git a/tests/com/iciql/test/PrimitivesTest.java b/tests/com/iciql/test/PrimitivesTest.java index 1cdeb1c..a200b57 100644 --- a/tests/com/iciql/test/PrimitivesTest.java +++ b/tests/com/iciql/test/PrimitivesTest.java @@ -16,8 +16,12 @@ package com.iciql.test;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import java.util.Collections;
+import java.util.List;
+
import org.junit.Test;
import com.iciql.Db;
@@ -32,14 +36,17 @@ public class PrimitivesTest { public void testPrimitives() {
Db db = IciqlSuite.openDb();
- // insert random model
- PrimitivesModel model = new PrimitivesModel();
- db.insert(model);
+ // insert random models in reverse order
+ List<PrimitivesModel> models = PrimitivesModel.getList();
+ PrimitivesModel model = models.get(0);
+ Collections.reverse(models);
+ // insert them in reverse order
+ db.insertAll(models);
PrimitivesModel p = new PrimitivesModel();
// retrieve model and compare
- PrimitivesModel retrievedModel = db.from(p).selectFirst();
+ PrimitivesModel retrievedModel = db.from(p).orderBy(p.myLong).selectFirst();
assertTrue(model.equivalentTo(retrievedModel));
retrievedModel = db.from(p).where("mylong = ? and myinteger = ?", model.myLong, model.myInteger)
@@ -47,17 +54,26 @@ public class PrimitivesTest { assertTrue(model.equivalentTo(retrievedModel));
// retrieve with conditions and compare
- // StatementLogger.activateConsoleLogger();
- // retrievedModel =
- // db.from(p).where(p.myLong).is(model.myLong).and(p.myInteger).is(model.myInteger)
- // .selectFirst();
- // assertTrue(model.equivalentTo(retrievedModel));
- //
- // // update myInteger and compare
- // db.from(p).set(p.myInteger).to(10).where(p.myLong).is(model.myLong).update();
- // retrievedModel = db.from(p).selectFirst();
-
- // assertEquals(10, retrievedModel.myInteger);
+ retrievedModel = db.from(p).where(p.myLong).is(model.myLong).and(p.myInteger).is(model.myInteger)
+ .selectFirst();
+ assertTrue(model.equivalentTo(retrievedModel));
+
+ // set myInteger & myDouble
+ db.from(p).set(p.myInteger).to(10).set(p.myDouble).to(3.0d).where(p.myLong).is(model.myLong).update();
+ retrievedModel = db.from(p).orderBy(p.myLong).selectFirst();
+
+ assertEquals(10, retrievedModel.myInteger);
+ assertEquals(3d, retrievedModel.myDouble, 0.001d);
+
+ // increment my double by pi
+ db.from(p).increment(p.myDouble).by(3.14d).update();
+ retrievedModel = db.from(p).orderBy(p.myLong).selectFirst();
+ assertEquals(6.14d, retrievedModel.myDouble, 0.001d);
+
+ // test order by
+ List<PrimitivesModel> list = db.from(p).orderBy(p.myLong).select();
+ assertEquals(models.size(), list.size());
+ assertEquals("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]", list.toString());
db.close();
}
diff --git a/tests/com/iciql/test/models/PrimitivesModel.java b/tests/com/iciql/test/models/PrimitivesModel.java index 2d1a7da..07c1611 100644 --- a/tests/com/iciql/test/models/PrimitivesModel.java +++ b/tests/com/iciql/test/models/PrimitivesModel.java @@ -15,6 +15,8 @@ */
package com.iciql.test.models;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Random;
import com.iciql.Iciql.IQColumn;
@@ -69,4 +71,19 @@ public class PrimitivesModel { same &= myFloat == p.myFloat;
return same;
}
+
+ public static List<PrimitivesModel> getList() {
+ List<PrimitivesModel> list = new ArrayList<PrimitivesModel>();
+ for (int i = 1; i <= 10; i++) {
+ PrimitivesModel p = new PrimitivesModel();
+ p.myLong = i;
+ list.add(p);
+ }
+ return list;
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(myLong);
+ }
}
|