From 684838def9e1646c266ffb46bbc65b5dfdc8f14d Mon Sep 17 00:00:00 2001 From: James Moger Date: Sun, 7 Aug 2011 15:53:18 -0400 Subject: [PATCH] Finished enum query support (issue 4) --- src/com/iciql/Condition.java | 4 +- src/com/iciql/Function.java | 16 +- src/com/iciql/OrderExpression.java | 2 +- src/com/iciql/Query.java | 29 ++-- src/com/iciql/QueryCondition.java | 7 +- src/com/iciql/SQLStatement.java | 2 +- src/com/iciql/TableDefinition.java | 2 +- src/com/iciql/TestCondition.java | 32 ++-- src/com/iciql/UpdateColumnIncrement.java | 6 +- src/com/iciql/UpdateColumnSet.java | 7 +- src/com/iciql/bytecode/Variable.java | 2 +- src/com/iciql/util/Utils.java | 19 +++ tests/com/iciql/test/ModelsTest.java | 41 ++++- tests/com/iciql/test/models/EnumModels.java | 153 ++++++++++++++++++ .../com/iciql/test/models/SupportedTypes.java | 37 +---- 15 files changed, 281 insertions(+), 78 deletions(-) create mode 100644 tests/com/iciql/test/models/EnumModels.java diff --git a/src/com/iciql/Condition.java b/src/com/iciql/Condition.java index df6b033..df9762a 100644 --- a/src/com/iciql/Condition.java +++ b/src/com/iciql/Condition.java @@ -35,12 +35,12 @@ class Condition implements Token { } public void appendSQL(SQLStatement stat, Query query) { - query.appendSQL(stat, x); + query.appendSQL(stat, null, x); stat.appendSQL(" "); stat.appendSQL(compareType.getString()); if (compareType.hasRightExpression()) { stat.appendSQL(" "); - query.appendSQL(stat, y); + query.appendSQL(stat, x, y); } } } diff --git a/src/com/iciql/Function.java b/src/com/iciql/Function.java index f2c9e30..f07a5ea 100644 --- a/src/com/iciql/Function.java +++ b/src/com/iciql/Function.java @@ -42,7 +42,7 @@ public class Function implements Token { if (i++ > 0) { stat.appendSQL(","); } - query.appendSQL(stat, o); + query.appendSQL(stat, null, o); } stat.appendSQL(")"); } @@ -67,7 +67,7 @@ public class Function implements Token { public static Boolean isNull(Object x) { return Db.registerToken(Utils.newObject(Boolean.class), new Function("", x) { public void appendSQL(SQLStatement stat, Query query) { - query.appendSQL(stat, x[0]); + query.appendSQL(stat, null, x[0]); stat.appendSQL(" IS NULL"); } }); @@ -76,7 +76,7 @@ public class Function implements Token { public static Boolean isNotNull(Object x) { return Db.registerToken(Utils.newObject(Boolean.class), new Function("", x) { public void appendSQL(SQLStatement stat, Query query) { - query.appendSQL(stat, x[0]); + query.appendSQL(stat, null, x[0]); stat.appendSQL(" IS NOT NULL"); } }); @@ -86,7 +86,7 @@ public class Function implements Token { return Db.registerToken(Utils.newObject(Boolean.class), new Function("", x) { public void appendSQL(SQLStatement stat, Query query) { stat.appendSQL("NOT "); - query.appendSQL(stat, x[0]); + query.appendSQL(stat, null, x[0]); } }); } @@ -99,7 +99,7 @@ public class Function implements Token { if (i++ > 0) { stat.appendSQL(" OR "); } - query.appendSQL(stat, o); + query.appendSQL(stat, null, o); } } }); @@ -113,7 +113,7 @@ public class Function implements Token { if (i++ > 0) { stat.appendSQL(" AND "); } - query.appendSQL(stat, o); + query.appendSQL(stat, null, o); } } }); @@ -138,9 +138,9 @@ public class Function implements Token { return Db.registerToken(o, new Function("LIKE", x, pattern) { public void appendSQL(SQLStatement stat, Query query) { stat.appendSQL("("); - query.appendSQL(stat, x[0]); + query.appendSQL(stat, null, x[0]); stat.appendSQL(" LIKE "); - query.appendSQL(stat, x[1]); + query.appendSQL(stat, x[0], x[1]); stat.appendSQL(")"); } }); diff --git a/src/com/iciql/OrderExpression.java b/src/com/iciql/OrderExpression.java index 36acf16..f450bfb 100644 --- a/src/com/iciql/OrderExpression.java +++ b/src/com/iciql/OrderExpression.java @@ -40,7 +40,7 @@ class OrderExpression { } void appendSQL(SQLStatement stat) { - query.appendSQL(stat, expression); + query.appendSQL(stat, null, expression); if (desc) { stat.appendSQL(" DESC"); } diff --git a/src/com/iciql/Query.java b/src/com/iciql/Query.java index b6018ea..c44cac7 100644 --- a/src/com/iciql/Query.java +++ b/src/com/iciql/Query.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.IdentityHashMap; import java.util.List; +import com.iciql.Iciql.EnumType; import com.iciql.bytecode.ClassReader; import com.iciql.util.JdbcUtils; import com.iciql.util.StatementLogger; @@ -213,7 +214,7 @@ public class Query { @SuppressWarnings("unchecked") private List selectSimple(X x, boolean distinct) { SQLStatement stat = getSelectStatement(distinct); - appendSQL(stat, x); + appendSQL(stat, null, x); appendFromWhere(stat); ResultSet rs = stat.executeQuery(); List result = Utils.newArrayList(); @@ -343,26 +344,36 @@ public class Query { * * @param stat * the statement - * @param x - * the alias object + * @param alias + * the alias object (can be null) + * @param value + * the value */ - public void appendSQL(SQLStatement stat, Object x) { - if (x == Function.count()) { + public void appendSQL(SQLStatement stat, Object alias, Object value) { + if (value == Function.count()) { stat.appendSQL("COUNT(*)"); return; } - Token token = Db.getToken(x); + Token token = Db.getToken(value); if (token != null) { token.appendSQL(stat, this); return; } - SelectColumn col = aliasMap.get(x); + SelectColumn col = aliasMap.get(value); if (col != null) { col.appendSQL(stat); return; } stat.appendSQL("?"); - stat.addParameter(x); + if (alias != null && value.getClass().isEnum()) { + col = aliasMap.get(alias); + EnumType type = col.getFieldDefinition().enumType; + Enum anEnum = (Enum) value; + Object y = Utils.convertEnum(anEnum, type); + stat.addParameter(y); + } else { + stat.addParameter(value); + } } void addConditionToken(Token condition) { @@ -397,7 +408,7 @@ public class Query { if (i++ > 0) { stat.appendSQL(", "); } - appendSQL(stat, obj); + appendSQL(stat, null, obj); stat.appendSQL(" "); } } diff --git a/src/com/iciql/QueryCondition.java b/src/com/iciql/QueryCondition.java index 8e7cd42..9d13995 100644 --- a/src/com/iciql/QueryCondition.java +++ b/src/com/iciql/QueryCondition.java @@ -41,6 +41,11 @@ public class QueryCondition { return new QueryWhere(query); } + public QueryWhere isNot(A y) { + query.addConditionToken(new Condition(x, y, CompareType.NOT_EQUAL)); + return new QueryWhere(query); + } + public QueryWhere exceeds(A y) { query.addConditionToken(new Condition(x, y, CompareType.EXCEEDS)); return new QueryWhere(query); @@ -60,7 +65,7 @@ public class QueryCondition { query.addConditionToken(new Condition(x, y, CompareType.AT_MOST)); return new QueryWhere(query); } - + public QueryWhere like(A pattern) { query.addConditionToken(new Condition(x, pattern, CompareType.LIKE)); return new QueryWhere(query); diff --git a/src/com/iciql/SQLStatement.java b/src/com/iciql/SQLStatement.java index b6c5e18..3a7c901 100644 --- a/src/com/iciql/SQLStatement.java +++ b/src/com/iciql/SQLStatement.java @@ -63,7 +63,7 @@ public class SQLStatement { } return sql; } - + SQLStatement addParameter(Object o) { params.add(o); return this; diff --git a/src/com/iciql/TableDefinition.java b/src/com/iciql/TableDefinition.java index 5d9b9c4..f0ee4fa 100644 --- a/src/com/iciql/TableDefinition.java +++ b/src/com/iciql/TableDefinition.java @@ -705,7 +705,7 @@ class TableDefinition { } FieldDefinition def = fields.get(i); Object obj = def.getValue(x); - query.appendSQL(stat, obj); + query.appendSQL(stat, x, obj); } } diff --git a/src/com/iciql/TestCondition.java b/src/com/iciql/TestCondition.java index b4c1c15..010f5a1 100644 --- a/src/com/iciql/TestCondition.java +++ b/src/com/iciql/TestCondition.java @@ -39,61 +39,61 @@ public class TestCondition { return Db.registerToken(o, new Function("=", x, y) { public void appendSQL(SQLStatement stat, Query query) { stat.appendSQL("("); - query.appendSQL(stat, x[0]); + query.appendSQL(stat, null, x[0]); stat.appendSQL(" = "); - query.appendSQL(stat, x[1]); + query.appendSQL(stat, x[0], x[1]); stat.appendSQL(")"); } }); } - public Boolean bigger(A y) { + public Boolean exceeds(A y) { Boolean o = Utils.newObject(Boolean.class); return Db.registerToken(o, new Function(">", x, y) { public void appendSQL(SQLStatement stat, Query query) { stat.appendSQL("("); - query.appendSQL(stat, x[0]); + query.appendSQL(stat, null, x[0]); stat.appendSQL(" > "); - query.appendSQL(stat, x[1]); + query.appendSQL(stat, x[0], x[1]); stat.appendSQL(")"); } }); } - public Boolean biggerEqual(A y) { + public Boolean atLeast(A y) { Boolean o = Utils.newObject(Boolean.class); return Db.registerToken(o, new Function(">=", x, y) { public void appendSQL(SQLStatement stat, Query query) { stat.appendSQL("("); - query.appendSQL(stat, x[0]); + query.appendSQL(stat, null, x[0]); stat.appendSQL(" >= "); - query.appendSQL(stat, x[1]); + query.appendSQL(stat, x[0], x[1]); stat.appendSQL(")"); } }); } - public Boolean smaller(A y) { + public Boolean lessThan(A y) { Boolean o = Utils.newObject(Boolean.class); return Db.registerToken(o, new Function("<", x, y) { public void appendSQL(SQLStatement stat, Query query) { stat.appendSQL("("); - query.appendSQL(stat, x[0]); + query.appendSQL(stat, null, x[0]); stat.appendSQL(" < "); - query.appendSQL(stat, x[1]); + query.appendSQL(stat, x[0], x[1]); stat.appendSQL(")"); } }); } - public Boolean smallerEqual(A y) { + public Boolean atMost(A y) { Boolean o = Utils.newObject(Boolean.class); return Db.registerToken(o, new Function("<=", x, y) { public void appendSQL(SQLStatement stat, Query query) { stat.appendSQL("("); - query.appendSQL(stat, x[0]); + query.appendSQL(stat, null, x[0]); stat.appendSQL(" <= "); - query.appendSQL(stat, x[1]); + query.appendSQL(stat, x[0], x[1]); stat.appendSQL(")"); } }); @@ -104,9 +104,9 @@ public class TestCondition { return Db.registerToken(o, new Function("LIKE", x, pattern) { public void appendSQL(SQLStatement stat, Query query) { stat.appendSQL("("); - query.appendSQL(stat, x[0]); + query.appendSQL(stat, null, x[0]); stat.appendSQL(" LIKE "); - query.appendSQL(stat, x[1]); + query.appendSQL(stat, x[0], x[1]); stat.appendSQL(")"); } }); diff --git a/src/com/iciql/UpdateColumnIncrement.java b/src/com/iciql/UpdateColumnIncrement.java index 43ef6d6..143ce48 100644 --- a/src/com/iciql/UpdateColumnIncrement.java +++ b/src/com/iciql/UpdateColumnIncrement.java @@ -44,11 +44,11 @@ public class UpdateColumnIncrement implements UpdateColumn { } public void appendSQL(SQLStatement stat) { - query.appendSQL(stat, x); + query.appendSQL(stat, null, x); stat.appendSQL("=("); - query.appendSQL(stat, x); + query.appendSQL(stat, null, x); stat.appendSQL("+"); - query.appendSQL(stat, y); + query.appendSQL(stat, x, y); stat.appendSQL(")"); } diff --git a/src/com/iciql/UpdateColumnSet.java b/src/com/iciql/UpdateColumnSet.java index 55d3790..f2322e3 100644 --- a/src/com/iciql/UpdateColumnSet.java +++ b/src/com/iciql/UpdateColumnSet.java @@ -17,6 +17,7 @@ package com.iciql; + /** * This class represents "SET column = value" in an UPDATE statement. * @@ -44,9 +45,9 @@ public class UpdateColumnSet implements UpdateColumn { } public void appendSQL(SQLStatement stat) { - query.appendSQL(stat, x); - stat.appendSQL("=?"); - stat.addParameter(y); + query.appendSQL(stat, null, x); + stat.appendSQL("="); + query.appendSQL(stat, x, y); } } diff --git a/src/com/iciql/bytecode/Variable.java b/src/com/iciql/bytecode/Variable.java index 3412349..f3dbc01 100644 --- a/src/com/iciql/bytecode/Variable.java +++ b/src/com/iciql/bytecode/Variable.java @@ -45,7 +45,7 @@ public class Variable implements Token { } public void appendSQL(SQLStatement stat, Query query) { - query.appendSQL(stat, obj); + query.appendSQL(stat, null, obj); } } diff --git a/src/com/iciql/util/Utils.java b/src/com/iciql/util/Utils.java index dac30fc..b8e2d60 100644 --- a/src/com/iciql/util/Utils.java +++ b/src/com/iciql/util/Utils.java @@ -275,6 +275,25 @@ public class Utils { throw new IciqlException("Can not convert the value {0} from {1} to {2}", o, currentType, targetType); } + public static Object convertEnum(Enum o, EnumType type) { + if (o == null || type == null) { + return null; + } + switch (type) { + case ORDINAL: + return o.ordinal(); + case ENUMID: + if (!EnumId.class.isAssignableFrom(o.getClass())) { + throw new IciqlException("Can not convert the enum {0} using ENUMID", o); + } + EnumId enumid = (EnumId) o; + return enumid.enumId(); + case STRING: + default: + return o.name(); + } + } + public static Object convertEnum(Object o, Class targetType, EnumType type) { if (o == null) { return null; diff --git a/tests/com/iciql/test/ModelsTest.java b/tests/com/iciql/test/ModelsTest.java index 8fbf219..acaf634 100644 --- a/tests/com/iciql/test/ModelsTest.java +++ b/tests/com/iciql/test/ModelsTest.java @@ -36,11 +36,17 @@ import com.iciql.DbUpgrader; import com.iciql.DbVersion; import com.iciql.Iciql.IQVersion; import com.iciql.ValidationRemark; +import com.iciql.test.models.EnumModels; +import com.iciql.test.models.EnumModels.EnumIdModel; +import com.iciql.test.models.EnumModels.EnumOrdinalModel; +import com.iciql.test.models.EnumModels.EnumStringModel; +import com.iciql.test.models.EnumModels.Tree; import com.iciql.test.models.Product; import com.iciql.test.models.ProductAnnotationOnly; import com.iciql.test.models.ProductMixedAnnotation; import com.iciql.test.models.SupportedTypes; import com.iciql.test.models.SupportedTypes.SupportedTypes2; +import com.iciql.util.StatementLogger; /** * Test that the mapping between classes and tables is done correctly. @@ -59,7 +65,7 @@ public class ModelsTest { private void log(String text) { System.out.println(text); } - + @Before public void setUp() { db = Db.open("jdbc:h2:mem:", "sa", "sa"); @@ -106,6 +112,39 @@ public class ModelsTest { } } + @Test + public void testEnumQueries() { + StatementLogger.activateConsoleLogger(); + testIntEnums(new EnumIdModel(), EnumIdModel.createList()); + testIntEnums(new EnumOrdinalModel(), EnumOrdinalModel.createList()); + testStringEnums(new EnumStringModel(), EnumStringModel.createList()); + StatementLogger.deactivateConsoleLogger(); + } + + private void testIntEnums(EnumModels e, List models) { + db.insertAll(models); + + EnumModels model = db.from(e).where(e.tree()).is(Tree.WALNUT).selectFirst(); + + assertEquals(400, model.id.intValue()); + assertEquals(Tree.WALNUT, model.tree()); + + List list = db.from(e).where(e.tree()).atLeast(Tree.BIRCH).select(); + assertEquals(3, list.size()); + } + + private void testStringEnums(EnumModels e, List models) { + db.insertAll(models); + + EnumModels model = db.from(e).where(e.tree()).is(Tree.WALNUT).selectFirst(); + + assertEquals(400, model.id.intValue()); + assertEquals(Tree.WALNUT, model.tree()); + + List list = db.from(e).where(e.tree()).isNot(Tree.BIRCH).select(); + assertEquals(models.size() - 1, list.size()); + } + @Test public void testModelGeneration() { List original = SupportedTypes.createList(); diff --git a/tests/com/iciql/test/models/EnumModels.java b/tests/com/iciql/test/models/EnumModels.java new file mode 100644 index 0000000..bd9d88a --- /dev/null +++ b/tests/com/iciql/test/models/EnumModels.java @@ -0,0 +1,153 @@ +/* + * 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.EnumId; +import com.iciql.Iciql.EnumType; +import com.iciql.Iciql.IQColumn; +import com.iciql.Iciql.IQEnum; +import com.iciql.Iciql.IQTable; + +public abstract class EnumModels { + + /** + * Test of @IQEnum annotated enumeration. This strategy is the default + * strategy for all fields of the Tree enum. + * + * Individual Tree field declarations can override this strategy by + * specifying a different @IQEnum annotation. + * + * Here ORDINAL specifies that this enum will be mapped to an INT column. + */ + @IQEnum(EnumType.ENUMID) + public enum Tree implements EnumId { + PINE(10), OAK(20), BIRCH(30), WALNUT(40), MAPLE(50); + + private int enumid; + + Tree(int id) { + this.enumid = id; + } + + @Override + public int enumId() { + return enumid; + } + } + + @IQColumn(primaryKey = true) + public Integer id; + + public abstract Tree tree(); + + /** + * Test model for enum-as-enumid. + */ + @IQTable(inheritColumns = true) + public static class EnumIdModel extends EnumModels { + + // no need to specify ENUMID type as the enumeration definition + // specifies it. + @IQColumn + private Tree tree; + + public EnumIdModel() { + } + + public EnumIdModel(int id, Tree tree) { + this.id = id; + this.tree = tree; + } + + @Override + public Tree tree() { + return tree; + } + + public static List createList() { + return Arrays.asList(new EnumIdModel(400, Tree.WALNUT), new EnumIdModel(200, Tree.OAK), + new EnumIdModel(500, Tree.MAPLE), new EnumIdModel(300, Tree.BIRCH), new EnumIdModel(100, + Tree.PINE)); + } + } + + /** + * Test model for enum-as-ordinal. + */ + @IQTable(inheritColumns = true) + public static class EnumOrdinalModel extends EnumModels { + + // override the enumtype to ordinal + @IQEnum(EnumType.ORDINAL) + @IQColumn + private Tree tree; + + public EnumOrdinalModel() { + } + + public EnumOrdinalModel(int id, Tree tree) { + this.id = id; + this.tree = tree; + } + + @Override + public Tree tree() { + return tree; + } + + public static List createList() { + return Arrays.asList(new EnumOrdinalModel(400, Tree.WALNUT), new EnumOrdinalModel(200, Tree.OAK), + new EnumOrdinalModel(500, Tree.MAPLE), new EnumOrdinalModel(300, Tree.BIRCH), + new EnumOrdinalModel(100, Tree.PINE)); + } + } + + /** + * Test model for enum-as-string. + */ + @IQTable(inheritColumns = true) + public static class EnumStringModel extends EnumModels { + + // override the enumtype to string + // ensure that we specify a length so that the column is VARCHAR + @IQEnum(EnumType.STRING) + @IQColumn(length = 25) + private Tree tree; + + public EnumStringModel() { + } + + public EnumStringModel(int id, Tree tree) { + this.id = id; + this.tree = tree; + } + + @Override + public Tree tree() { + return tree; + } + + public static List createList() { + return Arrays.asList(new EnumStringModel(400, Tree.WALNUT), new EnumStringModel(200, Tree.OAK), + new EnumStringModel(500, Tree.MAPLE), new EnumStringModel(300, Tree.BIRCH), + new EnumStringModel(100, Tree.PINE)); + } + } +} diff --git a/tests/com/iciql/test/models/SupportedTypes.java b/tests/com/iciql/test/models/SupportedTypes.java index c8aebcb..b8abf2d 100644 --- a/tests/com/iciql/test/models/SupportedTypes.java +++ b/tests/com/iciql/test/models/SupportedTypes.java @@ -21,7 +21,6 @@ import java.math.BigDecimal; import java.util.List; import java.util.Random; -import com.iciql.Iciql.EnumId; import com.iciql.Iciql.EnumType; import com.iciql.Iciql.IQColumn; import com.iciql.Iciql.IQEnum; @@ -30,6 +29,7 @@ import com.iciql.Iciql.IQIndexes; import com.iciql.Iciql.IQTable; import com.iciql.Iciql.IQVersion; import com.iciql.Iciql.IndexType; +import com.iciql.test.models.EnumModels.Tree; import com.iciql.util.Utils; /** @@ -51,31 +51,6 @@ public class SupportedTypes { ROSE, TULIP, MUM, PETUNIA, MARIGOLD, DAFFODIL; } - /** - * Test of @IQEnum annotated enumeration. This strategy is the default - * strategy for all fields of the Tree enum. - * - * Individual Tree field declarations can override this strategy by - * specifying a different @IQEnum annotation. - * - * Here ORDINAL specifies that this enum will be mapped to an INT column. - */ - @IQEnum(EnumType.ORDINAL) - public enum Tree implements EnumId { - PINE(10), OAK(20), BIRCH(30), WALNUT(40), MAPLE(50); - - private int enumid; - - Tree(int id) { - this.enumid = id; - } - - @Override - public int enumId() { - return enumid; - } - } - @IQColumn(primaryKey = true, autoIncrement = true) public Integer id; @@ -129,14 +104,14 @@ public class SupportedTypes { @IQColumn private Flower myOtherFavoriteFlower; - @IQColumn(length = 25) + @IQEnum(EnumType.ORDINAL) + @IQColumn + // override the default enum strategy and use the ordinal value + private Tree myFavoriteTree; + // @IQEnum is set on the enumeration definition and is shared // by all uses of Tree as an @IQColumn - private Tree myFavoriteTree; - - @IQEnum(EnumType.ENUMID) @IQColumn - // override the default enum strategy and use the custom enumid private Tree myOtherFavoriteTree; public static List createList() { -- 2.39.5