added supprt for Collection innerJoin in Querydsl Collections

This commit is contained in:
Timo Westkämper 2010-06-04 18:58:56 +00:00
parent 698249d7cb
commit 7ae855ff79
12 changed files with 206 additions and 20 deletions

View File

@ -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<Q extends AbstractColQuery<Q>> extends P
queryMixin.getMetadata().addJoin(JoinType.DEFAULT, entity.asExpr());
return (Q)this;
}
@SuppressWarnings("unchecked")
public <P> Q innerJoin(Path<? extends Collection<P>> target, Path<P> alias) {
queryMixin.getMetadata().addJoin(JoinType.INNERJOIN, createAlias(target, alias));
return (Q)this;
}
@SuppressWarnings("unchecked")
private <D> Expr<D> createAlias(Path<? extends Collection<D>> target, Path<D> alias){
return OSimple.create((Class<D>)alias.getType(), Ops.ALIAS, target.asExpr(), alias.asExpr());
}
protected ExprEvaluatorFactory getEvaluatorFactory() {
return evaluatorFactory;

View File

@ -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<ColQuery>, Projectable {
* @return
*/
<A> ColQuery from(Path<A> entity, Iterable<? extends A> col);
/**
* @param <P>
* @param target
* @param alias
* @return
*/
<P> ColQuery innerJoin(Path<? extends Collection<P>> target, Path<P> alias);
}

View File

@ -57,4 +57,6 @@ public class ColQueryImpl extends AbstractColQuery<ColQueryImpl> implements ColQ
return queryMixin.getMetadata();
}
}

View File

@ -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 <T> Evaluator<T> create(List<? extends Expr<?>> sources, final Expr<T> projection) {
@SuppressWarnings("unchecked")
public <T> Evaluator<List<T>> createEvaluator(Expr<? extends T> 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<Object,String> constantToLabel = ser.getConstantToLabel();
Map<String,Object> constants = new HashMap<String,Object>();
for (Map.Entry<Object,String> 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<List<Object[]>> createEvaluator(List<JoinExpression> joins, @Nullable EBoolean filter){
List<String> sourceNames = new ArrayList<String>();
List<Type> sourceTypes = new ArrayList<Type>();
List<Class> sourceClasses = new ArrayList<Class>();
StringBuilder vars = new StringBuilder();
ColQuerySerializer ser = new ColQuerySerializer(templates);
ser.append("java.util.List<Object[]> rv = new java.util.ArrayList<Object[]>();\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<Object,String> constantToLabel = ser.getConstantToLabel();
Map<String,Object> constants = new HashMap<String,Object>();
for (Map.Entry<Object,String> 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 <T> Evaluator<T> create(List<? extends Expr<?>> sources, Expr<T> projection) {
ColQuerySerializer serializer = new ColQuerySerializer(templates);
serializer.handle(projection);
Map<Object,String> constantToLabel = serializer.getConstantToLabel();
Map<String,Object> constants = new HashMap<String,Object>();
for (Map.Entry<Object,String> 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);
}

View File

@ -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<T> extends AbstractIterable<Object[],T>{
private final List<Expr<?>> sources = new ArrayList<Expr<?>>();
@SuppressWarnings("unchecked")
public MultiSourceIterable(QueryMetadata metadata,
ExprEvaluatorFactory evaluatorFactory,
IteratorFactory iteratorFactory,
@ -40,22 +42,27 @@ public class MultiSourceIterable<T> extends AbstractIterable<Object[],T>{
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<Object[]> initialIterator() {
List<Iterable<?>> iterableList = new ArrayList<Iterable<?>>(sources.size());
for (Expr<?> expr : sources){
iterableList.add(iterableMap.get(expr));
Evaluator ev = evaluatorFactory.createEvaluator(metadata.getJoins(), metadata.getWhere());
List<Iterable<?>> iterableList = new ArrayList<Iterable<?>>(metadata.getJoins().size());
for (JoinExpression join : metadata.getJoins()){
if (join.getType() == JoinType.DEFAULT){
iterableList.add(iterableMap.get(join.getTarget()));
}
}
Iterator<Object[]> 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")

View File

@ -44,12 +44,12 @@ public class SingleSourceIterable<I,T> extends AbstractIterable<I,T> {
@SuppressWarnings("unchecked")
@Override
protected Iterator<I> initialIterator() {
Iterator<I> it = iterable.iterator();
if (metadata.getWhere() != null) {
// where
it = iteratorFactory.singleArgFilter(it, (Expr<I>)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")

View File

@ -96,6 +96,7 @@ public class ColQueryStandardTest {
}
}
@SuppressWarnings("unchecked")
@Test
public void arrayProjection(){
List<String[]> results = MiniApi.from(cat, data).list(new EArrayConstructor<String>(String[].class, cat.name));

View File

@ -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<Cat> 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<Cat> 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());
}
}

View File

@ -33,7 +33,7 @@
<dependency>
<groupId>com.mysema.codegen</groupId>
<artifactId>codegen</artifactId>
<version>0.1.2</version>
<version>0.1.5</version>
</dependency>
<dependency>

View File

@ -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;

View File

@ -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 {
/**

View File

@ -37,7 +37,6 @@ public class HQLQueryMixin<T> extends QueryMixin<T> {
super(self, metadata);
}
@SuppressWarnings("unchecked")
private <D> Expr<D> createAlias(Path<? extends Collection<D>> target, Path<D> alias){
return OSimple.create((Class<D>)alias.getType(), Ops.ALIAS, target.asExpr(), alias.asExpr());