@@ -9,7 +9,7 @@ Alternatively, model classes can be automatically generated by iciql using the m | |||
<table> | |||
<tr><td>java.lang.String</td> | |||
<td>VARCHAR *(maxLength > 0)* or TEXT *(maxLength == 0)*</td></tr> | |||
<td>VARCHAR *(length > 0)* or TEXT *(length == 0)*</td></tr> | |||
<tr><td>java.lang.Boolean</td> | |||
<td>BIT</td></tr> | |||
@@ -51,7 +51,7 @@ Alternatively, model classes can be automatically generated by iciql using the m | |||
<td>BLOB</td></tr> | |||
<tr><td>java.lang.Enum.name()</td> | |||
<td>VARCHAR (maxLength > 0) or TEXT (maxLength == 0)<br/>EnumType.STRING</td></tr> | |||
<td>VARCHAR (length > 0) or TEXT (length == 0)<br/>EnumType.STRING</td></tr> | |||
<tr><td>java.lang.Enum.ordinal()</td> | |||
<td>INT<br/>EnumType.ORDINAL</td></tr> | |||
@@ -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); | |||
} | |||
} |
@@ -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 | |||
@@ -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); | |||
} |
@@ -37,7 +37,7 @@ import java.lang.annotation.Target; | |||
* <table> | |||
* <tr> | |||
* <td>java.lang.String</td> | |||
* <td>VARCHAR (maxLength > 0) or TEXT (maxLength == 0)</td> | |||
* <td>VARCHAR (length > 0) or TEXT (length == 0)</td> | |||
* </tr> | |||
* <tr> | |||
* <td>java.lang.Boolean</td> | |||
@@ -93,15 +93,19 @@ import java.lang.annotation.Target; | |||
* </tr> | |||
* <tr> | |||
* <td>java.lang.Enum.name()</td> | |||
* <td>VARCHAR (maxLength > 0) or TEXT (maxLength == 0)<br/>EnumType.STRING</td> | |||
* <td>VARCHAR (length > 0) or TEXT (length == 0)<br/> | |||
* EnumType.STRING</td> | |||
* </tr> | |||
* <tr> | |||
* <td>java.lang.Enum.ordinal()</td> | |||
* <td>INT<br/>EnumType.ORDINAL</td> | |||
* <td>INT<br/> | |||
* EnumType.ORDINAL</td> | |||
* </tr> | |||
* <tr> | |||
* <td>java.lang.Enum implements<br/>com.iciql.Iciql.EnumID.enumId()</td> | |||
* <td>INT<br/>EnumType.ENUMID</td> | |||
* <td>java.lang.Enum implements<br/> | |||
* com.iciql.Iciql.EnumID.enumId()</td> | |||
* <td>INT<br/> | |||
* EnumType.ENUMID</td> | |||
* </tr> | |||
* </tr> | |||
* </table> | |||
@@ -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). | |||
* <p> | |||
* 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 { | |||
* <li>ORDINAL - ordinal() : int | |||
* <li>ENUMID - enumId() : int | |||
* </ul> | |||
* @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) | |||
* </pre> | |||
* | |||
* 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) |
@@ -222,6 +222,7 @@ public class Query<T> { | |||
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())) { |
@@ -285,8 +285,8 @@ class TableDefinition<T> { | |||
} | |||
isAutoIncrement = col.autoIncrement(); | |||
isPrimaryKey = col.primaryKey(); | |||
maxLength = col.maxLength(); | |||
trimString = col.trimString(); | |||
maxLength = col.length(); | |||
trimString = col.trim(); | |||
allowNull = col.allowNull(); | |||
defaultValue = col.defaultValue(); | |||
} |
@@ -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 |
@@ -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; |
@@ -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. | |||
* <ul> | |||
* <li>mapping a BIT/BOOLEAN column as an Integer | |||
* <li>mapping a INT column as a Boolean. | |||
* </ul> | |||
*/ | |||
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<BooleanAsIntModel> 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<BooleanModel> 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(); | |||
} | |||
} |
@@ -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 { | |||
@@ -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 |
@@ -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<BooleanModel> 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<BooleanAsIntModel> getList() { | |||
return Arrays.asList(new BooleanAsIntModel(1, true), new BooleanAsIntModel(2, false), | |||
new BooleanAsIntModel(3, true), new BooleanAsIntModel(4, false)); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
@@ -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") |
@@ -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) |
@@ -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; |