@@ -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 |
@@ -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 | |||
<table> | |||
<tr><th colspan="2">Standard SQL Types</th></tr> | |||
<tr><td>java.lang.String</td> | |||
<tr><td colspan="3"><b>Fully Supported Types</b><br/> | |||
can be used for all iciql expressions | |||
</tr> | |||
<tr><th>Object</th><th>Primitive</th><th>SQL Type</th></tr> | |||
<tr><td>java.lang.String</td><td></td> | |||
<td>VARCHAR *(length > 0)* or CLOB *(length == 0)*</td></tr> | |||
<tr><td>java.lang.Boolean</td> | |||
<td>BIT</td></tr> | |||
<tr><td>java.lang.Boolean</td><td>boolean</td> | |||
<td>BOOLEAN</td></tr> | |||
<tr><td>java.lang.Byte</td> | |||
<tr><td>java.lang.Byte</td><td>byte</td> | |||
<td>TINYINT</td></tr> | |||
<tr><td>java.lang.Short</td> | |||
<tr><td>java.lang.Short</td><td>short</td> | |||
<td>SMALLINT</td></tr> | |||
<tr><td>java.lang.Integer</td> | |||
<tr><td>java.lang.Integer</td><td>int</td> | |||
<td>INT</td></tr> | |||
<tr><td>java.lang.Long</td> | |||
<tr><td>java.lang.Long</td><td>long</td> | |||
<td>BIGINT</td></tr> | |||
<tr><td>java.lang.Float</td> | |||
<tr><td>java.lang.Float</td><td>float</td> | |||
<td>REAL</td></tr> | |||
<tr><td>java.lang.Double</td> | |||
<tr><td>java.lang.Double</td><td>double</td> | |||
<td>DOUBLE</td></tr> | |||
<tr><td>java.math.BigDecimal</td> | |||
<tr><td>java.math.BigDecimal</td><td></td> | |||
<td>DECIMAL *(length == 0)* or DECIMAL(length,scale) *(length > 0)*</td></tr> | |||
<tr><td>java.sql.Date</td> | |||
<tr><td>java.sql.Date</td><td></td> | |||
<td>DATE</td></tr> | |||
<tr><td>java.sql.Time</td> | |||
<tr><td>java.sql.Time</td><td></td> | |||
<td>TIME</td></tr> | |||
<tr><td>java.sql.Timestamp</td> | |||
<tr><td>java.sql.Timestamp</td><td></td> | |||
<td>TIMESTAMP</td></tr> | |||
<tr><td>java.util.Date</td> | |||
<tr><td>java.util.Date</td><td></td> | |||
<td>TIMESTAMP</td></tr> | |||
<tr><td>java.lang.Enum.name()<br/>*default type*</td> | |||
<tr><td>java.lang.Enum.name()<br/>*default type*</td><td></td> | |||
<td>VARCHAR *(length > 0)* or CLOB *(length == 0)*<br/>*EnumType.NAME*</td></tr> | |||
<tr><td>java.lang.Enum.ordinal()</td> | |||
<tr><td>java.lang.Enum.ordinal()</td><td></td> | |||
<td>INT<br/>*EnumType.ORDINAL*</td></tr> | |||
<tr><td>java.lang.Enum implements<br/>*com.iciql.Iciql.EnumId.enumId()*</td> | |||
<tr><td>java.lang.Enum implements<br/>*com.iciql.Iciql.EnumId.enumId()*</td><td></td> | |||
<td>INT<br/>*EnumType.ENUMID*</td></tr> | |||
<tr><th colspan="2">H2 Database Types</th></tr> | |||
<tr><td>java.util.UUID</td> | |||
<tr><td colspan="3"><b>Partially Supported Types</b><br/> | |||
can not be directly referenced in an expression</td></tr> | |||
<tr><td>byte []</td><td></td> | |||
<td>BLOB</td><tr/> | |||
<tr><td colspan="3"><b>H2 Database Types</b><br/> | |||
fully supported when paired with an H2 database | |||
</td></tr> | |||
<tr><td>java.util.UUID</td><td></td> | |||
<td>UUID</td><tr/> | |||
</table> | |||
@@ -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.<br/> | |||
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 <u>BUT</u> these field types may **not** be used to specify **compile-time** clauses or constraints. | |||
<table> | |||
<tr><td>byte []</td> | |||
<td>BLOB</td></tr> | |||
<tr><td>boolean</td> | |||
<td>BIT</td></tr> | |||
<tr><td>byte</td> | |||
<td>TINYINT</td></tr> | |||
<tr><td>short</td> | |||
<td>SMALLINT</td></tr> | |||
<tr><td>int</td> | |||
<td>INT</td></tr> | |||
<tr><td>long</td> | |||
<td>BIGINT</td></tr> | |||
<tr><td>float</td> | |||
<td>REAL</td></tr> | |||
<tr><td>double</td> | |||
<td>DOUBLE</td></tr> | |||
</table> | |||
#### 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. | |||
@@ -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 <b>_ iq_versions</b> table to *iq_versions* since leading _ character is troublesome for some databases. | |||
- @IQColumn(allowNull=true) -> @IQColumn(nullable=true) |
@@ -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<T> { | |||
private ArrayList<SelectTable<T>> joins = Utils.newArrayList(); | |||
private final IdentityHashMap<Object, SelectColumn<T>> aliasMap = Utils.newIdentityHashMap(); | |||
private ArrayList<OrderExpression<T>> orderByList = Utils.newArrayList(); | |||
private Object[] groupByExpressions; | |||
private ArrayList<Object> groupByExpressions = Utils.newArrayList(); | |||
private long limit; | |||
private long offset; | |||
@@ -148,10 +149,80 @@ public class Query<T> { | |||
return new UpdateColumnSet<T, A>(this, field); | |||
} | |||
public UpdateColumnSet<T, Boolean> set(boolean field) { | |||
return setPrimitive(field); | |||
} | |||
public UpdateColumnSet<T, Byte> set(byte field) { | |||
return setPrimitive(field); | |||
} | |||
public UpdateColumnSet<T, Short> set(short field) { | |||
return setPrimitive(field); | |||
} | |||
public UpdateColumnSet<T, Integer> set(int field) { | |||
return setPrimitive(field); | |||
} | |||
public UpdateColumnSet<T, Long> set(long field) { | |||
return setPrimitive(field); | |||
} | |||
public UpdateColumnSet<T, Float> set(float field) { | |||
return setPrimitive(field); | |||
} | |||
public UpdateColumnSet<T, Double> set(double field) { | |||
return setPrimitive(field); | |||
} | |||
private <A> UpdateColumnSet<T, A> 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 <A> UpdateColumnIncrement<T, A> increment(A field) { | |||
return new UpdateColumnIncrement<T, A>(this, field); | |||
} | |||
public UpdateColumnIncrement<T, Byte> increment(byte field) { | |||
return incrementPrimitive(field); | |||
} | |||
public UpdateColumnIncrement<T, Short> increment(short field) { | |||
return incrementPrimitive(field); | |||
} | |||
public UpdateColumnIncrement<T, Integer> increment(int field) { | |||
return incrementPrimitive(field); | |||
} | |||
public UpdateColumnIncrement<T, Long> increment(long field) { | |||
return incrementPrimitive(field); | |||
} | |||
public UpdateColumnIncrement<T, Float> increment(float field) { | |||
return incrementPrimitive(field); | |||
} | |||
public UpdateColumnIncrement<T, Double> increment(double field) { | |||
return incrementPrimitive(field); | |||
} | |||
private <A> UpdateColumnIncrement<T, A> 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<T> { | |||
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<T, Boolean> 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<T, Byte> 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<T, Short> 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<T, Integer> 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<T, Long> 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<T, Float> 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<T, Double> where(double x) { | |||
return wherePrimitive(x); | |||
} | |||
/** | |||
* Begins a primitive field condition clause. | |||
* | |||
* @param value | |||
* @return a query condition to continue building the condition | |||
*/ | |||
private <A> QueryCondition<T, A> 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 <A> QueryCondition<T, A> where(A x) { | |||
return new QueryCondition<T, A>(this, x); | |||
} | |||
@@ -306,6 +476,48 @@ public class Query<T> { | |||
return this; | |||
} | |||
public Query<T> orderBy(boolean field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> orderBy(byte field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> orderBy(short field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> orderBy(int field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> orderBy(long field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> orderBy(float field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> orderBy(double field) { | |||
return orderByPrimitive(field); | |||
} | |||
Query<T> orderByPrimitive(Object field) { | |||
Object alias = getPrimitiveAliasByValue(field); | |||
if (alias == null) { | |||
return orderBy(field); | |||
} | |||
return orderBy(alias); | |||
} | |||
public Query<T> orderBy(Object expr) { | |||
OrderExpression<T> e = new OrderExpression<T>(this, expr, false, false, false); | |||
addOrderBy(e); | |||
return this; | |||
} | |||
/** | |||
* Order by a number of columns. | |||
* | |||
@@ -328,8 +540,49 @@ public class Query<T> { | |||
return this; | |||
} | |||
public Query<T> groupBy(boolean field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> groupBy(byte field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> groupBy(short field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> groupBy(int field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> groupBy(long field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> groupBy(float field) { | |||
return orderByPrimitive(field); | |||
} | |||
public Query<T> groupBy(double field) { | |||
return orderByPrimitive(field); | |||
} | |||
Query<T> groupByPrimitive(Object field) { | |||
Object alias = getPrimitiveAliasByValue(field); | |||
if (alias == null) { | |||
return groupBy(field); | |||
} | |||
return groupBy(alias); | |||
} | |||
public Query<T> groupBy(Object expr) { | |||
groupByExpressions.add(expr); | |||
return this; | |||
} | |||
public Query<T> groupBy(Object... groupBy) { | |||
this.groupByExpressions = groupBy; | |||
this.groupByExpressions.addAll(Arrays.asList(groupBy)); | |||
return this; | |||
} | |||
@@ -362,7 +615,7 @@ public class Query<T> { | |||
addParameter(stat, alias, value); | |||
return; | |||
} | |||
SelectColumn<T> col = aliasMap.get(value); | |||
SelectColumn<T> col = getColumnByReference(value); | |||
if (col != null) { | |||
col.appendSQL(stat); | |||
return; | |||
@@ -402,7 +655,7 @@ public class Query<T> { | |||
private void addParameter(SQLStatement stat, Object alias, Object value) { | |||
if (alias != null && value.getClass().isEnum()) { | |||
SelectColumn<T> col = aliasMap.get(alias); | |||
SelectColumn<T> 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<T> { | |||
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<T> { | |||
return !joins.isEmpty(); | |||
} | |||
SelectColumn<T> getSelectColumn(Object obj) { | |||
return aliasMap.get(obj); | |||
/** | |||
* This method returns a mapped Object field by its reference. | |||
* | |||
* @param obj | |||
* @return | |||
*/ | |||
private SelectColumn<T> getColumnByReference(Object obj) { | |||
SelectColumn<T> 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> A getPrimitiveAliasByValue(A obj) { | |||
for (Object alias : aliasMap.keySet()) { | |||
if (alias.equals(obj)) { | |||
SelectColumn<T> match = aliasMap.get(alias); | |||
if (match.getFieldDefinition().isPrimitive) { | |||
return (A) alias; | |||
} | |||
} | |||
} | |||
return null; | |||
} | |||
void addOrderBy(OrderExpression<T> expr) { |
@@ -34,11 +34,189 @@ public class QueryWhere<T> { | |||
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<T, Boolean> 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<T, Byte> 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<T, Short> 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<T, Integer> 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<T, Long> 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<T, Float> 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<T, Double> and(double x) { | |||
return addPrimitive(ConditionAndOr.AND, x); | |||
} | |||
private <A> QueryCondition<T, A> 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<T, A>(query, x); | |||
} | |||
return new QueryCondition<T, A>(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 <A> QueryCondition<T, A> and(A x) { | |||
query.addConditionToken(ConditionAndOr.AND); | |||
return new QueryCondition<T, A>(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<T, Boolean> 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<T, Byte> 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<T, Short> 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<T, Integer> 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<T, Long> 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<T, Float> 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<T, Double> 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 <A> QueryCondition<T, A> or(A x) { | |||
query.addConditionToken(ConditionAndOr.OR); | |||
return new QueryCondition<T, A>(query, x); | |||
@@ -88,7 +266,86 @@ public class QueryWhere<T> { | |||
} | |||
/** | |||
* Order by a number of columns. | |||
* Order by primitive boolean field | |||
* | |||
* @param field | |||
* a primitive boolean field | |||
* @return the query | |||
*/ | |||
public QueryWhere<T> orderBy(boolean field) { | |||
return orderByPrimitive(field); | |||
} | |||
/** | |||
* Order by primitive byte field | |||
* | |||
* @param field | |||
* a primitive byte field | |||
* @return the query | |||
*/ | |||
public QueryWhere<T> orderBy(byte field) { | |||
return orderByPrimitive(field); | |||
} | |||
/** | |||
* Order by primitive short field | |||
* | |||
* @param field | |||
* a primitive short field | |||
* @return the query | |||
*/ | |||
public QueryWhere<T> orderBy(short field) { | |||
return orderByPrimitive(field); | |||
} | |||
public QueryWhere<T> orderBy(int field) { | |||
return orderByPrimitive(field); | |||
} | |||
/** | |||
* Order by primitive long field | |||
* | |||
* @param field | |||
* a primitive long field | |||
* @return the query | |||
*/ | |||
public QueryWhere<T> orderBy(long field) { | |||
return orderByPrimitive(field); | |||
} | |||
/** | |||
* Order by primitive float field | |||
* | |||
* @param field | |||
* a primitive float field | |||
* @return the query | |||
*/ | |||
public QueryWhere<T> orderBy(float field) { | |||
return orderByPrimitive(field); | |||
} | |||
/** | |||
* Order by primitive double field | |||
* | |||
* @param field | |||
* a primitive double field | |||
* @return the query | |||
*/ | |||
public QueryWhere<T> orderBy(double field) { | |||
return orderByPrimitive(field); | |||
} | |||
private QueryWhere<T> orderByPrimitive(Object field) { | |||
query.orderByPrimitive(field); | |||
return this; | |||
} | |||
public QueryWhere<T> 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<T> { | |||
*/ | |||
public QueryWhere<T> orderBy(Object... expressions) { | |||
for (Object expr : expressions) { | |||
OrderExpression<T> e = new OrderExpression<T>(query, expr, false, false, false); | |||
query.addOrderBy(e); | |||
} | |||
query.orderBy(expressions); | |||
return this; | |||
} | |||
@@ -79,6 +79,7 @@ public class TableDefinition<T> { | |||
boolean nullable; | |||
String defaultValue; | |||
EnumType enumType; | |||
boolean isPrimitive; | |||
Object getValue(Object obj) { | |||
try { | |||
@@ -345,6 +346,7 @@ public class TableDefinition<T> { | |||
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<T> { | |||
query.appendSQL(stat, x, obj); | |||
} | |||
} | |||
<Y, X> void copyAttributeValues(Query<Y> query, X to, X map) { | |||
for (FieldDefinition def : fields) { | |||
Object obj = def.getValue(map); | |||
SelectColumn<Y> col = query.getSelectColumn(obj); | |||
Object value = col.getCurrentValue(); | |||
def.setValue(to, value); | |||
} | |||
} | |||
} |
@@ -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 { |
@@ -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<Product> 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(); | |||
} | |||
} |
@@ -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<PrimitivesModel> 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<PrimitivesModel> 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(); | |||
} |
@@ -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<PrimitivesModel> getList() { | |||
List<PrimitivesModel> list = new ArrayList<PrimitivesModel>(); | |||
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); | |||
} | |||
} |