From b865898879f0d0eb1e752b41562b463b0c31c517 Mon Sep 17 00:00:00 2001 From: James Moger Date: Fri, 5 Aug 2011 11:04:29 -0400 Subject: [PATCH] Simplified annotations. Interchangeable int-boolean runtime mapping. --- docs/01_model_classes.mkd | 12 +- docs/05_releases.mkd | 6 +- src/com/iciql/Define.java | 2 +- src/com/iciql/Iciql.java | 31 +++-- src/com/iciql/Query.java | 1 + src/com/iciql/TableDefinition.java | 4 +- src/com/iciql/TableInspector.java | 8 +- src/com/iciql/util/Utils.java | 16 +++ tests/com/iciql/test/BooleanModelTest.java | 128 ++++++++++++++++++ tests/com/iciql/test/IciqlSuite.java | 4 +- tests/com/iciql/test/ModelsTest.java | 2 +- tests/com/iciql/test/models/BooleanModel.java | 73 ++++++++++ tests/com/iciql/test/models/Product.java | 4 +- .../test/models/ProductAnnotationOnly.java | 2 +- .../test/models/ProductMixedAnnotation.java | 2 +- .../com/iciql/test/models/SupportedTypes.java | 10 +- 16 files changed, 266 insertions(+), 39 deletions(-) create mode 100644 tests/com/iciql/test/BooleanModelTest.java create mode 100644 tests/com/iciql/test/models/BooleanModel.java diff --git a/docs/01_model_classes.mkd b/docs/01_model_classes.mkd index bf8d72c..fb81e0f 100644 --- a/docs/01_model_classes.mkd +++ b/docs/01_model_classes.mkd @@ -9,7 +9,7 @@ Alternatively, model classes can be automatically generated by iciql using the m - + @@ -51,7 +51,7 @@ Alternatively, model classes can be automatically generated by iciql using the m - + @@ -115,10 +115,10 @@ public class Product { @IQColumn(primaryKey = true) public Integer productId; - @IQColumn(maxLength = 200, trimString = true) + @IQColumn(length = 200, trim = true) public String productName; - @IQColumn(maxLength = 50, trimString = true) + @IQColumn(length = 50, trim = true) public String category; @IQColumn @@ -185,8 +185,8 @@ public class Product implements Iciql { public void defineIQ() { com.iciql.Define.primaryKey(productId); com.iciql.Define.columnName(unitsInStock, "units"); - com.iciql.Define.maxLength(productName, 200); - com.iciql.Define.maxLength(category, 50); + com.iciql.Define.length(productName, 200); + com.iciql.Define.length(category, 50); com.iciql.Define.index(productName, category); } } diff --git a/docs/05_releases.mkd b/docs/05_releases.mkd index 50fda43..d04fbea 100644 --- a/docs/05_releases.mkd +++ b/docs/05_releases.mkd @@ -3,9 +3,11 @@ ### Current Release **%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 v2) - added BLOB support (issue 1) - added java.lang.Enum support (issue 2) -- api change release (API v2) +- allow runtime flexible mapping of BOOL columns to Integer fields +- allow runtime flexible mapping of INT columns to Boolean fields - annotations overhaul to reduce verbosity - @IQSchema(name="public") -> @IQSchema("public") - @IQDatabase(version=2) -> @IQVersion(2) @@ -15,6 +17,8 @@ %BEGINCODE% @IQIndexes({ @IQIndex("name"), @IQIndex(name="myindexname" value={"name", "nickname"}) }) %ENDCODE% + - @IQColumn(maxLength=20) -> @IQColumn(length=20) + - @IQColumn(trimString=true) -> @IQColumn(trim=true) ### Older Releases diff --git a/src/com/iciql/Define.java b/src/com/iciql/Define.java index 54e435f..d7b42ba 100644 --- a/src/com/iciql/Define.java +++ b/src/com/iciql/Define.java @@ -59,7 +59,7 @@ public class Define { currentTableDefinition.setColumnName(column, columnName); } - public static void maxLength(Object column, int length) { + public static void length(Object column, int length) { checkInDefine(); currentTableDefinition.setMaxLength(column, length); } diff --git a/src/com/iciql/Iciql.java b/src/com/iciql/Iciql.java index ff2452e..6abe97d 100644 --- a/src/com/iciql/Iciql.java +++ b/src/com/iciql/Iciql.java @@ -37,7 +37,7 @@ import java.lang.annotation.Target; *
java.lang.StringVARCHAR *(maxLength > 0)* or TEXT *(maxLength == 0)*
VARCHAR *(length > 0)* or TEXT *(length == 0)*
java.lang.Boolean BIT
BLOB
java.lang.Enum.name()VARCHAR (maxLength > 0) or TEXT (maxLength == 0)
EnumType.STRING
VARCHAR (length > 0) or TEXT (length == 0)
EnumType.STRING
java.lang.Enum.ordinal() INT
EnumType.ORDINAL
* * - * + * * * * @@ -93,15 +93,19 @@ import java.lang.annotation.Target; * * * - * + * * * * - * + * * * - * - * + * + * * * *
java.lang.StringVARCHAR (maxLength > 0) or TEXT (maxLength == 0)VARCHAR (length > 0) or TEXT (length == 0)
java.lang.Boolean
java.lang.Enum.name()VARCHAR (maxLength > 0) or TEXT (maxLength == 0)
EnumType.STRING
VARCHAR (length > 0) or TEXT (length == 0)
+ * EnumType.STRING
java.lang.Enum.ordinal()INT
EnumType.ORDINAL
INT
+ * EnumType.ORDINAL
java.lang.Enum implements
com.iciql.Iciql.EnumID.enumId()
INT
EnumType.ENUMID
java.lang.Enum implements
+ * com.iciql.Iciql.EnumID.enumId()
INT
+ * EnumType.ENUMID
@@ -346,18 +350,18 @@ public interface Iciql { /** * If larger than zero, it is used during the CREATE TABLE phase. It may * also be used to prevent database exceptions on INSERT and UPDATE - * statements (see trimString). + * statements (see trim). *

- * Any maxLength set in define() may override this annotation setting if + * Any length set in define() may override this annotation setting if * the model class is not annotated with IQTable. Default: 0. */ - int maxLength() default 0; + int length() default 0; /** * If true, iciql will automatically trim the string if it exceeds - * maxLength (value.substring(0, maxLength)). Default: false. + * length (value.substring(0, length)). Default: false. */ - boolean trimString() default false; + boolean trim() default false; /** * If false, iciql will set the column NOT NULL during the CREATE TABLE @@ -403,7 +407,8 @@ public interface Iciql { *

  • ORDINAL - ordinal() : int *
  • ENUMID - enumId() : int * - * @see com.iciql.Iciql.EnumId interface + * + * @see com.iciql.Iciql.EnumId interface */ public enum EnumType { STRING, ORDINAL, ENUMID; @@ -428,8 +433,8 @@ public interface Iciql { * IQEnum(EnumType.STRING) * * - * A string mapping will generate either a VARCHAR, if IQColumn.maxLength > - * 0 or a TEXT column if IQColumn.maxLength == 0 + * A string mapping will generate either a VARCHAR, if IQColumn.length > + * 0 or a TEXT column if IQColumn.length == 0 * */ @Retention(RetentionPolicy.RUNTIME) diff --git a/src/com/iciql/Query.java b/src/com/iciql/Query.java index d9dc84f..0611ffa 100644 --- a/src/com/iciql/Query.java +++ b/src/com/iciql/Query.java @@ -222,6 +222,7 @@ public class Query { try { X value; Object o = rs.getObject(1); + // Convert CLOB and BLOB now because we close the resultset if (Clob.class.isAssignableFrom(o.getClass())) { value = (X) Utils.convert(o, String.class); } else if (Blob.class.isAssignableFrom(o.getClass())) { diff --git a/src/com/iciql/TableDefinition.java b/src/com/iciql/TableDefinition.java index 72b5348..5d9b9c4 100644 --- a/src/com/iciql/TableDefinition.java +++ b/src/com/iciql/TableDefinition.java @@ -285,8 +285,8 @@ class TableDefinition { } isAutoIncrement = col.autoIncrement(); isPrimaryKey = col.primaryKey(); - maxLength = col.maxLength(); - trimString = col.trimString(); + maxLength = col.length(); + trimString = col.trim(); allowNull = col.allowNull(); defaultValue = col.defaultValue(); } diff --git a/src/com/iciql/TableInspector.java b/src/com/iciql/TableInspector.java index 879e23a..82b732a 100644 --- a/src/com/iciql/TableInspector.java +++ b/src/com/iciql/TableInspector.java @@ -346,13 +346,13 @@ public class TableInspector { ap.addParameter("primaryKey=true"); } - // IQColumn.maxLength + // IQColumn.length if ((clazz == String.class) && (col.size > 0) && (col.size < Integer.MAX_VALUE)) { - ap.addParameter("maxLength", col.size); + ap.addParameter("length", col.size); - // IQColumn.trimStrings + // IQColumn.trim if (trimStrings) { - ap.addParameter("trimString=true"); + ap.addParameter("trim=true"); } } else { // IQColumn.AutoIncrement diff --git a/src/com/iciql/util/Utils.java b/src/com/iciql/util/Utils.java index 302dd4d..dac30fc 100644 --- a/src/com/iciql/util/Utils.java +++ b/src/com/iciql/util/Utils.java @@ -226,6 +226,22 @@ public class Utils { return o.toString(); } + // convert from number to boolean + if (Boolean.class.isAssignableFrom(targetType)) { + if (Number.class.isAssignableFrom(currentType)) { + Number n = (Number) o; + return n.intValue() > 0; + } + } + + // convert from boolean to number + if (Boolean.class.isAssignableFrom(currentType)) { + if (Number.class.isAssignableFrom(targetType)) { + Boolean b = (Boolean) o; + return b ? 1 : 0; + } + } + // convert from number to number if (Number.class.isAssignableFrom(currentType)) { Number n = (Number) o; diff --git a/tests/com/iciql/test/BooleanModelTest.java b/tests/com/iciql/test/BooleanModelTest.java new file mode 100644 index 0000000..0287e06 --- /dev/null +++ b/tests/com/iciql/test/BooleanModelTest.java @@ -0,0 +1,128 @@ +/* + * Copyright 2011 James Moger. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.iciql.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; + +import com.iciql.Db; +import com.iciql.test.models.BooleanModel; +import com.iciql.test.models.BooleanModel.BooleanAsIntModel; + +/** + * Tests interchangeable mapping of INT columns with Booleans and BOOL columns + * with Integers. + *
      + *
    • mapping a BIT/BOOLEAN column as an Integer + *
    • mapping a INT column as a Boolean. + *
    + */ +public class BooleanModelTest { + + @Test + public void testBooleanColumn() { + Db db = Db.open("jdbc:h2:mem:", "sa", "sa"); + db.insertAll(BooleanModel.getList()); + BooleanAsIntModel b = new BooleanAsIntModel(); + List models = db.from(b).select(); + int count = 0; + for (BooleanAsIntModel model : models) { + if ((model.id % 2) == 1) { + // assert that odd ids are true + assertTrue(model.mybool > 0); + } else { + // assert that even ids are false + assertTrue(model.mybool == 0); + } + + // count true values + if (model.mybool > 0) { + count++; + } + } + assertEquals(2, count); + + // invert boolean values and update + for (BooleanAsIntModel model : models) { + model.mybool = model.mybool > 0 ? 0 : 1; + } + db.updateAll(models); + + // check even ids are true + models = db.from(b).select(); + for (BooleanAsIntModel model : models) { + if ((model.id % 2) == 1) { + // assert that odd ids are false + assertTrue(model.mybool == 0); + } else { + // assert that even ids are true + assertTrue(model.mybool > 0); + } + } + db.close(); + } + + @Test + public void testIntColumn() { + Db db = Db.open("jdbc:h2:mem:", "sa", "sa"); + // insert INT column + db.insertAll(BooleanAsIntModel.getList()); + + // select all rows with INT column and map to Boolean + BooleanModel b = new BooleanModel(); + List models = db.from(b).select(); + int count = 0; + for (BooleanModel model : models) { + if ((model.id % 2) == 1) { + // assert that odd ids are true + assertTrue(model.mybool); + } else { + // assert that even ids are false + assertTrue(!model.mybool); + } + + // count true values + if (model.mybool) { + count++; + } + } + assertEquals(2, count); + + // invert boolean values and update + for (BooleanModel model : models) { + model.mybool = !model.mybool; + } + db.updateAll(models); + + // check even ids are true + models = db.from(b).select(); + for (BooleanModel model : models) { + if ((model.id % 2) == 1) { + // assert that odd ids are false + assertTrue(!model.mybool); + } else { + // assert that even ids are true + assertTrue(model.mybool); + } + } + db.close(); + } +} diff --git a/tests/com/iciql/test/IciqlSuite.java b/tests/com/iciql/test/IciqlSuite.java index b235f6d..20dbea2 100644 --- a/tests/com/iciql/test/IciqlSuite.java +++ b/tests/com/iciql/test/IciqlSuite.java @@ -25,8 +25,8 @@ import org.junit.runners.Suite.SuiteClasses; * */ @RunWith(Suite.class) -@SuiteClasses({ AliasMapTest.class, AnnotationsTest.class, ClobTest.class, ConcurrencyTest.class, - ModelsTest.class, SamplesTest.class, UpdateTest.class, RuntimeQueryTest.class, +@SuiteClasses({ AliasMapTest.class, AnnotationsTest.class, BooleanModelTest.class, ClobTest.class, + ConcurrencyTest.class, ModelsTest.class, SamplesTest.class, UpdateTest.class, RuntimeQueryTest.class, StatementLoggerTest.class }) public class IciqlSuite { diff --git a/tests/com/iciql/test/ModelsTest.java b/tests/com/iciql/test/ModelsTest.java index 716b1bc..9012296 100644 --- a/tests/com/iciql/test/ModelsTest.java +++ b/tests/com/iciql/test/ModelsTest.java @@ -115,7 +115,7 @@ public class ModelsTest { true); assertEquals(1, models.size()); // a poor test, but a start - assertEquals(1904, models.get(0).length()); + assertEquals(1895, models.get(0).length()); } @Test diff --git a/tests/com/iciql/test/models/BooleanModel.java b/tests/com/iciql/test/models/BooleanModel.java new file mode 100644 index 0000000..d22e3c1 --- /dev/null +++ b/tests/com/iciql/test/models/BooleanModel.java @@ -0,0 +1,73 @@ +/* + * Copyright 2011 James Moger. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.iciql.test.models; + +import java.util.Arrays; +import java.util.List; + +import com.iciql.Iciql.IQColumn; +import com.iciql.Iciql.IQTable; + +/** + * Boolean types model. + */ +@IQTable(name = "BooleanTest") +public class BooleanModel { + + @IQColumn(primaryKey = true) + public Integer id; + + @IQColumn + public Boolean mybool; + + public BooleanModel() { + } + + BooleanModel(int id, boolean val) { + this.id = id; + this.mybool = val; + } + + public static List getList() { + return Arrays.asList(new BooleanModel(1, true), new BooleanModel(2, false), + new BooleanModel(3, true), new BooleanModel(4, false)); + } + + /** + * Test boolean as int + */ + @IQTable(name = "BooleanTest") + public static class BooleanAsIntModel { + @IQColumn(primaryKey = true) + public Integer id; + + @IQColumn + public Integer mybool; + + public BooleanAsIntModel() { + } + + BooleanAsIntModel(int id, boolean val) { + this.id = id; + this.mybool = val ? 1 : 0; + } + + public static List getList() { + return Arrays.asList(new BooleanAsIntModel(1, true), new BooleanAsIntModel(2, false), + new BooleanAsIntModel(3, true), new BooleanAsIntModel(4, false)); + } + } +} diff --git a/tests/com/iciql/test/models/Product.java b/tests/com/iciql/test/models/Product.java index 735ebab..065e624 100644 --- a/tests/com/iciql/test/models/Product.java +++ b/tests/com/iciql/test/models/Product.java @@ -18,7 +18,7 @@ package com.iciql.test.models; import static com.iciql.Define.index; -import static com.iciql.Define.maxLength; +import static com.iciql.Define.length; import static com.iciql.Define.primaryKey; import static com.iciql.Define.tableName; @@ -54,7 +54,7 @@ public class Product implements Iciql { public void defineIQ() { tableName("Product"); primaryKey(productId); - maxLength(category, 255); + length(category, 255); index(productName, category); } diff --git a/tests/com/iciql/test/models/ProductAnnotationOnly.java b/tests/com/iciql/test/models/ProductAnnotationOnly.java index caf07c7..673ca14 100644 --- a/tests/com/iciql/test/models/ProductAnnotationOnly.java +++ b/tests/com/iciql/test/models/ProductAnnotationOnly.java @@ -42,7 +42,7 @@ public class ProductAnnotationOnly { @IQColumn(name = "id") public Integer productId; - @IQColumn(name = "cat", maxLength = 15, trimString = true) + @IQColumn(name = "cat", length = 15, trim = true) public String category; @IQColumn(name = "name") diff --git a/tests/com/iciql/test/models/ProductMixedAnnotation.java b/tests/com/iciql/test/models/ProductMixedAnnotation.java index 1d3cb8f..ff7d46f 100644 --- a/tests/com/iciql/test/models/ProductMixedAnnotation.java +++ b/tests/com/iciql/test/models/ProductMixedAnnotation.java @@ -36,7 +36,7 @@ public class ProductMixedAnnotation { public Integer unitsInStock; public String mappedField; - @IQColumn(name = "cat", maxLength = 255) + @IQColumn(name = "cat", length = 255) public String category; @IQColumn(name = "id", primaryKey = true) diff --git a/tests/com/iciql/test/models/SupportedTypes.java b/tests/com/iciql/test/models/SupportedTypes.java index 97a8675..c8aebcb 100644 --- a/tests/com/iciql/test/models/SupportedTypes.java +++ b/tests/com/iciql/test/models/SupportedTypes.java @@ -80,10 +80,10 @@ public class SupportedTypes { public Integer id; @IQColumn - private Boolean myBool = false; + private Boolean myBool; @IQColumn - private Byte myByte = 2; + private Byte myByte; @IQColumn private Short myShort; @@ -95,7 +95,7 @@ public class SupportedTypes { private Long myLong; @IQColumn - private Float myFloat = 1.0f; + private Float myFloat; @IQColumn private Double myDouble; @@ -122,14 +122,14 @@ public class SupportedTypes { private byte[] myBlob; @IQEnum(EnumType.STRING) - @IQColumn(trimString = true, maxLength = 25) + @IQColumn(trim = true, length = 25) private Flower myFavoriteFlower; @IQEnum(EnumType.ORDINAL) @IQColumn private Flower myOtherFavoriteFlower; - @IQColumn(maxLength = 25) + @IQColumn(length = 25) // @IQEnum is set on the enumeration definition and is shared // by all uses of Tree as an @IQColumn private Tree myFavoriteTree; -- 2.39.5