Browse Source

add support stackable condition: ex. X and (Y or Z)

see samples in
src/test/java/com/iciql/test/StackableConditionsTest.java
tags/v1.3.0
Sotaro SUZUKI 9 years ago
parent
commit
48da60f625

+ 33
- 0
src/main/java/com/iciql/ConditionOpenClose.java View File

@@ -0,0 +1,33 @@
/*
* Copyright 2004-2011 H2 Group.
* 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;

enum ConditionOpenClose implements Token {
OPEN("("), CLOSE(")");

private String text;

ConditionOpenClose(String text) {
this.text = text;
}

public <T> void appendSQL(SQLStatement stat, Query<T> query) {
stat.appendSQL(text);
}

}

+ 132
- 0
src/main/java/com/iciql/Conditions.java View File

@@ -0,0 +1,132 @@
/*
* Copyright (c) 2009-2014, Architector Inc., Japan
* All rights reserved.
*
* 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;

public abstract class Conditions<T> {

public static class And<T> extends Conditions<T> {

public And(Db db, T alias) {
super(db, alias);
}

protected QueryCondition<T, Boolean> and(boolean x) {
return where.and(x);
}

protected QueryCondition<T, Byte> and(byte x) {
return where.and(x);
}

protected QueryCondition<T, Short> and(short x) {
return where.and(x);
}

protected QueryCondition<T, Integer> and(int x) {
return where.and(x);
}

protected QueryCondition<T, Long> and(long x) {
return where.and(x);
}

protected QueryCondition<T, Float> and(float x) {
return where.and(x);
}

protected QueryCondition<T, Double> and(double x) {
return where.and(x);
}

protected <A> QueryCondition<T, A> and(A x) {
return where.and(x);
}

protected QueryWhere<T> and(And<T> conditions) {
where.andOpenTrue();
where.query.addConditionToken(conditions.where.query);
return where.close();
}

protected QueryWhere<T> and(Or<T> conditions) {
where.andOpenFalse();
where.query.addConditionToken(conditions.where.query);
return where.close();
}

}

public static class Or<T> extends Conditions<T> {

public Or(Db db, T alias) {
super(db, alias);
}

protected QueryCondition<T, Boolean> or(boolean x) {
return where.or(x);
}

protected QueryCondition<T, Byte> or(byte x) {
return where.or(x);
}

protected QueryCondition<T, Short> or(short x) {
return where.or(x);
}

protected QueryCondition<T, Integer> or(int x) {
return where.or(x);
}

protected QueryCondition<T, Long> or(long x) {
return where.or(x);
}

protected QueryCondition<T, Float> or(float x) {
return where.or(x);
}

protected QueryCondition<T, Double> or(double x) {
return where.or(x);
}

protected <A> QueryCondition<T, A> or(A x) {
return where.or(x);
}

protected QueryWhere<T> or(And<T> conditions) {
where.orOpenTrue();
where.query.addConditionToken(conditions.where.query);
return where.close();
}

protected QueryWhere<T> or(Or<T> conditions) {
where.orOpenFalse();
where.query.addConditionToken(conditions.where.query);
return where.close();
}

}

QueryWhere<T> where;

private Conditions(Db db, T alias) {
where = new QueryWhere<T>(Query.rebuild(db, alias));
}

}

+ 51
- 3
src/main/java/com/iciql/Query.java View File

@@ -27,7 +27,8 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import com.iciql.Conditions.And;
import com.iciql.Conditions.Or;
import com.iciql.Iciql.EnumType;
import com.iciql.bytecode.ClassReader;
import com.iciql.util.JdbcUtils;
@@ -47,6 +48,7 @@ public class Query<T> {
private SelectTable<T> from;
private ArrayList<Token> conditions = Utils.newArrayList();
private ArrayList<UpdateColumn> updateColumnDeclarations = Utils.newArrayList();
private int conditionDepth = 0;
private ArrayList<SelectTable<T>> joins = Utils.newArrayList();
private final IdentityHashMap<Object, SelectColumn<T>> aliasMap = Utils.newIdentityHashMap();
private ArrayList<OrderExpression<T>> orderByList = Utils.newArrayList();
@@ -70,7 +72,16 @@ public class Query<T> {
Query<T> query = new Query<T>(db);
TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
query.from = new SelectTable<T>(db, query, alias, false);
def.initSelectObject(query.from, alias, query.aliasMap);
def.initSelectObject(query.from, alias, query.aliasMap, false);
return query;
}
@SuppressWarnings("unchecked")
static <T> Query<T> rebuild(Db db, T alias) {
Query<T> query = new Query<T>(db);
TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
query.from = new SelectTable<T>(db, query, alias, false);
def.initSelectObject(query.from, alias, query.aliasMap, true);
return query;
}
@@ -583,6 +594,26 @@ public class Query<T> {
return new QueryWhere<T>(this);
}
public Query<T> where(And<T> conditions) {
whereTrue();
addConditionToken(conditions.where.query);
return this;
}
public Query<T> where(Or<T> conditions) {
whereFalse();
addConditionToken(conditions.where.query);
return this;
}
public QueryWhere<T> whereTrue() {
return whereTrue(true);
}
public QueryWhere<T> whereFalse() {
return whereTrue(false);
}
public QueryWhere<T> whereTrue(Boolean condition) {
Token token = new Function("", condition);
addConditionToken(token);
@@ -821,14 +852,31 @@ public class Query<T> {
}
void addConditionToken(Token condition) {
if (condition == ConditionOpenClose.OPEN) {
conditionDepth ++;
} else if (condition == ConditionOpenClose.CLOSE) {
conditionDepth --;
if (conditionDepth < 0) {
throw new IciqlException("unmatch condition open-close count");
}
}
conditions.add(condition);
}
void addConditionToken(Query<T> other) {
for (Token condition : other.conditions) {
addConditionToken(condition);
}
}
void addUpdateColumnDeclaration(UpdateColumn declaration) {
updateColumnDeclarations.add(declaration);
}
void appendWhere(SQLStatement stat) {
if (conditionDepth != 0) {
throw new IciqlException("unmatch condition open-close count");
}
if (!conditions.isEmpty()) {
stat.appendSQL(" WHERE ");
for (Token token : conditions) {
@@ -897,7 +945,7 @@ public class Query<T> {
private <A> QueryJoin<T> join(A alias, boolean outerJoin) {
TableDefinition<T> def = (TableDefinition<T>) db.define(alias.getClass());
SelectTable<T> join = new SelectTable(db, this, alias, outerJoin);
def.initSelectObject(join, alias, aliasMap);
def.initSelectObject(join, alias, aliasMap, false);
joins.add(join);
return new QueryJoin(this, join);
}

+ 55
- 0
src/main/java/com/iciql/QueryWhere.java View File

@@ -19,6 +19,9 @@ package com.iciql;
import java.util.List;
import com.iciql.Conditions.And;
import com.iciql.Conditions.Or;
/**
* This class represents a query with a condition.
*
@@ -135,6 +138,26 @@ public class QueryWhere<T> {
return new QueryCondition<T, A>(query, x);
}
public QueryWhere<T> and(And<T> conditions) {
andOpenTrue();
query.addConditionToken(conditions.where.query);
return close();
}
public QueryWhere<T> and(Or<T> conditions) {
andOpenFalse();
query.addConditionToken(conditions.where.query);
return close();
}
public QueryWhere<T> andOpenTrue() {
return open(ConditionAndOr.AND, true);
}
public QueryWhere<T> andOpenFalse() {
return open(ConditionAndOr.AND, false);
}
/**
* Specify an OR condition with a mapped primitive boolean.
*
@@ -226,6 +249,38 @@ public class QueryWhere<T> {
return new QueryCondition<T, A>(query, x);
}
public QueryWhere<T> or(And<T> conditions) {
orOpenTrue();
query.addConditionToken(conditions.where.query);
return close();
}
public QueryWhere<T> or(Or<T> conditions) {
orOpenFalse();
query.addConditionToken(conditions.where.query);
return close();
}
public QueryWhere<T> orOpenTrue() {
return open(ConditionAndOr.OR, true);
}
public QueryWhere<T> orOpenFalse() {
return open(ConditionAndOr.OR, false);
}
private QueryWhere<T> open(ConditionAndOr andOr, Boolean condition) {
query.addConditionToken(andOr);
query.addConditionToken(ConditionOpenClose.OPEN);
query.addConditionToken(new Function("", condition));
return this;
}
public QueryWhere<T> close() {
query.addConditionToken(ConditionOpenClose.CLOSE);
return this;
}
public QueryWhere<T> limit(long limit) {
query.limit(limit);
return this;

+ 8
- 3
src/main/java/com/iciql/TableDefinition.java View File

@@ -1131,11 +1131,16 @@ public class TableDefinition<T> {
}
}
void initSelectObject(SelectTable<T> table, Object obj, Map<Object, SelectColumn<T>> map) {
void initSelectObject(SelectTable<T> table, Object obj, Map<Object, SelectColumn<T>> map, boolean reuse) {
for (FieldDefinition def : fields) {
Object newValue = def.initWithNewObject(obj);
Object value;
if (!reuse) {
value = def.initWithNewObject(obj);
} else {
value = def.getValue(obj);
}
SelectColumn<T> column = new SelectColumn<T>(table, def);
map.put(newValue, column);
map.put(value, column);
}
}

+ 1
- 1
src/test/java/com/iciql/test/IciqlSuite.java View File

@@ -93,7 +93,7 @@ import com.iciql.util.Utils;
@SuiteClasses({ AliasMapTest.class, AnnotationsTest.class, BooleanModelTest.class, ClobTest.class,
ConcurrencyTest.class, EnumsTest.class, ModelsTest.class, PrimitivesTest.class, OneOfTest.class,
RuntimeQueryTest.class, SamplesTest.class, UpdateTest.class, UpgradesTest.class, JoinTest.class,
UUIDTest.class, ViewsTest.class, ForeignKeyTest.class, TransactionTest.class })
UUIDTest.class, ViewsTest.class, ForeignKeyTest.class, TransactionTest.class, StackableConditionsTest.class })
public class IciqlSuite {
private static final TestDb[] TEST_DBS = {

+ 178
- 0
src/test/java/com/iciql/test/StackableConditionsTest.java View File

@@ -0,0 +1,178 @@
/*
* Copyright (c) 2009-2014, Architector Inc., Japan
* All rights reserved.
*
* 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 org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.iciql.Conditions.And;
import com.iciql.Conditions.Or;
import com.iciql.Db;
import com.iciql.IciqlException;
import com.iciql.QueryWhere;
import com.iciql.test.models.Customer;

public class StackableConditionsTest {

enum Region {
JP, FR
}

private Db db;

@Before
public void setUp() {
db = IciqlSuite.openNewDb();
}

@After
public void tearDown() {
db.close();
}

private String search(Region region, String... customerIds) {
Customer model;
QueryWhere<Customer> query;

model = new Customer();
query = db.from(model).whereTrue();
if (customerIds != null) {
query.andOpenFalse();
for (String value : customerIds) {
query.or(model.customerId).is(value);
}
query.close();
}
if (region != null) {
query.and(model.region).is(region.name());
}
return query.toSQL();
}

@SuppressWarnings("serial")
@Test
public void andOrTest() {
assertEquals(
search(null, (String[]) null),
"SELECT * FROM Customer WHERE (true)");
assertEquals(
search(null, new String[0]),
"SELECT * FROM Customer WHERE (true) AND ( (false) )");
assertEquals(
search(null, "0001"),
"SELECT * FROM Customer WHERE (true) AND ( (false) OR customerId = '0001' )");
assertEquals(
search(null, "0001", "0002"),
"SELECT * FROM Customer WHERE (true) AND ( (false) OR customerId = '0001' OR customerId = '0002' )");
assertEquals(
search(Region.JP, (String[]) null),
"SELECT * FROM Customer WHERE (true) AND region = 'JP'");
assertEquals(
search(Region.JP, new String[0]),
"SELECT * FROM Customer WHERE (true) AND ( (false) ) AND region = 'JP'");
assertEquals(
search(Region.JP, "0001"),
"SELECT * FROM Customer WHERE (true) AND ( (false) OR customerId = '0001' ) AND region = 'JP'");
assertEquals(
search(Region.JP, "0001", "0002"),
"SELECT * FROM Customer WHERE (true) AND ( (false) OR customerId = '0001' OR customerId = '0002' ) AND region = 'JP'");
}

@Test
public void errorTest() {
Customer model;

model = new Customer();
try {
db.from(model)
.where(model.customerId).is("0001")
.andOpenFalse()
.or(model.region).is("FR")
.or(model.region).is("JP")
.close()
.toSQL();
assertTrue(true);
}
catch (IciqlException error) {
assertTrue(false);
}
try {
db.from(model)
.where(model.customerId).is("0001")
.andOpenFalse()
.or(model.region).is("FR")
.or(model.region).is("JP")
.toSQL();
assertTrue(false);
}
catch (IciqlException error) {
assertTrue(true);
}
try {
db.from(model)
.where(model.customerId).is("0001")
.andOpenFalse()
.or(model.region).is("FR")
.or(model.region).is("JP")
.close()
.close();
assertTrue(false);
}
catch (IciqlException error) {
assertTrue(true);
}
}

@Test
public void fluentTest() {
final Customer model = new Customer();
assertEquals(
db.from(model).where(new And<Customer>(db, model) {{
and(model.customerId).is("0001");
and(new Or<Customer>(db, model) {{
or(model.region).is("CA");
or(model.region).is("LA");
}});
}}).toSQL(),
"SELECT * FROM Customer WHERE (true) AND customerId = '0001' AND ( (false) OR region = 'CA' OR region = 'LA' )");
assertEquals(
db.from(model).where(new Or<Customer>(db, model) {{
or(model.customerId).is("0001");
or(new And<Customer>(db, model) {{
and(model.customerId).is("0002");
and(model.region).is("LA");
}});
}}).toSQL(),
"SELECT * FROM Customer WHERE (false) OR customerId = '0001' OR ( (true) AND customerId = '0002' AND region = 'LA' )");
assertEquals(
db.from(model)
.where(model.customerId).isNotNull()
.and(new Or<Customer>(db, model) {{
or(model.region).is("LA");
or(model.region).is("CA");
}})
.and(model.region).isNotNull()
.toSQL(),
"SELECT * FROM Customer WHERE customerId IS NOT NULL AND ( (false) OR region = 'LA' OR region = 'CA' ) AND region IS NOT NULL");
}

}

Loading…
Cancel
Save