mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-24 15:55:55 +00:00
#355 - ENH: Add notIn(...) expressions. Just to make life easier in those cases
This commit is contained in:
@@ -169,7 +169,22 @@ public interface ExpressionFactory {
|
||||
* In - property has a value in the collection of values.
|
||||
*/
|
||||
Expression in(String propertyName, Collection<?> values);
|
||||
|
||||
|
||||
/**
|
||||
* Not In - property has a value in the array of values.
|
||||
*/
|
||||
Expression notIn(String propertyName, Object[] values);
|
||||
|
||||
/**
|
||||
* Not In - property has a value in the collection of values.
|
||||
*/
|
||||
Expression notIn(String propertyName, Collection<?> values);
|
||||
|
||||
/**
|
||||
* Not In - using a subQuery.
|
||||
*/
|
||||
Expression notIn(String propertyName, Query<?> subQuery);
|
||||
|
||||
/**
|
||||
* Exists expression
|
||||
*/
|
||||
|
||||
@@ -488,7 +488,22 @@ public interface ExpressionList<T> extends Serializable {
|
||||
* In - property has a value in the collection of values.
|
||||
*/
|
||||
ExpressionList<T> in(String propertyName, Collection<?> values);
|
||||
|
||||
|
||||
/**
|
||||
* Not In - property has a value in the array of values.
|
||||
*/
|
||||
ExpressionList<T> notIn(String propertyName, Object... values);
|
||||
|
||||
/**
|
||||
* Not In - property has a value in the collection of values.
|
||||
*/
|
||||
ExpressionList<T> notIn(String propertyName, Collection<?> values);
|
||||
|
||||
/**
|
||||
* Not In - using a subQuery.
|
||||
*/
|
||||
ExpressionList<T> notIn(String propertyName, Query<?> subQuery);
|
||||
|
||||
/**
|
||||
* Exists expression
|
||||
*/
|
||||
|
||||
+25
-4
@@ -225,23 +225,44 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
* In - property has a value in the array of values.
|
||||
*/
|
||||
public Expression in(String propertyName, Object[] values) {
|
||||
return new InExpression(propertyName, values);
|
||||
return new InExpression(propertyName, values, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* In - using a subQuery.
|
||||
*/
|
||||
public Expression in(String propertyName, Query<?> subQuery) {
|
||||
return new InQueryExpression(propertyName, (SpiQuery<?>) subQuery);
|
||||
return new InQueryExpression(propertyName, (SpiQuery<?>) subQuery, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* In - property has a value in the collection of values.
|
||||
*/
|
||||
public Expression in(String propertyName, Collection<?> values) {
|
||||
return new InExpression(propertyName, values);
|
||||
return new InExpression(propertyName, values, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* In - property has a value in the array of values.
|
||||
*/
|
||||
public Expression notIn(String propertyName, Object[] values) {
|
||||
return new InExpression(propertyName, values, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Not In - property has a value in the collection of values.
|
||||
*/
|
||||
public Expression notIn(String propertyName, Collection<?> values) {
|
||||
return new InExpression(propertyName, values, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* In - using a subQuery.
|
||||
*/
|
||||
public Expression notIn(String propertyName, Query<?> subQuery) {
|
||||
return new InQueryExpression(propertyName, (SpiQuery<?>) subQuery, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exists subquery
|
||||
*/
|
||||
|
||||
@@ -12,16 +12,20 @@ class InExpression extends AbstractExpression {
|
||||
|
||||
private static final long serialVersionUID = 3150665801693551260L;
|
||||
|
||||
private final boolean not;
|
||||
|
||||
private final Object[] values;
|
||||
|
||||
InExpression(String propertyName, Collection<?> coll) {
|
||||
InExpression(String propertyName, Collection<?> coll, boolean not) {
|
||||
super(propertyName);
|
||||
values = coll.toArray(new Object[coll.size()]);
|
||||
this.values = coll.toArray(new Object[coll.size()]);
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
InExpression(String propertyName, Object[] array) {
|
||||
InExpression(String propertyName, Object[] array, boolean not) {
|
||||
super(propertyName);
|
||||
this.values = array;
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
public void addBindValues(SpiExpressionRequest request) {
|
||||
@@ -50,8 +54,10 @@ class InExpression extends AbstractExpression {
|
||||
public void addSql(SpiExpressionRequest request) {
|
||||
|
||||
if (values.length == 0) {
|
||||
// 'no match' for in empty collection
|
||||
request.append("1=0");
|
||||
if (!not) {
|
||||
// 'no match' for in empty collection
|
||||
request.append("1=0");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -69,6 +75,9 @@ class InExpression extends AbstractExpression {
|
||||
|
||||
} else {
|
||||
request.append(propertyName);
|
||||
if (not) {
|
||||
request.append(" not");
|
||||
}
|
||||
request.append(" in (?");
|
||||
for (int i = 1; i < values.length; i++) {
|
||||
request.append(", ").append("?");
|
||||
@@ -82,7 +91,7 @@ class InExpression extends AbstractExpression {
|
||||
* Based on the number of values in the in clause.
|
||||
*/
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(InExpression.class).add(propName).add(values.length);
|
||||
builder.add(InExpression.class).add(propName).add(values.length).add(not);
|
||||
builder.bind(values.length);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,18 +18,20 @@ class InQueryExpression extends AbstractExpression {
|
||||
|
||||
private static final long serialVersionUID = 666990277309851644L;
|
||||
|
||||
private final boolean not;
|
||||
|
||||
private final SpiQuery<?> subQuery;
|
||||
|
||||
private transient CQuery<?> compiledSubQuery;
|
||||
|
||||
public InQueryExpression(String propertyName, SpiQuery<?> subQuery) {
|
||||
public InQueryExpression(String propertyName, SpiQuery<?> subQuery, boolean not) {
|
||||
super(propertyName);
|
||||
this.subQuery = subQuery;
|
||||
this.not = not;
|
||||
}
|
||||
|
||||
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
|
||||
builder.add(InQueryExpression.class).add(propName);
|
||||
|
||||
builder.add(InQueryExpression.class).add(propName).add(not);
|
||||
subQuery.queryAutofetchHash(builder);
|
||||
}
|
||||
|
||||
@@ -61,9 +63,11 @@ class InQueryExpression extends AbstractExpression {
|
||||
subSelect = subSelect.replace('\n', ' ');
|
||||
|
||||
String propertyName = getPropertyName();
|
||||
request.append(" (");
|
||||
request.append(propertyName);
|
||||
request.append(") in (");
|
||||
request.append(" (").append(propertyName).append(")");
|
||||
if (not) {
|
||||
request.append(" not");
|
||||
}
|
||||
request.append(" in (");
|
||||
request.append(subSelect);
|
||||
request.append(") ");
|
||||
}
|
||||
|
||||
@@ -367,7 +367,22 @@ abstract class JunctionExpression<T> implements Junction<T>, SpiExpression, Expr
|
||||
public ExpressionList<T> in(String propertyName, com.avaje.ebean.Query<?> subQuery) {
|
||||
return exprList.in(propertyName, subQuery);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> notIn(String propertyName, Collection<?> values) {
|
||||
return exprList.notIn(propertyName, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> notIn(String propertyName, Object... values) {
|
||||
return exprList.notIn(propertyName, values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> notIn(String propertyName, com.avaje.ebean.Query<?> subQuery) {
|
||||
return exprList.notIn(propertyName, subQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> exists(Query<?> subQuery) {
|
||||
return exprList.exists(subQuery);
|
||||
|
||||
@@ -475,6 +475,24 @@ public class DefaultExpressionList<T> implements SpiExpressionList<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> notIn(String propertyName, Object... values) {
|
||||
add(expr.notIn(propertyName, values));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> notIn(String propertyName, Collection<?> values) {
|
||||
add(expr.notIn(propertyName, values));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> notIn(String propertyName, Query<?> subQuery) {
|
||||
add(expr.notIn(propertyName, subQuery));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionList<T> exists(Query<?> subQuery) {
|
||||
add(expr.exists(subQuery));
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.avaje.ebeaninternal.server.expression;
|
||||
|
||||
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class InExpressionTest {
|
||||
|
||||
|
||||
@Test
|
||||
public void queryPlanHash_given_diffPropertyName_should_differentPlanHash() throws Exception {
|
||||
|
||||
List<Integer> values = values(42, 92);
|
||||
|
||||
InExpression ex1 = new InExpression("foo", values, false);
|
||||
InExpression ex2 = new InExpression("bar", values, false);
|
||||
|
||||
HashQueryPlanBuilder b1 = new HashQueryPlanBuilder();
|
||||
ex1.queryPlanHash(null, b1);
|
||||
|
||||
HashQueryPlanBuilder b2 = new HashQueryPlanBuilder();
|
||||
ex2.queryPlanHash(null, b2);
|
||||
|
||||
assertNotEquals(b1.build(), b2.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryPlanHash_given_diffBindCount_should_differentPlanHash() throws Exception {
|
||||
|
||||
List<Integer> values1 = values(42, 92);
|
||||
List<Integer> values2 = values(42, 92, 82);
|
||||
|
||||
InExpression ex1 = new InExpression("foo", values1, false);
|
||||
InExpression ex2 = new InExpression("foo", values2, false);
|
||||
|
||||
HashQueryPlanBuilder b1 = new HashQueryPlanBuilder();
|
||||
ex1.queryPlanHash(null, b1);
|
||||
|
||||
HashQueryPlanBuilder b2 = new HashQueryPlanBuilder();
|
||||
ex2.queryPlanHash(null, b2);
|
||||
|
||||
assertNotEquals(b1.build(), b2.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryPlanHash_given_diffNotFlag_should_differentPlanHash() throws Exception {
|
||||
|
||||
List<Integer> values = values(42, 92);
|
||||
|
||||
InExpression ex1 = new InExpression("foo", values, true);
|
||||
InExpression ex2 = new InExpression("foo", values, false);
|
||||
|
||||
HashQueryPlanBuilder b1 = new HashQueryPlanBuilder();
|
||||
ex1.queryPlanHash(null, b1);
|
||||
|
||||
HashQueryPlanBuilder b2 = new HashQueryPlanBuilder();
|
||||
ex2.queryPlanHash(null, b2);
|
||||
|
||||
assertNotEquals(b1.build(), b2.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queryPlanHash_given_sameNotFlag_should_samePlanHash() throws Exception {
|
||||
|
||||
List<Integer> values = values(42, 92);
|
||||
|
||||
InExpression ex1 = new InExpression("foo", values, true);
|
||||
InExpression ex2 = new InExpression("foo", values, true);
|
||||
|
||||
HashQueryPlanBuilder b1 = new HashQueryPlanBuilder();
|
||||
ex1.queryPlanHash(null, b1);
|
||||
|
||||
HashQueryPlanBuilder b2 = new HashQueryPlanBuilder();
|
||||
ex2.queryPlanHash(null, b2);
|
||||
|
||||
assertEquals(b1.build(), b2.build());
|
||||
}
|
||||
|
||||
List<Integer> values(int... vals) {
|
||||
ArrayList list = new ArrayList<Integer>();
|
||||
for (int i = 0; i < vals.length; i++) {
|
||||
list.add(vals[i]);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,10 @@ import com.avaje.tests.model.basic.CKeyParent;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
|
||||
public class TestQueryAlias extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void testExists() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<CKeyParent> sq = Ebean.createQuery(CKeyParent.class)
|
||||
@@ -24,7 +26,6 @@ public class TestQueryAlias extends BaseTestCase {
|
||||
|
||||
String sql = pq.getGeneratedSql();
|
||||
|
||||
System.out.println(sql);
|
||||
// Without alias command is should be:
|
||||
// select t0.one_key c0, t0.two_key c1, t0.name c2, t0.version c3, t0.assoc_id c4 from ckey_parent t0 where (t0
|
||||
// .one_key) in (select t0.one_key from ckey_parent t0)
|
||||
@@ -32,7 +33,34 @@ public class TestQueryAlias extends BaseTestCase {
|
||||
// select myt0.one_key c0, myt0.two_key c1, myt0.name c2, myt0.version c3, myt0.assoc_id c4 from ckey_parent myt0
|
||||
// where (myt0.one_key) in (select st0.one_key from ckey_parent st0)
|
||||
|
||||
Assert.assertTrue(sql.indexOf("ckey_parent myt0") > 0);
|
||||
Assert.assertTrue(sql.indexOf("in (select st0.one_key from ckey_parent st0)") > 0);
|
||||
Assert.assertTrue(sql.contains("ckey_parent myt0"));
|
||||
Assert.assertTrue(sql.contains("(myt0.one_key) in (select st0.one_key from ckey_parent st0)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotExists() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<CKeyParent> sq = Ebean.createQuery(CKeyParent.class)
|
||||
.select("id.oneKey").alias("st0")
|
||||
.setAutofetch(false).where().query();
|
||||
|
||||
Query<CKeyParent> pq = Ebean.find(CKeyParent.class).alias("myt0").where().notIn("id.oneKey", sq).query();
|
||||
|
||||
pq.findList();
|
||||
|
||||
String sql = pq.getGeneratedSql();
|
||||
|
||||
// Without alias command is should be:
|
||||
// select t0.one_key c0, t0.two_key c1, t0.name c2, t0.version c3, t0.assoc_id c4 from ckey_parent t0 where (t0
|
||||
// .one_key) in (select t0.one_key from ckey_parent t0)
|
||||
// but with alias command SQL should look like this:
|
||||
// select myt0.one_key c0, myt0.two_key c1, myt0.name c2, myt0.version c3, myt0.assoc_id c4 from ckey_parent myt0
|
||||
// where (myt0.one_key) in (select st0.one_key from ckey_parent st0)
|
||||
|
||||
Assert.assertTrue(sql.contains("ckey_parent myt0"));
|
||||
Assert.assertTrue(sql.contains("(myt0.one_key) not in (select st0.one_key from ckey_parent st0)"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user