mirror of
https://github.com/querydsl/querydsl.git
synced 2026-06-27 21:01:15 +08:00
added supprt for Collection innerJoin in Querydsl Collections
This commit is contained in:
parent
698249d7cb
commit
7ae855ff79
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
}
|
||||
|
||||
@ -57,4 +57,6 @@ public class ColQueryImpl extends AbstractColQuery<ColQueryImpl> implements ColQ
|
||||
return queryMixin.getMetadata();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
@ -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());
|
||||
|
||||
Loading…
Reference in New Issue
Block a user