Use deterministic path creation

This commit is contained in:
Timo Westkämper 2015-03-07 12:18:30 +02:00
parent fe4bffff81
commit b958a2aa9c
12 changed files with 75 additions and 83 deletions

View File

@ -48,7 +48,7 @@ public class CollQueryMixin<T> extends QueryMixin<T> {
predicate = (Predicate)ExpressionUtils.extract(predicate);
if (predicate != null) {
Context context = new Context();
Predicate transformed = (Predicate) predicate.accept(CollectionAnyVisitor.DEFAULT, context);
Predicate transformed = (Predicate) predicate.accept(collectionAnyVisitor, context);
for (int i = 0; i < context.paths.size(); i++) {
leftJoin(
(Path)context.paths.get(i).getMetadata().getParent(),

View File

@ -14,10 +14,7 @@
package com.querydsl.collections;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import javax.annotation.Nullable;
import javax.tools.JavaCompiler;
@ -57,6 +54,8 @@ public class DefaultEvaluatorFactory {
private final CollQueryTemplates templates;
private final CollectionAnyVisitor collectionAnyVisitor = new CollectionAnyVisitor();
public DefaultEvaluatorFactory(CollQueryTemplates templates) {
this(templates,
Thread.currentThread().getContextClassLoader());
@ -215,7 +214,7 @@ public class DefaultEvaluatorFactory {
if (colAnyJoin) {
Context context = new Context();
Expression<?> replacement = alias.getArg(0)
.accept(CollectionAnyVisitor.DEFAULT, context);
.accept(collectionAnyVisitor, context);
ser.handle(replacement);
} else {
ser.handle(alias.getArg(0));

View File

@ -31,7 +31,7 @@ import com.querydsl.core.types.template.BooleanTemplate;
@SuppressWarnings("unchecked")
public class CollectionAnyVisitor implements Visitor<Expression<?>,Context> {
public static final CollectionAnyVisitor DEFAULT = new CollectionAnyVisitor();
private int replacedCounter;
@SuppressWarnings("rawtypes")
private static <T> Path<T> replaceParent(Path<T> path, Path<?> parent) {
@ -110,7 +110,7 @@ public class CollectionAnyVisitor implements Visitor<Expression<?>,Context> {
Path<?> parent = (Path<?>) expr.getMetadata().getParent().accept(this, context);
expr = new PathImpl<Object>(expr.getType(), PathMetadataFactory.forCollectionAny(parent));
EntityPath<?> replacement = new EntityPathBase<Object>(expr.getType(),
ExpressionUtils.createRootVariable(expr));
ExpressionUtils.createRootVariable(expr, replacedCounter++));
context.add(expr, replacement);
return replacement;
@ -135,6 +135,4 @@ public class CollectionAnyVisitor implements Visitor<Expression<?>,Context> {
return expr;
}
protected CollectionAnyVisitor() {}
}

View File

@ -46,6 +46,8 @@ public class QueryMixin<T> {
}
};
protected final CollectionAnyVisitor collectionAnyVisitor = new CollectionAnyVisitor();
private T self;
public QueryMixin() {
@ -123,7 +125,7 @@ public class QueryMixin<T> {
private Path<?> normalizePath(Path<?> expr) {
Context context = new Context();
Path<?> replaced = (Path<?>)expr.accept(CollectionAnyVisitor.DEFAULT, context);
Path<?> replaced = (Path<?>)expr.accept(collectionAnyVisitor, context);
if (!replaced.equals(expr)) {
for (int i = 0; i < context.paths.size(); i++) {
Path path = context.paths.get(i).getMetadata().getParent();

View File

@ -13,10 +13,10 @@
*/
package com.querydsl.core.types;
import javax.annotation.Nullable;
import java.util.*;
import javax.annotation.Nullable;
import com.google.common.collect.ImmutableList;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.QueryException;
@ -533,14 +533,14 @@ public final class ExpressionUtils {
}
/**
* Create a new root variable based on the given path
* Create a new root variable based on the given path and suffix
*
* @param path
* @param suffix
* @return
*/
public static String createRootVariable(Path<?> path) {
String variable = path.accept(ToStringVisitor.DEFAULT, TEMPLATES).replace('.', '_');
String suffix = UUID.randomUUID().toString().replace("-", "").substring(0, 5);
public static String createRootVariable(Path<?> path, int suffix) {
String variable = path.accept(ToStringVisitor.DEFAULT, TEMPLATES);
return variable + "_" + suffix;
}

View File

@ -30,56 +30,58 @@ public class CollectionAnyVisitorTest {
@Test
public void Path() {
assertMatches("cat_kittens.*", serialize(cat.kittens.any()));
assertEquals("cat_kittens_0", serialize(cat.kittens.any()));
}
@Test
public void Longer_Path() {
assertMatches("cat_kittens.*\\.name", serialize(cat.kittens.any().name));
assertEquals("cat_kittens_0.name", serialize(cat.kittens.any().name));
}
@Test
public void Longer_Path2() {
CollectionAnyVisitor visitor = new CollectionAnyVisitor();
assertEquals("cat_kittens_0.name", serialize(cat.kittens.any().name, visitor));
assertEquals("cat_kittens_1.name", serialize(cat.kittens.any().name, visitor));
}
@Test
public void Very_Long_Path() {
assertMatches("cat_kittens.*_kittens.*\\.name", serialize(cat.kittens.any().kittens.any().name));
assertEquals("cat_kittens_0_kittens_1.name", serialize(cat.kittens.any().kittens.any().name));
}
@Test
public void Simple_BooleanOperation() {
Predicate predicate = cat.kittens.any().name.eq("Ruth123");
assertMatches("cat_kittens.*\\.name = Ruth123", serialize(predicate));
Predicate predicate = cat.kittens.any().name.eq("Ruth123");
assertEquals("cat_kittens_0.name = Ruth123", serialize(predicate));
}
@Test
public void Simple_StringOperation() {
Predicate predicate = cat.kittens.any().name.substring(1).eq("uth123");
assertMatches("substring\\(cat_kittens.*\\.name,1\\) = uth123", serialize(predicate));
Predicate predicate = cat.kittens.any().name.substring(1).eq("uth123");
assertEquals("substring(cat_kittens_0.name,1) = uth123", serialize(predicate));
}
@Test
public void And_Operation() {
Predicate predicate = cat.kittens.any().name.eq("Ruth123").and(cat.kittens.any().bodyWeight.gt(10.0));
assertMatches("cat_kittens.*\\.name = Ruth123 && cat_kittens.*\\.bodyWeight > 10.0", serialize(predicate));
assertEquals("cat_kittens_0.name = Ruth123 && cat_kittens_1.bodyWeight > 10.0", serialize(predicate));
}
@Test
public void Template() {
Expression<Boolean> templateExpr = TemplateExpressionImpl.create(Boolean.class, "{0} = {1}",
cat.kittens.any().name, ConstantImpl.create("Ruth123"));
assertMatches("cat_kittens.*\\.name = Ruth123", serialize(templateExpr));
assertEquals("cat_kittens_0.name = Ruth123", serialize(templateExpr));
}
private String serialize(Expression<?> expression) {
CollectionAnyVisitor visitor = new CollectionAnyVisitor() {
@Override
protected Predicate exists(Context c, Predicate condition) {
return condition;
}
};
return serialize(expression, new CollectionAnyVisitor());
}
private String serialize(Expression<?> expression, CollectionAnyVisitor visitor) {
Expression<?> transformed = expression.accept(visitor, new Context());
return transformed.toString();
}
private static void assertMatches(String str1, String str2) {
assertTrue(str2, str2.matches(str1));
}
}

View File

@ -14,15 +14,9 @@
package com.querydsl.jdo;
import com.querydsl.core.QueryMetadata;
import com.querydsl.core.support.CollectionAnyVisitor;
import com.querydsl.core.support.Context;
import com.querydsl.core.support.QueryMixin;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Ops;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.PredicateOperation;
import com.querydsl.core.types.*;
/**
* JDOQueryMixin extends {@link QueryMixin} to provide module specific extensions
@ -48,7 +42,7 @@ public class JDOQueryMixin<T> extends QueryMixin<T> {
predicate = (Predicate)ExpressionUtils.extract(predicate);
if (predicate != null) {
Context context = new Context();
Predicate transformed = (Predicate) predicate.accept(CollectionAnyVisitor.DEFAULT, context);
Predicate transformed = (Predicate) predicate.accept(collectionAnyVisitor, context);
for (int i = 0; i < context.paths.size(); i++) {
Path<?> path = context.paths.get(i);
addCondition(context, i, path, role);

View File

@ -31,8 +31,6 @@ import com.querydsl.core.types.path.SimplePath;
*/
public final class JPACollectionAnyVisitor extends CollectionAnyVisitor {
public static final JPACollectionAnyVisitor DEFAULT = new JPACollectionAnyVisitor();
@SuppressWarnings("unchecked")
@Override
protected Predicate exists(Context c, Predicate condition) {
@ -46,7 +44,7 @@ public final class JPACollectionAnyVisitor extends CollectionAnyVisitor {
// join via parent
Path<?> parent = child.getMetadata().getParent();
EntityPathBase<Object> newParent = new EntityPathBase<Object>(parent.getType(),
ExpressionUtils.createRootVariable(parent));
ExpressionUtils.createRootVariable(parent, Math.abs(condition.hashCode())));
EntityPath<Object> newChild = new EntityPathBase<Object>(child.getType(),
PathMetadataFactory.forProperty(newParent, child.getMetadata().getName()));
query.from(newParent).innerJoin(newChild, replacement);
@ -58,6 +56,4 @@ public final class JPACollectionAnyVisitor extends CollectionAnyVisitor {
return query.exists();
}
private JPACollectionAnyVisitor() {}
}

View File

@ -31,7 +31,7 @@ class JPAListAccessVisitor extends ReplaceVisitor<Void> {
// join parent as path123 on index(path123) = ...
Path parent = pathMetadata.getParent();
replacement = new PathImpl(expr.getType(),
ExpressionUtils.createRootVariable(parent));
ExpressionUtils.createRootVariable(parent, replacements.size()));
metadata.addJoin(JoinType.LEFTJOIN, ExpressionUtils.as(parent, replacement));
metadata.addJoinCondition(ExpressionUtils.eq(
(Expression)Expressions.operation(Integer.class, JPQLOps.INDEX, replacement),

View File

@ -27,7 +27,7 @@ class JPAMapAccessVisitor extends ReplaceVisitor<Void> {
ParameterizedExpression map = (ParameterizedExpression<?>) expr.getArg(0);
Expression key = expr.getArg(1);
Path replacement = new PathImpl<Object>(map.getParameter(1),
ExpressionUtils.createRootVariable((Path<?>)map));
ExpressionUtils.createRootVariable((Path<?>)map, Math.abs(expr.hashCode())));
metadata.addJoin(JoinType.LEFTJOIN, ExpressionUtils.as(map, replacement));
metadata.addJoinCondition(ExpressionUtils.eq(
Expressions.operation(map.getParameter(0), JPQLOps.KEY, replacement),
@ -54,7 +54,7 @@ class JPAMapAccessVisitor extends ReplaceVisitor<Void> {
Path parent = pathMetadata.getParent();
ParameterizedExpression parExpr = (ParameterizedExpression) parent;
replacement = new PathImpl(parExpr.getParameter(1),
ExpressionUtils.createRootVariable(parent));
ExpressionUtils.createRootVariable(parent, replacements.size()));
metadata.addJoin(JoinType.LEFTJOIN, ExpressionUtils.as(parent, replacement));
metadata.addJoinCondition(ExpressionUtils.eq(
Expressions.operation(parExpr.getParameter(0), JPQLOps.KEY, replacement),

View File

@ -49,6 +49,8 @@ public class JPAQueryMixin<T> extends QueryMixin<T> {
private final JPAListAccessVisitor listAccessVisitor;
private final JPACollectionAnyVisitor collectionAnyVisitor;
private final ReplaceVisitor<Void> replaceVisitor = new ReplaceVisitor<Void>() {
public Expression<?> visit(Path<?> expr, Void context) {
return convertPathForOrder(expr);
@ -67,18 +69,21 @@ public class JPAQueryMixin<T> extends QueryMixin<T> {
public JPAQueryMixin() {
mapAccessVisitor = new JPAMapAccessVisitor(getMetadata());
listAccessVisitor = new JPAListAccessVisitor(getMetadata());
collectionAnyVisitor = new JPACollectionAnyVisitor();
}
public JPAQueryMixin(QueryMetadata metadata) {
super(metadata);
mapAccessVisitor = new JPAMapAccessVisitor(metadata);
listAccessVisitor = new JPAListAccessVisitor(metadata);
collectionAnyVisitor = new JPACollectionAnyVisitor();
}
public JPAQueryMixin(T self, QueryMetadata metadata) {
super(self, metadata);
mapAccessVisitor = new JPAMapAccessVisitor(metadata);
listAccessVisitor = new JPAListAccessVisitor(metadata);
collectionAnyVisitor = new JPACollectionAnyVisitor();
}
public T fetch() {
@ -196,7 +201,7 @@ public class JPAQueryMixin<T> extends QueryMixin<T> {
}
if (predicate != null) {
// transform any usage
predicate = (Predicate) predicate.accept(JPACollectionAnyVisitor.DEFAULT, new Context());
predicate = (Predicate) predicate.accept(collectionAnyVisitor, new Context());
return predicate;
} else {
return null;

View File

@ -13,7 +13,7 @@
*/
package com.querydsl.jpa;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
@ -22,7 +22,6 @@ import com.querydsl.core.types.ConstantImpl;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.TemplateExpressionImpl;
import com.querydsl.jpa.domain.JobFunction;
import com.querydsl.jpa.domain.QCat;
import com.querydsl.jpa.domain.QDomesticCat;
import com.querydsl.jpa.domain.QEmployee;
@ -34,57 +33,57 @@ public class JPACollectionAnyVisitorTest {
@Test
public void Path() {
assertMatches("cat_kittens.*", serialize(cat.kittens.any()));
assertEquals("cat_kittens_0", serialize(cat.kittens.any()));
}
@Test
public void Longer_Path() {
assertMatches("cat_kittens.*\\.name", serialize(cat.kittens.any().name));
assertEquals("cat_kittens_0.name", serialize(cat.kittens.any().name));
}
@Test
public void Simple_BooleanOperation() {
Predicate predicate = cat.kittens.any().name.eq("Ruth123");
assertMatches("exists \\(select 1\n" +
"from cat.kittens as cat_kittens.*\n" +
"where cat_kittens.*\\.name = \\?1\\)", serialize(predicate));
assertEquals("exists (select 1\n" +
"from cat.kittens as cat_kittens_0\n" +
"where cat_kittens_0.name = ?1)", serialize(predicate));
}
@Test
public void Simple_BooleanOperation_ElementCollection() {
QEmployee employee = QEmployee.employee;
Predicate predicate = employee.jobFunctions.any().eq(JobFunction.CODER);
assertMatches("exists \\(select 1\n" +
"from Employee employee.*\n" +
" inner join employee.*.jobFunctions as employee_jobFunctions.*\n" +
"where employee.* = employee and employee_jobFunctions.* = \\?1\\)", serialize(predicate));
Predicate predicate = employee.jobFunctions.any().stringValue().eq("CODER");
assertEquals("exists (select 1\n" +
"from Employee employee_1463394548\n" +
" inner join employee_1463394548.jobFunctions as employee_jobFunctions_0\n" +
"where employee_1463394548 = employee and str(employee_jobFunctions_0) = ?1)", serialize(predicate));
}
@Test
public void Simple_StringOperation() {
Predicate predicate = cat.kittens.any().name.substring(1).eq("uth123");
assertMatches("exists \\(select 1\n"+
"from cat.kittens as cat_kittens.*\n" +
"where substring\\(cat_kittens.*\\.name,2\\) = \\?1\\)", serialize(predicate));
assertEquals("exists (select 1\n" +
"from cat.kittens as cat_kittens_0\n" +
"where substring(cat_kittens_0.name,2) = ?1)", serialize(predicate));
}
@Test
public void And_Operation() {
Predicate predicate = cat.kittens.any().name.eq("Ruth123").and(cat.kittens.any().bodyWeight.gt(10.0));
assertMatches("exists \\(select 1\n"+
"from cat.kittens as cat_kittens.*\n" +
"where cat_kittens.*\\.name = \\?1\\) and exists \\(select 1\n" +
"from cat.kittens as cat_kittens.*\n" +
"where cat_kittens.*\\.bodyWeight > \\?2\\)", serialize(predicate));
assertEquals("exists (select 1\n" +
"from cat.kittens as cat_kittens_0\n" +
"where cat_kittens_0.name = ?1) and exists (select 1\n" +
"from cat.kittens as cat_kittens_1\n" +
"where cat_kittens_1.bodyWeight > ?2)", serialize(predicate));
}
@Test
public void Template() {
Expression<Boolean> templateExpr = TemplateExpressionImpl.create(Boolean.class, "{0} = {1}",
cat.kittens.any().name, ConstantImpl.create("Ruth123"));
assertMatches("exists \\(select 1\n" +
"from cat.kittens as cat_kittens.*\n" +
"where cat_kittens.*\\.name = \\?1\\)", serialize(templateExpr));
assertEquals("exists (select 1\n" +
"from cat.kittens as cat_kittens_0\n" +
"where cat_kittens_0.name = ?1)", serialize(templateExpr));
}
@Test
@ -97,19 +96,16 @@ public class JPACollectionAnyVisitorTest {
QDomesticCat anyCat = QCat.cat.kittens.any().as(QDomesticCat.class);
Predicate predicate = anyCat.name.eq("X");
assertMatches("exists \\(select 1\n" +
"from cat.kittens as cat_kittens.*\n" +
"where cat_kittens.*\\.name = \\?1\\)", serialize(predicate));
assertEquals("exists (select 1\n" +
"from cat.kittens as cat_kittens_0\n" +
"where cat_kittens_0.name = ?1)", serialize(predicate));
}
private String serialize(Expression<?> expression) {
Expression<?> transformed = expression.accept(JPACollectionAnyVisitor.DEFAULT, new Context());
Expression<?> transformed = expression.accept(new JPACollectionAnyVisitor(), new Context());
JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT, null);
serializer.handle(transformed);
return serializer.toString();
}
private static void assertMatches(String str1, String str2) {
assertTrue(str2, str2.matches(str1));
}
}