Partition lists when needed

This commit is contained in:
Timo Westkämper 2014-11-01 12:53:33 +02:00
parent a2c7b2dcb0
commit a8034ab226
6 changed files with 118 additions and 13 deletions

View File

@ -13,13 +13,12 @@
*/
package com.mysema.query.types;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.mysema.query.QueryException;
@ -389,6 +388,34 @@ public final class ExpressionUtils {
return PredicateOperation.create(Ops.NE, left, right);
}
/**
* Create an left not in right expression
*
* @param <D>
* @param left
* @param right
* @return
*/
public static <D> Predicate notIn(Expression<D> left, CollectionExpression<?,? extends D> right) {
return PredicateOperation.create(Ops.NOT_IN, left, right);
}
/**
* Create an left not in right expression
*
* @param <D>
* @param left
* @param right
* @return
*/
public static <D> Predicate notIn(Expression<D> left, Collection<? extends D> right) {
if (right.size() == 1) {
return neConst(left, right.iterator().next());
} else {
return PredicateOperation.create(Ops.NOT_IN, left, ConstantImpl.create(right));
}
}
/**
* Create a left or right expression
*

View File

@ -68,6 +68,7 @@ public class OracleTemplates extends SQLTemplates {
setBatchCountViaGetUpdateCount(true);
setWithRecursive("with ");
setCountViaAnalytics(true);
setListMaxSize(1000);
add(Ops.ALIAS, "{0} {1}");
add(SQLOps.NEXTVAL, "{0s}.nextval");

View File

@ -18,13 +18,11 @@ import java.util.*;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.mysema.commons.lang.Pair;
import com.mysema.query.JoinExpression;
import com.mysema.query.JoinFlag;
import com.mysema.query.QueryFlag;
import com.mysema.query.*;
import com.mysema.query.QueryFlag.Position;
import com.mysema.query.QueryMetadata;
import com.mysema.query.sql.types.Null;
import com.mysema.query.support.Expressions;
import com.mysema.query.support.SerializerBase;
@ -828,6 +826,7 @@ public class SQLSerializer extends SerializerBase<SQLSerializer> {
@Override
protected void visitOperation(Class<?> type, Operator<?> operator, List<? extends Expression<?>> args) {
boolean pathAdded = false;
if (args.size() == 2
&& !useLiterals
&& args.get(0) instanceof Path<?>
@ -838,6 +837,7 @@ public class SQLSerializer extends SerializerBase<SQLSerializer> {
for (Element element : templates.getTemplate(operator).getElements()) {
if (element instanceof Template.ByIndex && ((Template.ByIndex)element).getIndex() == 1) {
constantPaths.add((Path<?>)args.get(0));
pathAdded = true;
break;
}
}
@ -877,10 +877,29 @@ public class SQLSerializer extends SerializerBase<SQLSerializer> {
} else if ((operator == Ops.IN || operator == Ops.NOT_IN)
&& args.get(0) instanceof Path
&& args.get(1) instanceof Constant
&& ((Constant<Collection>)args.get(1)).getConstant().isEmpty()) {
super.visitOperation(type, operator == Ops.IN ? Ops.EQ : Ops.NE,
ImmutableList.of(NumberTemplate.ONE, NumberTemplate.TWO));
&& args.get(1) instanceof Constant) {
Collection<Object> coll = ((Constant<Collection>)args.get(1)).getConstant();
if (coll.isEmpty()) {
super.visitOperation(type, operator == Ops.IN ? Ops.EQ : Ops.NE,
ImmutableList.of(NumberTemplate.ONE, NumberTemplate.TWO));
} else {
if (templates.getListMaxSize() == 0 || coll.size() <= templates.getListMaxSize()) {
super.visitOperation(type, operator, args);
} else {
if (!constantPaths.isEmpty()) {
constantPaths.removeLast();
}
BooleanBuilder b = new BooleanBuilder();
for (List part : Iterables.partition(coll, templates.getListMaxSize())) {
if (operator == Ops.IN) {
b.or(ExpressionUtils.in(args.get(0), part));
} else {
b.and(ExpressionUtils.notIn(args.get(0), part));
}
}
b.getValue().accept(this, null);
}
}
} else if (operator == SQLOps.WITH_COLUMNS) {
boolean oldSkipParent = skipParent;

View File

@ -13,11 +13,12 @@
*/
package com.mysema.query.sql;
import static com.google.common.base.CharMatcher.inRange;
import java.lang.reflect.Field;
import java.sql.Types;
import java.util.*;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableSet;
@ -31,6 +32,7 @@ import com.mysema.query.QueryMetadata;
import com.mysema.query.QueryModifiers;
import com.mysema.query.sql.types.Type;
import com.mysema.query.types.*;
import static com.google.common.base.CharMatcher.inRange;
/**
* SQLTemplates extends Templates to provides SQL specific extensions
@ -273,6 +275,8 @@ public class SQLTemplates extends Templates {
private boolean arraysSupported = true;
private int listMaxSize = 0;
@Deprecated
protected SQLTemplates(String quoteStr, char escape, boolean useQuotes) {
this(SQL_RESERVED_WORDS, quoteStr, escape, useQuotes);
@ -787,6 +791,10 @@ public class SQLTemplates extends Templates {
return arraysSupported;
}
public int getListMaxSize() {
return listMaxSize;
}
protected void newLineToSingleSpace() {
for (Class<?> cl : Arrays.<Class<?>>asList(getClass(), SQLTemplates.class)) {
for (Field field : cl.getDeclaredFields()) {
@ -1147,4 +1155,7 @@ public class SQLTemplates extends Templates {
this.arraysSupported = b;
}
protected void setListMaxSize(int i ) {
listMaxSize = i;
}
}

View File

@ -623,6 +623,28 @@ public class SelectBase extends AbstractBaseTest {
query().from(employee).where(employee.id.in(Arrays.asList(1,2))).list(employee);
}
@Test
@ExcludeIn(SQLITE)
public void In_Long_List() {
List<Integer> ids = Lists.newArrayList();
for (int i = 0; i < 20000; i++) {
ids.add(i);
}
assertEquals(
query().from(employee).count(),
query().from(employee).where(employee.id.in(ids)).count());
}
@Test
@ExcludeIn(SQLITE)
public void NotIn_Long_List() {
List<Integer> ids = Lists.newArrayList();
for (int i = 0; i < 20000; i++) {
ids.add(i);
}
assertEquals(0, query().from(employee).where(employee.id.notIn(ids)).count());
}
@Test
public void In_Empty() {
assertEquals(0, query().from(employee).where(employee.id.in(ImmutableList.<Integer>of())).count());

View File

@ -27,6 +27,7 @@ import com.mysema.query.sql.domain.QEmployeeNoPK;
import com.mysema.query.sql.domain.QSurvey;
import com.mysema.query.support.Expressions;
import com.mysema.query.types.Expression;
import com.mysema.query.types.ExpressionUtils;
import com.mysema.query.types.Path;
import com.mysema.query.types.SubQueryExpression;
import com.mysema.query.types.expr.Wildcard;
@ -110,6 +111,30 @@ public class SQLSerializerTest {
"where \"user\".id = ?)", serializer.toString());
}
@Test
public void In() {
StringPath path = Expressions.stringPath("str");
Expression<?> expr = ExpressionUtils.in(path, Arrays.asList("1", "2", "3"));
SQLSerializer serializer = new SQLSerializer(Configuration.DEFAULT);
serializer.handle(expr);
assertEquals(Arrays.asList(path, path, path), serializer.getConstantPaths());
assertEquals(3, serializer.getConstants().size());
}
@Test
public void Or_In() {
StringPath path = Expressions.stringPath("str");
Expression<?> expr = ExpressionUtils.anyOf(
ExpressionUtils.in(path, Arrays.asList("1", "2", "3")),
ExpressionUtils.in(path, Arrays.asList("4", "5", "6")));
SQLSerializer serializer = new SQLSerializer(Configuration.DEFAULT);
serializer.handle(expr);
assertEquals(Arrays.asList(path, path, path, path, path, path), serializer.getConstantPaths());
assertEquals(6, serializer.getConstants().size());
}
@Test
public void Some() {
//select some((e.FIRSTNAME is not null)) from EMPLOYEE