]> source.dussan.org Git - iciql.git/commitdiff
Columns mapped by name in result set instead of index. Disallow multiple
authorJames Moger <james.moger@gmail.com>
Fri, 9 Dec 2011 21:42:59 +0000 (16:42 -0500)
committerJames Moger <james.moger@gmail.com>
Fri, 9 Dec 2011 21:42:59 +0000 (16:42 -0500)
primitive bools in a model WITH explicit referencing.

14 files changed:
api/v8.xml
docs/01_model_classes.mkd
docs/04_examples.mkd
docs/05_releases.mkd
docs/06_jaqu_comparison.mkd
src/com/iciql/Db.java
src/com/iciql/Iciql.java
src/com/iciql/Query.java
src/com/iciql/QueryJoin.java
src/com/iciql/QueryWhere.java
src/com/iciql/TableDefinition.java
tests/com/iciql/test/PrimitivesTest.java
tests/com/iciql/test/SamplesTest.java
tests/com/iciql/test/models/MultipleBoolsModel.java [new file with mode: 0644]

index d738d6a06b22e59f23746b0de7ebb5118a620cb0..8a2014d6efc8b7fe19e741429626fa0b4e0a1dc8 100644 (file)
@@ -43,7 +43,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value="&quot;0.7.3&quot;"
+ value="&quot;0.7.4-SNAPSHOT&quot;"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -54,7 +54,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value="&quot;2011-12-06&quot;"
+ value="&quot;PENDING&quot;"
  static="true"
  final="true"
  deprecated="not deprecated"
  deprecated="not deprecated"
  visibility="public"
 >\r
+<method name="activateConsoleLogger"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>\r
+</method>\r
 <method name="buildObjects"
  return="java.util.List&lt;T&gt;"
  abstract="false"
  visibility="public"
 >\r
 </method>\r
+<method name="deactivateConsoleLogger"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>\r
+</method>\r
 <method name="delete"
  return="int"
  abstract="false"
 <parameter name="args" type="java.lang.Object...">\r
 </parameter>\r
 </method>\r
+<method name="executeQuery"
+ return="java.util.List&lt;T&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>\r
+<parameter name="modelClass" type="java.lang.Class&lt;? extends T&gt;">\r
+</parameter>\r
+<parameter name="sql" type="java.lang.String">\r
+</parameter>\r
+<parameter name="args" type="java.util.List&lt;?&gt;">\r
+</parameter>\r
+</method>\r
 <method name="executeQuery"
  return="java.sql.ResultSet"
  abstract="false"
 <parameter name="args" type="java.lang.Object...">\r
 </parameter>\r
 </method>\r
+<method name="executeQuery"
+ return="java.sql.ResultSet"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>\r
+<parameter name="sql" type="java.lang.String">\r
+</parameter>\r
+<parameter name="args" type="java.util.List&lt;?&gt;">\r
+</parameter>\r
+</method>\r
 <method name="executeUpdate"
  return="int"
  abstract="false"
 <implements name="java.lang.annotation.Annotation">\r
 </implements>\r
 </class>\r
+<class name="Iciql.IQFunction"\r
+ extends="java.lang.Object"\r
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>\r
+<implements name="java.lang.annotation.Annotation">\r
+</implements>\r
+</class>\r
 <class name="Iciql.IQIgnore"\r
  extends="java.lang.Object"\r
  abstract="true"
 <parameter name="args" type="java.lang.Object...">\r
 </parameter>\r
 </method>\r
+<method name="where"
+ return="com.iciql.QueryWhere&lt;T&gt;"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>\r
+<parameter name="fragment" type="java.lang.String">\r
+</parameter>\r
+<parameter name="args" type="java.util.List&lt;?&gt;">\r
+</parameter>\r
+</method>\r
 <method name="where"
  return="com.iciql.QueryCondition&lt;T, java.lang.Long&gt;"
  abstract="false"
index 5babdccaafde3e142e45382365b5bbebdc4eb676..f5346600eddd5babb0053f794f3aeca56693d9e3 100644 (file)
@@ -23,7 +23,7 @@ can be used for all iciql expressions
 <td>VARCHAR *(length > 0)* or CLOB *(length == 0)*</td></tr>\r
        \r
 <tr><td>java.lang.Boolean</td><td>boolean</td>\r
-<td>BOOLEAN</td></tr>\r
+<td>BOOLEAN<br/><i>can only **declare and explicitly reference** one <u>primitive boolean</u> per model<br/>multiple primitives are allowed if not using where/set/on/and/or/groupBy/orderBy(boolean)</i></td></tr>\r
        \r
 <tr><td>java.lang.Byte</td><td>byte</td>\r
 <td>TINYINT</td></tr>\r
index 480e29dca91d96aef6d550e03d6f63a2f5b54aa3..a1273be9ef55e04948085b7a67358c7f79395267 100644 (file)
@@ -8,6 +8,13 @@ List&lt;Product&gt; allProducts = db.from(p).select();
 Customer c = new Customer();\r
 List&lt;Customer&gt; waCustomers = db.from(c). where(c.region).is("WA").select();\r
 \r
+public static class ProductPrice {\r
+       public String productName;\r
+       public String category;\r
+       @IQColumn(name = "unitPrice")\r
+       public Double price;\r
+}\r
+\r
 // select with generation of new anonymous inner class\r
 List&lt;ProductPrice&gt; productPrices =\r
     db.from(p).\r
index 9a7d13e98248ccd1f7c87e0e814509de920510f8..d4aa8f2c1d77b84cd5cc05c1f44dcf3310352474 100644 (file)
@@ -6,9 +6,29 @@
 \r
 **%VERSION%** ([zip](http://code.google.com/p/iciql/downloads/detail?name=%ZIP%)|[jar](http://code.google.com/p/iciql/downloads/detail?name=%JAR%)) &nbsp; *released %BUILDDATE%*\r
 \r
-- Added list alternatives to the varargs methods because it was too easy to forget list.toArray() for the varargs methods  \r
-List<T> Db.executeQuery(Class<? extends T> modelClass, String sql, List<?> args)\r
-ResultSet executeQuery(String sql, List<?> args)\r
+- Disallow **declaring and explicitly referencing** multiple primitive booleans in a single model.<br/>A runtime exception will be thrown if an attempt to use where/set/on/and/or/groupBy/orderBy(boolean) and your model has multiple mapped primitive boolean fields.\r
+- Added list alternatives to the varargs methods because it was too easy to forget list.toArray()<br/>\r
+*Db.executeQuery(Class&lt;? extends T&gt; modelClass, String sql, List&lt;?&gt; args)*<br/>\r
+*Db.executeQuery(String sql, List&lt;?&gt; args)*<br/>\r
+*Query.where(String fragment, List&lt;?&gt; args)*<br/>\r
+- Fixed inherited JaQu bug related to model classes and wildcard queries (select *).<p/>\r
+Iciql maps resultset columns by the index of the model class field from a list.  This assumes that *all* columns in the resultset have a corresponding model field definition.  This works fine for most queries because iciql explicitly selects columns from the table (*select alpha, beta...*) when you execute *select()*.  The problem is when iciql issues a join or a custom wildcard query and your model does not represent all columns in the resultset: columns and fields fail to correctly line-up.<p/>  \r
+The fix for this (building a column index from the resultset by column name lookup) breaks selecting into *some* anonymous inner classes.  At issue is that the inner class field names must now match the column names or the fields must be explicitly annotated with the column names.<p/>**Example** (notice *IQColumn* annotation)<br/>\r
+%BEGINCODE%\r
+public static class ProductPrice {\r
+       public String productName;\r
+       public String category;\r
+       @IQColumn(name = "unitPrice")\r
+       public Double price;\r
+}\r
+\r
+db....select(new ProductPrice() {{\r
+    productName = p.productName;\r
+       category = p.category;\r
+       // or unitPrice = p.unitPrice;\r
+       price = p.unitPrice;\r
+}}\r
+%ENDCODE%\r
 \r
 ### Older Releases\r
 \r
index 33ca975f00f06772e09daa1575596846199f98c6..9c0f56cc0dd13aca12b369c3ff8f20f52338981d 100644 (file)
@@ -10,6 +10,7 @@ This is an overview of the fundamental differences between the original JaQu pro
 <tr><td>databases</td><td>H2, HSQL, Derby, MySQL, and PostreSQL</td><td>H2 only</td></tr>\r
 <tr><td>logging</td><td>console, SLF4J, or custom logging</td><td>console logging</td></tr>\r
 <tr><td>exceptions</td><td>always includes generated statement in exception, when available</td><td>--</td></tr>\r
+<tr><td>column mappings</td><td>result sets built by column name</td><td>result sets built by field index<br/>this can fail for dynamic queries or joins</td></tr>\r
 <tr><th colspan="3">syntax and api</th></tr>\r
 <tr><td>dynamic queries</td><td>methods and where clauses for dynamic queries that build iciql objects</td><td>--</td></tr>\r
 <tr><td>DROP</td><td>syntax to drop a table</td><td></td></tr>\r
index e05ec564fcd0c9f806ebacf2a4feadd0479f1482..4dc0b5e4ffee68f0cfd2ed3503033ca43ac5f608 100644 (file)
@@ -138,6 +138,20 @@ public class Db {
                        throw new IciqlException(e);\r
                }\r
        }\r
+       \r
+       /**\r
+        * Convenience function to avoid import statements in application code.\r
+        */\r
+       public static void activateConsoleLogger() {\r
+               IciqlLogger.activateConsoleLogger();\r
+       }\r
+\r
+       /**\r
+        * Convenience function to avoid import statements in application code.\r
+        */\r
+       public static void deactivateConsoleLogger() {\r
+               IciqlLogger.deactivateConsoleLogger();\r
+       }\r
 \r
        public static Db open(String url) {\r
                try {\r
@@ -273,9 +287,10 @@ public class Db {
                List<T> result = new ArrayList<T>();\r
                TableDefinition<T> def = (TableDefinition<T>) define(modelClass);\r
                try {\r
+                       int [] columns = def.mapColumns(rs);\r
                        while (rs.next()) {\r
                                T item = Utils.newObject(modelClass);\r
-                               def.readRow(item, rs);\r
+                               def.readRow(item, rs, columns);\r
                                result.add(item);\r
                        }\r
                } catch (SQLException e) {\r
index eaa256a2eaeece168e17000012810c57fbee8af6..b13baf470eea53aa512196aabd3031f005b1e835 100644 (file)
@@ -499,6 +499,17 @@ public interface Iciql {
                EnumType value() default EnumType.NAME;\r
        }\r
 \r
+       /**\r
+        * Annotation to define a field that should contain the result a function.\r
+        * This annotation ensures that functions mapped in anonymous inner classes\r
+        * can still be referenced in the ResultSet after the switch to dynamic\r
+        * column-name mapping from fixed position column mapping.\r
+        */\r
+       @Retention(RetentionPolicy.RUNTIME)\r
+       @Target(ElementType.FIELD)\r
+       public @interface IQFunction{   \r
+       }\r
+       \r
        /**\r
         * Annotation to define an ignored field.\r
         */\r
index b43f774c567c9cdecb7221522f7808e22abf88b5..33b6dfa64f74d0779909bcc1d110c1338f2b316a 100644 (file)
@@ -123,9 +123,10 @@ public class Query<T> {
                appendFromWhere(stat);\r
                ResultSet rs = stat.executeQuery();\r
                try {\r
+                       int[] columns = def.mapColumns(rs);\r
                        while (rs.next()) {\r
                                T item = from.newObject();\r
-                               from.getAliasDefinition().readRow(item, rs);\r
+                               def.readRow(item, rs, columns);\r
                                result.add(item);\r
                        }\r
                } catch (SQLException e) {\r
@@ -150,6 +151,7 @@ public class Query<T> {
        }\r
 \r
        public UpdateColumnSet<T, Boolean> set(boolean field) {\r
+               from.getAliasDefinition().checkMultipleBooleans();\r
                return setPrimitive(field);\r
        }\r
 \r
@@ -269,9 +271,10 @@ public class Query<T> {
                appendFromWhere(stat);\r
                ResultSet rs = stat.executeQuery();\r
                try {\r
+                       int[] columns = def.mapColumns(rs);\r
                        while (rs.next()) {\r
                                X row = Utils.newObject(clazz);\r
-                               def.readRow(row, rs);\r
+                               def.readRow(row, rs, columns);\r
                                result.add(row);\r
                        }\r
                } catch (SQLException e) {\r
@@ -328,6 +331,7 @@ public class Query<T> {
         * @return a query condition to continue building the condition\r
         */\r
        public QueryCondition<T, Boolean> where(boolean x) {\r
+               from.getAliasDefinition().checkMultipleBooleans();\r
                return wherePrimitive(x);\r
        }\r
 \r
@@ -449,6 +453,10 @@ public class Query<T> {
                return new QueryWhere<T>(this);\r
        }\r
 \r
+       public QueryWhere<T> where(String fragment, List<?> args) {\r
+               return this.where(fragment, args.toArray());\r
+       }\r
+\r
        public QueryWhere<T> where(String fragment, Object... args) {\r
                conditions.add(new RuntimeToken(fragment, args));\r
                return new QueryWhere<T>(this);\r
@@ -477,6 +485,7 @@ public class Query<T> {
        }\r
 \r
        public Query<T> orderBy(boolean field) {\r
+               from.getAliasDefinition().checkMultipleBooleans();\r
                return orderByPrimitive(field);\r
        }\r
 \r
@@ -541,6 +550,7 @@ public class Query<T> {
        }\r
 \r
        public Query<T> groupBy(boolean field) {\r
+               from.getAliasDefinition().checkMultipleBooleans();\r
                return groupByPrimitive(field);\r
        }\r
 \r
@@ -737,6 +747,10 @@ public class Query<T> {
                return db;\r
        }\r
 \r
+       SelectTable<T> getFrom() {\r
+               return from;\r
+       }\r
+\r
        boolean isJoin() {\r
                return !joins.isEmpty();\r
        }\r
index 652d937c49c11f59314fb2c0f6d8cfc32e296985..6d0484edddc70c1239beb903ad636f26dd1a8e29 100644 (file)
@@ -32,6 +32,7 @@ public class QueryJoin<T> {
        }\r
 \r
        public QueryJoinCondition<T, Boolean> on(boolean x) {\r
+               query.getFrom().getAliasDefinition().checkMultipleBooleans();\r
                return addPrimitive(x);\r
        }\r
 \r
@@ -59,7 +60,7 @@ public class QueryJoin<T> {
                return addPrimitive(x);\r
        }\r
 \r
-       private <A> QueryJoinCondition<T, A> addPrimitive(A x) {                \r
+       private <A> QueryJoinCondition<T, A> addPrimitive(A x) {\r
                A alias = query.getPrimitiveAliasByValue(x);\r
                if (alias == null) {\r
                        // this will result in an unmapped field exception\r
index c1e3b0363ae42e1e7bdc42ddbd99e75823ce434a..df93439ad3552f12d0cff4f599d3464151fb75b0 100644 (file)
@@ -42,6 +42,7 @@ public class QueryWhere<T> {
         * @return a query condition to continue building the condition\r
         */\r
        public QueryCondition<T, Boolean> and(boolean x) {\r
+               query.getFrom().getAliasDefinition().checkMultipleBooleans();\r
                return addPrimitive(ConditionAndOr.AND, x);\r
        }\r
 \r
@@ -111,7 +112,7 @@ public class QueryWhere<T> {
                return addPrimitive(ConditionAndOr.AND, x);\r
        }\r
 \r
-       private <A> QueryCondition<T, A> addPrimitive(ConditionAndOr condition, A x) {\r
+       private <A> QueryCondition<T, A> addPrimitive(ConditionAndOr condition, A x) {          \r
                query.addConditionToken(condition);\r
                A alias = query.getPrimitiveAliasByValue(x);\r
                if (alias == null) {\r
@@ -141,6 +142,7 @@ public class QueryWhere<T> {
         * @return a query condition to continue building the condition\r
         */\r
        public QueryCondition<T, Boolean> or(boolean x) {\r
+               query.getFrom().getAliasDefinition().checkMultipleBooleans();\r
                return addPrimitive(ConditionAndOr.OR, x);\r
        }\r
 \r
@@ -273,6 +275,7 @@ public class QueryWhere<T> {
         * @return the query\r
         */\r
        public QueryWhere<T> orderBy(boolean field) {\r
+               query.getFrom().getAliasDefinition().checkMultipleBooleans();\r
                return orderByPrimitive(field);\r
        }\r
 \r
index 97060f9ff56c9e32073abb302e61a26e32a7960c..11472386f794808d8c1d1d2e34516aa7858cf60c 100644 (file)
@@ -30,6 +30,7 @@ import com.iciql.Iciql.EnumId;
 import com.iciql.Iciql.EnumType;\r
 import com.iciql.Iciql.IQColumn;\r
 import com.iciql.Iciql.IQEnum;\r
+import com.iciql.Iciql.IQFunction;\r
 import com.iciql.Iciql.IQIgnore;\r
 import com.iciql.Iciql.IQIndex;\r
 import com.iciql.Iciql.IQIndexes;\r
@@ -73,6 +74,7 @@ public class TableDefinition<T> {
                String dataType;\r
                int length;\r
                int scale;\r
+               boolean isFunction;\r
                boolean isPrimaryKey;\r
                boolean isAutoIncrement;\r
                boolean trim;\r
@@ -115,6 +117,10 @@ public class TableDefinition<T> {
                }\r
 \r
                private Object read(ResultSet rs, int columnIndex) {\r
+                       if (columnIndex == 0) {\r
+                               // unmapped column or function field\r
+                               return null;\r
+                       }\r
                        try {\r
                                return rs.getObject(columnIndex);\r
                        } catch (SQLException e) {\r
@@ -129,6 +135,7 @@ public class TableDefinition<T> {
        int tableVersion;\r
        List<String> primaryKeyColumnNames;\r
        boolean memoryTable;\r
+       boolean multiplePrimitiveBools;\r
 \r
        private boolean createIfRequired = true;\r
        private Class<T> clazz;\r
@@ -354,6 +361,7 @@ public class TableDefinition<T> {
                                throw new IciqlException(e, "failed to get default object for {0}", columnName);\r
                        }\r
 \r
+                       boolean isFunction = f.isAnnotationPresent(IQFunction.class);\r
                        boolean hasAnnotation = f.isAnnotationPresent(IQColumn.class);\r
                        if (hasAnnotation) {\r
                                IQColumn col = f.getAnnotation(IQColumn.class);\r
@@ -385,6 +393,7 @@ public class TableDefinition<T> {
                                fieldDef.scale = scale;\r
                                fieldDef.trim = trim;\r
                                fieldDef.nullable = nullable;\r
+                               fieldDef.isFunction = isFunction;\r
                                fieldDef.defaultValue = defaultValue;\r
                                fieldDef.enumType = enumType;\r
                                fieldDef.dataType = ModelUtils.getDataType(fieldDef);\r
@@ -392,16 +401,32 @@ public class TableDefinition<T> {
                        }\r
                }\r
                List<String> primaryKey = Utils.newArrayList();\r
+               int primitiveBoolean = 0;\r
                for (FieldDefinition fieldDef : fields) {\r
                        if (fieldDef.isPrimaryKey) {\r
                                primaryKey.add(fieldDef.columnName);\r
                        }\r
+                       if (fieldDef.isPrimitive && fieldDef.field.getType().equals(boolean.class)) {\r
+                               primitiveBoolean++;\r
+                       }\r
+               }\r
+               if (primitiveBoolean > 1) {\r
+                       multiplePrimitiveBools = true;\r
+                       IciqlLogger\r
+                                       .warn("Model {0} has multiple primitive booleans! Possible where,set,join clause problem!");\r
                }\r
                if (primaryKey.size() > 0) {\r
                        setPrimaryKey(primaryKey);\r
                }\r
        }\r
 \r
+       void checkMultipleBooleans() {\r
+               if (multiplePrimitiveBools) {\r
+                       throw new IciqlException(\r
+                                       "Can not explicitly reference multiple primitive booleans in a model class!");\r
+               }\r
+       }\r
+\r
        /**\r
         * Optionally truncates strings to the maximum length and converts\r
         * java.lang.Enum types to Strings or Integers.\r
@@ -698,10 +723,47 @@ public class TableDefinition<T> {
                }\r
        }\r
 \r
-       void readRow(Object item, ResultSet rs) {\r
+       /**\r
+        * Most queries executed by iciql have named select lists (select alpha,\r
+        * beta where...) but sometimes a wildcard select is executed (select *).\r
+        * When a wildcard query is executed on a table that has more columns than\r
+        * are mapped in your model object this creates a column mapping issue. JaQu\r
+        * assumed that you can always use the integer index of the reflectively\r
+        * mapped field definition to determine position in the result set.\r
+        * \r
+        * This is not always true.\r
+        * \r
+        * So iciql maps column names to column index in the result set to properly\r
+        * map the results of wildcard queries.\r
+        * \r
+        * @param rs\r
+        * @return\r
+        */\r
+       int[] mapColumns(ResultSet rs) {\r
+               int[] columns = new int[fields.size()];\r
                for (int i = 0; i < fields.size(); i++) {\r
-                       FieldDefinition def = fields.get(i);\r
-                       Object o = def.read(rs, i + 1);\r
+                       try {\r
+                               FieldDefinition def = fields.get(i);\r
+                               int columnIndex;\r
+                               if (def.isFunction) {\r
+                                       // XXX review functions _always_ map after fields?\r
+                                       columnIndex = i + 1;\r
+                               } else {\r
+                                       columnIndex = rs.findColumn(def.columnName);    \r
+                               }                               \r
+                               columns[i] = columnIndex;\r
+                       } catch (SQLException s) {\r
+                               throw new IciqlException(s);\r
+                       }\r
+               }\r
+               return columns;\r
+       }\r
+\r
+       void readRow(Object item, ResultSet rs, int[] columns) {\r
+               for (int i = 0; i < fields.size(); i++) {\r
+                       FieldDefinition def = fields.get(i);                    \r
+                       int index = columns[i];\r
+                       Object o = def.read(rs, index);\r
                        def.setValue(item, o);\r
                }\r
        }\r
index be2b7260520e32b027e2aec4598247f3444149f9..3d3811ee0007d1d60943d5815c420bb6914d5ba3 100644 (file)
@@ -25,6 +25,8 @@ import java.util.List;
 import org.junit.Test;\r
 \r
 import com.iciql.Db;\r
+import com.iciql.IciqlException;\r
+import com.iciql.test.models.MultipleBoolsModel;\r
 import com.iciql.test.models.PrimitivesModel;\r
 \r
 /**\r
@@ -82,4 +84,19 @@ public class PrimitivesTest {
 \r
                db.close();\r
        }\r
+       \r
+       @Test\r
+       public void testMultipleBooleans() {\r
+               Db db = IciqlSuite.openNewDb();\r
+               db.insertAll(MultipleBoolsModel.getList());\r
+               \r
+               MultipleBoolsModel m = new MultipleBoolsModel();\r
+               try {\r
+                       db.from(m).where(m.a).is(true).select();\r
+                       assertTrue(false);\r
+               } catch (IciqlException e) {\r
+                       assertTrue(true);\r
+               }\r
+               db.close();\r
+       }\r
 }\r
index 17c2151b2feaf35c7b8d9aa497a8e27311c914ac..5c7ffc8b362a19343d350702445e77f74cceb75b 100644 (file)
@@ -39,6 +39,8 @@ import org.junit.Test;
 \r
 import com.iciql.Db;\r
 import com.iciql.Filter;\r
+import com.iciql.Iciql.IQColumn;\r
+import com.iciql.Iciql.IQFunction;\r
 import com.iciql.test.models.ComplexObject;\r
 import com.iciql.test.models.Customer;\r
 import com.iciql.test.models.Order;\r
@@ -161,6 +163,7 @@ public class SamplesTest {
        public static class ProductPrice {\r
                public String productName;\r
                public String category;\r
+               @IQColumn(name = "unitPrice")\r
                public Double price;\r
        }\r
 \r
@@ -406,6 +409,7 @@ public class SamplesTest {
         */\r
        public static class ProductGroup {\r
                public String category;\r
+               @IQFunction\r
                public Long productCount;\r
 \r
                public String toString() {\r
@@ -432,7 +436,6 @@ public class SamplesTest {
                                                productCount = count();\r
                                        }\r
                                });\r
-\r
                assertEquals("[Beverages:2, Condiments:5, Meat/Poultry:1, Produce:1, Seafood:1]", list.toString());\r
        }\r
 \r
diff --git a/tests/com/iciql/test/models/MultipleBoolsModel.java b/tests/com/iciql/test/models/MultipleBoolsModel.java
new file mode 100644 (file)
index 0000000..7bc429c
--- /dev/null
@@ -0,0 +1,40 @@
+package com.iciql.test.models;\r
+\r
+import java.util.Arrays;\r
+import java.util.List;\r
+\r
+import com.iciql.Iciql.IQColumn;\r
+import com.iciql.Iciql.IQTable;\r
+\r
+/**\r
+ * Model class to test the runtime exception of too many primitive boolean\r
+ * fields in the model.\r
+ * \r
+ * @author James Moger\r
+ * \r
+ */\r
+@IQTable\r
+public class MultipleBoolsModel {\r
+\r
+       @IQColumn(autoIncrement = true, primaryKey = true)\r
+       public int id;\r
+\r
+       @IQColumn\r
+       public boolean a;\r
+\r
+       @IQColumn\r
+       public boolean b;\r
+\r
+       public MultipleBoolsModel() {\r
+       }\r
+\r
+       public MultipleBoolsModel(boolean a, boolean b) {\r
+               this.a = a;\r
+               this.b = b;\r
+       }\r
+\r
+       public static List<MultipleBoolsModel> getList() {\r
+               return Arrays.asList(new MultipleBoolsModel(true, true), new MultipleBoolsModel(true, false),\r
+                               new MultipleBoolsModel(true, false), new MultipleBoolsModel(false, false));\r
+       }\r
+}
\ No newline at end of file