From 7ae855ff7994f5cd9c6f95e38b43e9e3682baa6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Westk=C3=A4mper?= Date: Fri, 4 Jun 2010 18:58:56 +0000 Subject: [PATCH] added supprt for Collection innerJoin in Querydsl Collections --- .../query/collections/AbstractColQuery.java | 14 +++ .../mysema/query/collections/ColQuery.java | 10 ++ .../query/collections/ColQueryImpl.java | 2 + .../collections/ExprEvaluatorFactory.java | 108 +++++++++++++++++- .../collections/MultiSourceIterable.java | 27 +++-- .../collections/SingleSourceIterable.java | 10 +- .../mysema/query/ColQueryStandardTest.java | 1 + .../query/collections/InnerJoinTest.java | 49 ++++++++ querydsl-core/pom.xml | 2 +- .../mysema/query/DefaultQueryMetadata.java | 1 + .../java/com/mysema/query/QueryMetadata.java | 1 + .../com/mysema/query/hql/HQLQueryMixin.java | 1 - 12 files changed, 206 insertions(+), 20 deletions(-) create mode 100644 querydsl-collections/src/test/java/com/mysema/query/collections/InnerJoinTest.java diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java b/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java index 4495bd814..e9b204d58 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/AbstractColQuery.java @@ -5,6 +5,7 @@ */ package com.mysema.query.collections; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -23,8 +24,10 @@ import com.mysema.query.SearchResults; import com.mysema.query.support.ProjectableQuery; import com.mysema.query.support.QueryMixin; import com.mysema.query.types.Expr; +import com.mysema.query.types.Ops; import com.mysema.query.types.Path; import com.mysema.query.types.expr.EArrayConstructor; +import com.mysema.query.types.expr.OSimple; /** * AbstractColQuery provides a base class for Collection query implementations. @@ -98,6 +101,17 @@ public abstract class AbstractColQuery> extends P queryMixin.getMetadata().addJoin(JoinType.DEFAULT, entity.asExpr()); return (Q)this; } + + @SuppressWarnings("unchecked") + public

Q innerJoin(Path> target, Path

alias) { + queryMixin.getMetadata().addJoin(JoinType.INNERJOIN, createAlias(target, alias)); + return (Q)this; + } + + @SuppressWarnings("unchecked") + private Expr createAlias(Path> target, Path alias){ + return OSimple.create((Class)alias.getType(), Ops.ALIAS, target.asExpr(), alias.asExpr()); + } protected ExprEvaluatorFactory getEvaluatorFactory() { return evaluatorFactory; diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/ColQuery.java b/querydsl-collections/src/main/java/com/mysema/query/collections/ColQuery.java index 953ced364..cfc50d53a 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/ColQuery.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/ColQuery.java @@ -5,6 +5,8 @@ */ package com.mysema.query.collections; +import java.util.Collection; + import com.mysema.query.Projectable; import com.mysema.query.Query; import com.mysema.query.types.Path; @@ -33,5 +35,13 @@ public interface ColQuery extends Query, Projectable { * @return */ ColQuery from(Path entity, Iterable col); + + /** + * @param

+ * @param target + * @param alias + * @return + */ +

ColQuery innerJoin(Path> target, Path

alias); } diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/ColQueryImpl.java b/querydsl-collections/src/main/java/com/mysema/query/collections/ColQueryImpl.java index 9586872c5..d22176117 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/ColQueryImpl.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/ColQueryImpl.java @@ -57,4 +57,6 @@ public class ColQueryImpl extends AbstractColQuery implements ColQ return queryMixin.getMetadata(); } + + } \ No newline at end of file diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/ExprEvaluatorFactory.java b/querydsl-collections/src/main/java/com/mysema/query/collections/ExprEvaluatorFactory.java index 5c28937d9..7f3cb30f1 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/ExprEvaluatorFactory.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/ExprEvaluatorFactory.java @@ -6,10 +6,12 @@ package com.mysema.query.collections; import java.net.URLClassLoader; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.Nullable; import javax.tools.JavaCompiler; import javax.tools.ToolProvider; @@ -19,8 +21,13 @@ import org.apache.commons.lang.ClassUtils; import com.mysema.codegen.Evaluator; import com.mysema.codegen.EvaluatorFactory; +import com.mysema.codegen.Type; +import com.mysema.query.JoinExpression; +import com.mysema.query.JoinType; import com.mysema.query.types.EConstructor; import com.mysema.query.types.Expr; +import com.mysema.query.types.Operation; +import com.mysema.query.types.expr.EBoolean; /** * @author tiwe @@ -55,15 +62,109 @@ public class ExprEvaluatorFactory { this.factory = new EvaluatorFactory(classLoader, compiler); } - public Evaluator create(List> sources, final Expr projection) { + @SuppressWarnings("unchecked") + public Evaluator> createEvaluator(Expr source, EBoolean filter){ + String typeName = com.mysema.codegen.ClassUtils.getName(source.getType()); + ColQuerySerializer ser = new ColQuerySerializer(templates); + ser.append("java.util.List<"+typeName+"> rv = new java.util.ArrayList<"+typeName+">();\n"); + ser.append("for (" + typeName + " "+ source + " : " + source + "_){\n"); + ser.append(" if (").handle(filter).append("){\n"); + ser.append(" rv.add("+source+");\n"); + ser.append(" }\n"); + ser.append("}\n"); + ser.append("return rv;"); + + Map constantToLabel = ser.getConstantToLabel(); + Map constants = new HashMap(); + for (Map.Entry entry : constantToLabel.entrySet()){ + constants.put(entry.getValue(), entry.getKey()); + } + + Type sourceType = new Type(source.getType()); + Type sourceListType = new Type(Iterable.class, sourceType); + + return factory.createEvaluator( + ser.toString(), + sourceListType, + new String[]{source+"_"}, + new Type[]{sourceListType}, + new Class[]{Iterable.class}, + constants); + } + + @SuppressWarnings("unchecked") + public Evaluator> createEvaluator(List joins, @Nullable EBoolean filter){ + List sourceNames = new ArrayList(); + List sourceTypes = new ArrayList(); + List sourceClasses = new ArrayList(); + StringBuilder vars = new StringBuilder(); + ColQuerySerializer ser = new ColQuerySerializer(templates); + ser.append("java.util.List rv = new java.util.ArrayList();\n"); + for (JoinExpression join : joins){ + Expr target = join.getTarget(); + String typeName = com.mysema.codegen.ClassUtils.getName(target.getType()); + if (vars.length() > 0){ + vars.append(","); + } + if (join.getType() == JoinType.DEFAULT){ + ser.append("for (" + typeName + " "+ target + " : " + target + "_){\n"); + vars.append(target); + sourceNames.add(target+"_"); + sourceTypes.add(new Type(Iterable.class, new Type(target.getType()))); + sourceClasses.add(Iterable.class); + + }else if (join.getType() == JoinType.INNERJOIN){ + Operation alias = (Operation)join.getTarget(); + // TODO : handle also Map inner joins + // TODO : handle join condition + ser.append("for ( " + typeName + " " + alias.getArg(1) + " : ").handle(alias.getArg(0)).append("){\n"); + vars.append(alias.getArg(1)); + + // TODO : left join + + }else{ + throw new IllegalArgumentException("Illegal join expression " + join); + } + } + + if (filter != null){ + ser.append("if (").handle(filter).append("){\n"); + ser.append(" rv.add(new Object[]{"+vars+"});\n"); + ser.append("}\n"); + }else{ + ser.append("rv.add(new Object[]{"+vars+"});\n"); + } + + for (int i = 0; i < joins.size(); i++){ + ser.append("}\n"); + } + ser.append("return rv;"); + + Map constantToLabel = ser.getConstantToLabel(); + Map constants = new HashMap(); + for (Map.Entry entry : constantToLabel.entrySet()){ + constants.put(entry.getValue(), entry.getKey()); + } + + Type projectionType = new Type(List.class, new Type(Object[].class)); + return factory.createEvaluator( + ser.toString(), + projectionType, + sourceNames.toArray(new String[sourceNames.size()]), + sourceTypes.toArray(new Type[sourceTypes.size()]), + sourceClasses.toArray(new Class[sourceClasses.size()]), + constants); + } + + public Evaluator create(List> sources, Expr projection) { ColQuerySerializer serializer = new ColQuerySerializer(templates); serializer.handle(projection); + Map constantToLabel = serializer.getConstantToLabel(); Map constants = new HashMap(); for (Map.Entry entry : constantToLabel.entrySet()){ constants.put(entry.getValue(), entry.getKey()); } - String javaSource = serializer.toString(); Class[] types = new Class[sources.size()]; String[] names = new String[sources.size()]; for (int i = 0; i < sources.size(); i++) { @@ -78,11 +179,12 @@ public class ExprEvaluatorFactory { } } + String javaSource = serializer.toString(); if (projection instanceof EConstructor){ javaSource = "("+com.mysema.codegen.ClassUtils.getName(projection.getType())+")(" + javaSource+")"; } - return factory.createEvaluator(javaSource, projection.getType(), names, types, constants); + return factory.createEvaluator("return " + javaSource +";", projection.getType(), names, types, constants); } diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/MultiSourceIterable.java b/querydsl-collections/src/main/java/com/mysema/query/collections/MultiSourceIterable.java index 6cfb61cdc..bb9f1e172 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/MultiSourceIterable.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/MultiSourceIterable.java @@ -15,12 +15,13 @@ import org.apache.commons.collections15.IteratorUtils; import com.mysema.codegen.Evaluator; import com.mysema.query.JoinExpression; +import com.mysema.query.JoinType; import com.mysema.query.QueryMetadata; import com.mysema.query.types.Expr; +import com.mysema.query.types.Operation; import com.mysema.query.types.Order; import com.mysema.query.types.OrderSpecifier; import com.mysema.query.types.expr.EArrayConstructor; -import com.mysema.util.MultiIterator; /** * @author tiwe @@ -32,6 +33,7 @@ public class MultiSourceIterable extends AbstractIterable{ private final List> sources = new ArrayList>(); + @SuppressWarnings("unchecked") public MultiSourceIterable(QueryMetadata metadata, ExprEvaluatorFactory evaluatorFactory, IteratorFactory iteratorFactory, @@ -40,22 +42,27 @@ public class MultiSourceIterable extends AbstractIterable{ super(metadata, evaluatorFactory, iteratorFactory, forCount); this.iterableMap = iterables; for (JoinExpression join : metadata.getJoins()){ - sources.add(join.getTarget()); + if (join.getType() == JoinType.DEFAULT){ + sources.add(join.getTarget()); + }else{ + Operation target = (Operation) join.getTarget(); + sources.add(target.getArg(1)); + } + } } @SuppressWarnings("unchecked") @Override protected Iterator initialIterator() { - List> iterableList = new ArrayList>(sources.size()); - for (Expr expr : sources){ - iterableList.add(iterableMap.get(expr)); + Evaluator ev = evaluatorFactory.createEvaluator(metadata.getJoins(), metadata.getWhere()); + List> iterableList = new ArrayList>(metadata.getJoins().size()); + for (JoinExpression join : metadata.getJoins()){ + if (join.getType() == JoinType.DEFAULT){ + iterableList.add(iterableMap.get(join.getTarget())); + } } - Iterator it = new MultiIterator(iterableList); - if (metadata.getWhere() != null) { - it = iteratorFactory.multiArgFilter(it, sources, metadata.getWhere()); - } - return it; + return ((Iterable)ev.evaluate(iterableList.toArray())).iterator(); } @SuppressWarnings("unchecked") diff --git a/querydsl-collections/src/main/java/com/mysema/query/collections/SingleSourceIterable.java b/querydsl-collections/src/main/java/com/mysema/query/collections/SingleSourceIterable.java index f28b02fb5..ad4b50da8 100644 --- a/querydsl-collections/src/main/java/com/mysema/query/collections/SingleSourceIterable.java +++ b/querydsl-collections/src/main/java/com/mysema/query/collections/SingleSourceIterable.java @@ -44,12 +44,12 @@ public class SingleSourceIterable extends AbstractIterable { @SuppressWarnings("unchecked") @Override protected Iterator initialIterator() { - Iterator it = iterable.iterator(); - if (metadata.getWhere() != null) { - // where - it = iteratorFactory.singleArgFilter(it, (Expr)sources.get(0), metadata.getWhere()); + if (metadata.getWhere() != null){ + Evaluator ev = evaluatorFactory.createEvaluator(sources.get(0), metadata.getWhere()); + return ((Iterable)ev.evaluate(iterable)).iterator(); + }else{ + return iterable.iterator(); } - return it; } @SuppressWarnings("unchecked") diff --git a/querydsl-collections/src/test/java/com/mysema/query/ColQueryStandardTest.java b/querydsl-collections/src/test/java/com/mysema/query/ColQueryStandardTest.java index cf2a35f81..798b15c0b 100644 --- a/querydsl-collections/src/test/java/com/mysema/query/ColQueryStandardTest.java +++ b/querydsl-collections/src/test/java/com/mysema/query/ColQueryStandardTest.java @@ -96,6 +96,7 @@ public class ColQueryStandardTest { } } + @SuppressWarnings("unchecked") @Test public void arrayProjection(){ List results = MiniApi.from(cat, data).list(new EArrayConstructor(String[].class, cat.name)); diff --git a/querydsl-collections/src/test/java/com/mysema/query/collections/InnerJoinTest.java b/querydsl-collections/src/test/java/com/mysema/query/collections/InnerJoinTest.java new file mode 100644 index 000000000..40ae0cbd5 --- /dev/null +++ b/querydsl-collections/src/test/java/com/mysema/query/collections/InnerJoinTest.java @@ -0,0 +1,49 @@ +package com.mysema.query.collections; + +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; + +import com.mysema.query.animal.Cat; +import com.mysema.query.animal.QCat; + +public class InnerJoinTest extends AbstractQueryTest{ + + private QCat cat, kitten; + + private List cats; + + @Before + public void setUp(){ + super.setUp(); + cat = new QCat("c"); + kitten = new QCat("k"); + Cat bob = new Cat("Bob"); + Cat bob2 = new Cat("Bob"); + Cat kate = new Cat("Kate"); + Cat kate2 = new Cat("Kate"); + Cat franz = new Cat("Franz"); + + bob.setKittens(Collections.singletonList(bob2)); + kate.setKittens(Collections.singletonList(kate2)); + cats = Arrays.asList(bob, bob2, kate, kate2, franz); + } + + @Test + public void testList(){ + List rv = MiniApi.from(cat, cats) + .innerJoin(cat.kittens, kitten) + .where(cat.name.eq(kitten.name)) + .orderBy(cat.name.asc()) + .list(cat); + assertEquals("Bob", rv.get(0).getName()); + assertEquals("Kate", rv.get(1).getName()); + + } + +} diff --git a/querydsl-core/pom.xml b/querydsl-core/pom.xml index 5c24370c4..52e2de247 100644 --- a/querydsl-core/pom.xml +++ b/querydsl-core/pom.xml @@ -33,7 +33,7 @@ com.mysema.codegen codegen - 0.1.2 + 0.1.5 diff --git a/querydsl-core/src/main/java/com/mysema/query/DefaultQueryMetadata.java b/querydsl-core/src/main/java/com/mysema/query/DefaultQueryMetadata.java index a52bba57c..f94f200c1 100644 --- a/querydsl-core/src/main/java/com/mysema/query/DefaultQueryMetadata.java +++ b/querydsl-core/src/main/java/com/mysema/query/DefaultQueryMetadata.java @@ -26,6 +26,7 @@ import edu.umd.cs.findbugs.annotations.SuppressWarnings; * * @author tiwe */ +//TODO : rename to DefaultQueryModel in Querydsl 2.0 public class DefaultQueryMetadata implements QueryMetadata, Cloneable { private static final long serialVersionUID = 317736313966701232L; diff --git a/querydsl-core/src/main/java/com/mysema/query/QueryMetadata.java b/querydsl-core/src/main/java/com/mysema/query/QueryMetadata.java index fe7ceddf1..8c4c10991 100644 --- a/querydsl-core/src/main/java/com/mysema/query/QueryMetadata.java +++ b/querydsl-core/src/main/java/com/mysema/query/QueryMetadata.java @@ -20,6 +20,7 @@ import com.mysema.query.types.expr.EBoolean; * * @author tiwe */ +// TODO : rename to QueryModel in Querydsl 2.0 public interface QueryMetadata extends Serializable { /** diff --git a/querydsl-hql/src/main/java/com/mysema/query/hql/HQLQueryMixin.java b/querydsl-hql/src/main/java/com/mysema/query/hql/HQLQueryMixin.java index 5fe8d981a..41dd15d75 100644 --- a/querydsl-hql/src/main/java/com/mysema/query/hql/HQLQueryMixin.java +++ b/querydsl-hql/src/main/java/com/mysema/query/hql/HQLQueryMixin.java @@ -37,7 +37,6 @@ public class HQLQueryMixin extends QueryMixin { super(self, metadata); } - @SuppressWarnings("unchecked") private Expr createAlias(Path> target, Path alias){ return OSimple.create((Class)alias.getType(), Ops.ALIAS, target.asExpr(), alias.asExpr());