From 78d9f3ecbd9203a5ad63138037960e265509efba Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 12 Aug 2011 13:04:29 -0400 Subject: [PATCH] Full primitives support. --- NOTICE | 8 + docs/01_model_classes.mkd | 124 +++----- docs/05_releases.mkd | 3 +- src/com/iciql/Query.java | 293 +++++++++++++++++- src/com/iciql/QueryWhere.java | 264 +++++++++++++++- src/com/iciql/TableDefinition.java | 12 +- src/com/iciql/util/Utils.java | 4 + tests/com/iciql/test/AliasMapTest.java | 109 ++++++- tests/com/iciql/test/PrimitivesTest.java | 46 ++- .../iciql/test/models/PrimitivesModel.java | 17 + 10 files changed, 743 insertions(+), 137 deletions(-) diff --git a/NOTICE b/NOTICE index 8b6516c..76b6a69 100644 --- a/NOTICE +++ b/NOTICE @@ -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 - - + + + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + + + + + +
Standard SQL Types
java.lang.String
Fully Supported Types
+can be used for all iciql expressions +
ObjectPrimitiveSQL Type
java.lang.String VARCHAR *(length > 0)* or CLOB *(length == 0)*
java.lang.BooleanBIT
java.lang.BooleanbooleanBOOLEAN
java.lang.Byte
java.lang.Bytebyte TINYINT
java.lang.Short
java.lang.Shortshort SMALLINT
java.lang.Integer
java.lang.Integerint INT
java.lang.Long
java.lang.Longlong BIGINT
java.lang.Float
java.lang.Floatfloat REAL
java.lang.Double
java.lang.Doubledouble DOUBLE
java.math.BigDecimal
java.math.BigDecimal DECIMAL *(length == 0)* or DECIMAL(length,scale) *(length > 0)*
java.sql.Date
java.sql.Date DATE
java.sql.Time
java.sql.Time TIME
java.sql.Timestamp
java.sql.Timestamp TIMESTAMP
java.util.Date
java.util.Date TIMESTAMP
java.lang.Enum.name()
*default type*
java.lang.Enum.name()
*default type*
VARCHAR *(length > 0)* or CLOB *(length == 0)*
*EnumType.NAME*
java.lang.Enum.ordinal()
java.lang.Enum.ordinal() INT
*EnumType.ORDINAL*
java.lang.Enum implements
*com.iciql.Iciql.EnumId.enumId()*
java.lang.Enum implements
*com.iciql.Iciql.EnumId.enumId()*
INT
*EnumType.ENUMID*
H2 Database Types
java.util.UUID
Partially Supported Types
+can not be directly referenced in an expression
byte []BLOB
H2 Database Types
+fully supported when paired with an H2 database +
java.util.UUID UUID
@@ -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.
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 BUT these field types may **not** be used to specify **compile-time** clauses or constraints. - - - - - - - - - - - - - - - - - - - - - - - - - - -
byte []BLOB
booleanBIT
byteTINYINT
shortSMALLINT
intINT
longBIGINT
floatREAL
doubleDOUBLE
- -#### 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 _ iq_versions 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 { private ArrayList> joins = Utils.newArrayList(); private final IdentityHashMap> aliasMap = Utils.newIdentityHashMap(); private ArrayList> orderByList = Utils.newArrayList(); - private Object[] groupByExpressions; + private ArrayList groupByExpressions = Utils.newArrayList(); private long limit; private long offset; @@ -148,10 +149,80 @@ public class Query { return new UpdateColumnSet(this, field); } + public UpdateColumnSet set(boolean field) { + return setPrimitive(field); + } + + public UpdateColumnSet set(byte field) { + return setPrimitive(field); + } + + public UpdateColumnSet set(short field) { + return setPrimitive(field); + } + + public UpdateColumnSet set(int field) { + return setPrimitive(field); + } + + public UpdateColumnSet set(long field) { + return setPrimitive(field); + } + + public UpdateColumnSet set(float field) { + return setPrimitive(field); + } + + public UpdateColumnSet set(double field) { + return setPrimitive(field); + } + + private UpdateColumnSet setPrimitive(A field) { + A alias = getPrimitiveAliasByValue(field); + if (alias == null) { + // this will result in an unmapped field exception + return set(field); + } + return set(alias); + } + public UpdateColumnIncrement increment(A field) { return new UpdateColumnIncrement(this, field); } + public UpdateColumnIncrement increment(byte field) { + return incrementPrimitive(field); + } + + public UpdateColumnIncrement increment(short field) { + return incrementPrimitive(field); + } + + public UpdateColumnIncrement increment(int field) { + return incrementPrimitive(field); + } + + public UpdateColumnIncrement increment(long field) { + return incrementPrimitive(field); + } + + public UpdateColumnIncrement increment(float field) { + return incrementPrimitive(field); + } + + public UpdateColumnIncrement increment(double field) { + return incrementPrimitive(field); + } + + private UpdateColumnIncrement incrementPrimitive(A field) { + A alias = getPrimitiveAliasByValue(field); + if (alias == null) { + // this will result in an unmapped field exception + return increment(field); + } + return increment(alias); + } + public int update() { if (updateColumnDeclarations.size() == 0) { throw new IciqlException("Missing set or increment call."); @@ -249,6 +320,105 @@ public class Query { return stat; } + /** + * Begin a primitive boolean field condition clause. + * + * @param x + * the primitive boolean field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(boolean x) { + return wherePrimitive(x); + } + + /** + * Begin a primitive short field condition clause. + * + * @param x + * the primitive short field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(byte x) { + return wherePrimitive(x); + } + + /** + * Begin a primitive short field condition clause. + * + * @param x + * the primitive short field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(short x) { + return wherePrimitive(x); + } + + /** + * Begin a primitive int field condition clause. + * + * @param x + * the primitive int field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(int x) { + return wherePrimitive(x); + } + + /** + * Begin a primitive long field condition clause. + * + * @param x + * the primitive long field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(long x) { + return wherePrimitive(x); + } + + /** + * Begin a primitive float field condition clause. + * + * @param x + * the primitive float field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(float x) { + return wherePrimitive(x); + } + + /** + * Begin a primitive double field condition clause. + * + * @param x + * the primitive double field to query + * @return a query condition to continue building the condition + */ + public QueryCondition where(double x) { + return wherePrimitive(x); + } + + /** + * Begins a primitive field condition clause. + * + * @param value + * @return a query condition to continue building the condition + */ + private QueryCondition wherePrimitive(A value) { + A alias = getPrimitiveAliasByValue(value); + if (alias == null) { + // this will result in an unmapped field exception + return where(value); + } + return where(alias); + } + + /** + * Begin an Object field condition clause. + * + * @param x + * the mapped object to query + * @return a query condition to continue building the condition + */ public QueryCondition where(A x) { return new QueryCondition(this, x); } @@ -306,6 +476,48 @@ public class Query { return this; } + public Query orderBy(boolean field) { + return orderByPrimitive(field); + } + + public Query orderBy(byte field) { + return orderByPrimitive(field); + } + + public Query orderBy(short field) { + return orderByPrimitive(field); + } + + public Query orderBy(int field) { + return orderByPrimitive(field); + } + + public Query orderBy(long field) { + return orderByPrimitive(field); + } + + public Query orderBy(float field) { + return orderByPrimitive(field); + } + + public Query orderBy(double field) { + return orderByPrimitive(field); + } + + Query orderByPrimitive(Object field) { + Object alias = getPrimitiveAliasByValue(field); + if (alias == null) { + return orderBy(field); + } + return orderBy(alias); + } + + public Query orderBy(Object expr) { + OrderExpression e = new OrderExpression(this, expr, false, false, false); + addOrderBy(e); + return this; + } + /** * Order by a number of columns. * @@ -328,8 +540,49 @@ public class Query { return this; } + public Query groupBy(boolean field) { + return orderByPrimitive(field); + } + + public Query groupBy(byte field) { + return orderByPrimitive(field); + } + + public Query groupBy(short field) { + return orderByPrimitive(field); + } + + public Query groupBy(int field) { + return orderByPrimitive(field); + } + + public Query groupBy(long field) { + return orderByPrimitive(field); + } + + public Query groupBy(float field) { + return orderByPrimitive(field); + } + + public Query groupBy(double field) { + return orderByPrimitive(field); + } + + Query groupByPrimitive(Object field) { + Object alias = getPrimitiveAliasByValue(field); + if (alias == null) { + return groupBy(field); + } + return groupBy(alias); + } + + public Query groupBy(Object expr) { + groupByExpressions.add(expr); + return this; + } + public Query groupBy(Object... groupBy) { - this.groupByExpressions = groupBy; + this.groupByExpressions.addAll(Arrays.asList(groupBy)); return this; } @@ -362,7 +615,7 @@ public class Query { addParameter(stat, alias, value); return; } - SelectColumn col = aliasMap.get(value); + SelectColumn col = getColumnByReference(value); if (col != null) { col.appendSQL(stat); return; @@ -402,7 +655,7 @@ public class Query { private void addParameter(SQLStatement stat, Object alias, Object value) { if (alias != null && value.getClass().isEnum()) { - SelectColumn col = aliasMap.get(alias); + SelectColumn 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 { 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 { return !joins.isEmpty(); } - SelectColumn getSelectColumn(Object obj) { - return aliasMap.get(obj); + /** + * This method returns a mapped Object field by its reference. + * + * @param obj + * @return + */ + private SelectColumn getColumnByReference(Object obj) { + SelectColumn col = aliasMap.get(obj); + return col; + } + + /** + * This method returns the alias of a mapped primitive field by its value. + * + * @param obj + * @return + */ + @SuppressWarnings("unchecked") + A getPrimitiveAliasByValue(A obj) { + for (Object alias : aliasMap.keySet()) { + if (alias.equals(obj)) { + SelectColumn match = aliasMap.get(alias); + if (match.getFieldDefinition().isPrimitive) { + return (A) alias; + } + } + } + return null; } void addOrderBy(OrderExpression expr) { 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 { this.query = query; } + /** + * Specify an AND condition with a mapped primitive boolean. + * + * @param x + * the primitive boolean field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(boolean x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive byte. + * + * @param x + * the primitive byte field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(byte x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive short. + * + * @param x + * the primitive short field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(short x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive int. + * + * @param x + * the primitive int field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(int x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive long. + * + * @param x + * the primitive long field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(long x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive float. + * + * @param x + * the primitive float field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(float x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + /** + * Specify an AND condition with a mapped primitive double. + * + * @param x + * the primitive double field to query + * @return a query condition to continue building the condition + */ + public QueryCondition and(double x) { + return addPrimitive(ConditionAndOr.AND, x); + } + + private QueryCondition addPrimitive(ConditionAndOr condition, A x) { + query.addConditionToken(condition); + A alias = query.getPrimitiveAliasByValue(x); + if (alias == null) { + // this will result in an unmapped field exception + return new QueryCondition(query, x); + } + return new QueryCondition(query, alias); + } + + /** + * Specify an AND condition with a mapped Object field. + * + * @param x + * the Object field to query + * @return a query condition to continue building the condition + */ public QueryCondition and(A x) { query.addConditionToken(ConditionAndOr.AND); return new QueryCondition(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 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 or(byte x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped primitive short. + * + * @param x + * the primitive short field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(short x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped primitive int. + * + * @param x + * the primitive int field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(int x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped primitive long. + * + * @param x + * the primitive long field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(long x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped primitive float. + * + * @param x + * the primitive float field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(float x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped primitive double. + * + * @param x + * the primitive double field to query + * @return a query condition to continue building the condition + */ + public QueryCondition or(double x) { + return addPrimitive(ConditionAndOr.OR, x); + } + + /** + * Specify an OR condition with a mapped Object field. + * + * @param x + * the Object field to query + * @return a query condition to continue building the condition + */ public QueryCondition or(A x) { query.addConditionToken(ConditionAndOr.OR); return new QueryCondition(query, x); @@ -88,7 +266,86 @@ public class QueryWhere { } /** - * Order by a number of columns. + * Order by primitive boolean field + * + * @param field + * a primitive boolean field + * @return the query + */ + public QueryWhere orderBy(boolean field) { + return orderByPrimitive(field); + } + + /** + * Order by primitive byte field + * + * @param field + * a primitive byte field + * @return the query + */ + public QueryWhere orderBy(byte field) { + return orderByPrimitive(field); + } + + /** + * Order by primitive short field + * + * @param field + * a primitive short field + * @return the query + */ + public QueryWhere orderBy(short field) { + return orderByPrimitive(field); + } + + public QueryWhere orderBy(int field) { + return orderByPrimitive(field); + } + + /** + * Order by primitive long field + * + * @param field + * a primitive long field + * @return the query + */ + public QueryWhere orderBy(long field) { + return orderByPrimitive(field); + } + + /** + * Order by primitive float field + * + * @param field + * a primitive float field + * @return the query + */ + public QueryWhere orderBy(float field) { + return orderByPrimitive(field); + } + + /** + * Order by primitive double field + * + * @param field + * a primitive double field + * @return the query + */ + public QueryWhere orderBy(double field) { + return orderByPrimitive(field); + } + + private QueryWhere orderByPrimitive(Object field) { + query.orderByPrimitive(field); + return this; + } + + public QueryWhere orderBy(Object field) { + query.orderBy(field); + return this; + } + /** + * Order by a number of Object columns. * * @param expressions * the order by expressions @@ -96,10 +353,7 @@ public class QueryWhere { */ public QueryWhere orderBy(Object... expressions) { - for (Object expr : expressions) { - OrderExpression e = new OrderExpression(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 { boolean nullable; String defaultValue; EnumType enumType; + boolean isPrimitive; Object getValue(Object obj) { try { @@ -345,6 +346,7 @@ public class TableDefinition { 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 { query.appendSQL(stat, x, obj); } } - - void copyAttributeValues(Query query, X to, X map) { - for (FieldDefinition def : fields) { - Object obj = def.getValue(map); - SelectColumn 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 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 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 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 getList() { + List list = new ArrayList(); + for (int i = 1; i <= 10; i++) { + PrimitivesModel p = new PrimitivesModel(); + p.myLong = i; + list.add(p); + } + return list; + } + + @Override + public String toString() { + return String.valueOf(myLong); + } } -- 2.39.5