diff --git a/querydsl-core/src/main/java/com/mysema/query/support/DetachableMixin.java b/querydsl-core/src/main/java/com/mysema/query/support/DetachableMixin.java index 51e4ff7806..f3f7bc86cb 100644 --- a/querydsl-core/src/main/java/com/mysema/query/support/DetachableMixin.java +++ b/querydsl-core/src/main/java/com/mysema/query/support/DetachableMixin.java @@ -121,7 +121,7 @@ public BooleanExpression notExists() { private QueryMetadata projection(Expression... projection) { QueryMetadata metadata = queryMixin.getMetadata().clone(); for (Expression expr : projection) { - expr = queryMixin.convert(expr); + expr = queryMixin.convert(expr, false); metadata.addProjection(nullAsTemplate(expr)); } return metadata; diff --git a/querydsl-core/src/main/java/com/mysema/query/support/QueryMixin.java b/querydsl-core/src/main/java/com/mysema/query/support/QueryMixin.java index 27deb7a84f..7c26f9c5da 100644 --- a/querydsl-core/src/main/java/com/mysema/query/support/QueryMixin.java +++ b/querydsl-core/src/main/java/com/mysema/query/support/QueryMixin.java @@ -95,14 +95,14 @@ public T addJoinFlag(JoinFlag flag) { } public Expression addProjection(Expression e) { - e = convert(e); + e = convert(e, false); metadata.addProjection(e); return e; } public T addProjection(Expression... o) { for (Expression e : o) { - metadata.addProjection(convert(e)); + metadata.addProjection(convert(e, false)); } return self; } @@ -115,7 +115,7 @@ private

> P assertRoot(P p) { } @SuppressWarnings("rawtypes") - public Expression convert(Expression expr) { + public Expression convert(Expression expr, boolean forOrder) { if (validateAnyPaths && expr instanceof Path) { Context context = new Context(); Expression replaced = expr.accept(CollectionAnyVisitor.DEFAULT, context); @@ -129,7 +129,7 @@ public Expression convert(Expression expr) { } } if (expr instanceof ProjectionRole) { - return convert(((ProjectionRole) expr).getProjection()); + return convert(((ProjectionRole) expr).getProjection(), forOrder); } else if (expr instanceof FactoryExpression && !(expr instanceof FactoryExpressionAdapter)) { return FactoryExpressionUtils.wrap((FactoryExpression)expr); } else { @@ -137,9 +137,9 @@ public Expression convert(Expression expr) { } } - protected Expression createAlias(Expression expr, Path alias) { + protected Expression createAlias(Expression expr, Path alias) { assertRoot(alias); - return ExpressionUtils.as(expr, alias); + return ExpressionUtils.as((Expression)expr, alias); } public final T distinct() { @@ -178,8 +178,7 @@ public final

T fullJoin(MapExpression target, Path

alias) { return self; } - @SuppressWarnings("unchecked") - public final

T fullJoin(SubQueryExpression

target, Path alias) { + public final

T fullJoin(SubQueryExpression

target, Path alias) { metadata.addJoin(JoinType.FULLJOIN, createAlias(target, alias)); return self; } @@ -236,8 +235,7 @@ public final

T innerJoin(MapExpression target, Path

alias) { return self; } - @SuppressWarnings("unchecked") - public final

T innerJoin(SubQueryExpression

target, Path alias) { + public final

T innerJoin(SubQueryExpression

target, Path alias) { metadata.addJoin(JoinType.INNERJOIN, createAlias(target, alias)); return self; } @@ -270,8 +268,7 @@ public final

T join(MapExpression target, Path

alias) { return getSelf(); } - @SuppressWarnings("unchecked") - public final

T join(SubQueryExpression

target, Path alias) { + public final

T join(SubQueryExpression

target, Path alias) { metadata.addJoin(JoinType.JOIN, createAlias(target, alias)); return self; } @@ -296,8 +293,7 @@ public final

T leftJoin(MapExpression target, Path

alias) { return getSelf(); } - @SuppressWarnings("unchecked") - public final

T leftJoin(SubQueryExpression

target, Path alias) { + public final

T leftJoin(SubQueryExpression

target, Path alias) { metadata.addJoin(JoinType.LEFTJOIN, createAlias(target, alias)); return self; } @@ -325,7 +321,7 @@ public final T on(Predicate... conditions) { } public final T orderBy(OrderSpecifier spec) { - Expression e = convert(spec.getTarget()); + Expression e = convert(spec.getTarget(), true); if (!spec.getTarget().equals(e)) { metadata.addOrderBy(new OrderSpecifier(spec.getOrder(), e)); } else { @@ -366,8 +362,7 @@ public final

T rightJoin(MapExpression target, Path

alias) { return getSelf(); } - @SuppressWarnings("unchecked") - public final

T rightJoin(SubQueryExpression

target, Path alias) { + public final

T rightJoin(SubQueryExpression

target, Path alias) { metadata.addJoin(JoinType.RIGHTJOIN, createAlias(target, alias)); return self; } diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/AbstractSQLQuery.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/AbstractSQLQuery.java index 2ffe9a1c82..725dd07452 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/AbstractSQLQuery.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/AbstractSQLQuery.java @@ -60,8 +60,8 @@ private NativeQueryMixin(QueryMetadata metadata) { } @Override - public Expression convert(Expression expr) { - return super.convert(Conversions.convertForNativeQuery(expr)); + public Expression convert(Expression expr, boolean forOrder) { + return super.convert(Conversions.convertForNativeQuery(expr), forOrder); } } diff --git a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPAQueryMixin.java b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPAQueryMixin.java index 16a18b0ace..5794f7b571 100644 --- a/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPAQueryMixin.java +++ b/querydsl-jpa/src/main/java/com/mysema/query/jpa/JPAQueryMixin.java @@ -13,9 +13,11 @@ */ package com.mysema.query.jpa; -import java.util.HashSet; +import java.util.Map; import java.util.Set; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; import com.mysema.query.JoinFlag; import com.mysema.query.QueryMetadata; import com.mysema.query.support.Context; @@ -27,6 +29,9 @@ import com.mysema.query.types.ExpressionUtils; import com.mysema.query.types.OperationImpl; import com.mysema.query.types.Path; +import com.mysema.query.types.PathImpl; +import com.mysema.query.types.PathMetadata; +import com.mysema.query.types.PathType; import com.mysema.query.types.Predicate; /** @@ -38,7 +43,9 @@ */ public class JPAQueryMixin extends QueryMixin { - private final Set> paths = new HashSet>(); + private final Set> paths = Sets.newHashSet(); + + private final Map, Path> aliases = Maps.newHashMap(); public static final JoinFlag FETCH = new JoinFlag("fetch "); @@ -65,8 +72,44 @@ public T fetchAll() { } @Override - public Expression convert(Expression expr) { - return super.convert(Conversions.convert(expr)); + protected Expression createAlias(Expression expr, Path alias) { + aliases.put(expr, alias); + return super.createAlias(expr, alias); + } + + private Path shorten(Path path) { + PathMetadata metadata = path.getMetadata(); + if (metadata.getPathType() != PathType.PROPERTY) { + return path; + } else if (aliases.containsKey(path)) { + return (Path) aliases.get(path); + } else if (metadata.getParent().getMetadata().isRoot()) { + Path newPath = new PathImpl(path.getType(), path.toString().replace('.', '_')); + leftJoin(path, newPath); + return newPath; + } else { + Path parent = shorten(metadata.getParent()); + Path oldPath = new PathImpl(path.getType(), + new PathMetadata(parent, metadata.getElement(), metadata.getPathType())); + Path newPath = new PathImpl(path.getType(), oldPath.toString().replace('.', '_')); + leftJoin(oldPath, newPath); + return newPath; + } + } + + @Override + public Expression convert(Expression expr, boolean forOrder) { + if (forOrder && expr instanceof Path) { + Path path = (Path)expr; + PathMetadata metadata = path.getMetadata(); + // at least three levels + if (metadata.getParent() != null && !metadata.getParent().getMetadata().isRoot()) { + Path shortened = shorten(metadata.getParent()); + expr = new PathImpl((Class)shortened.getType(), + new PathMetadata(shortened, metadata.getElement(), metadata.getPathType())); + } + } + return super.convert(Conversions.convert(expr), forOrder); } @Override diff --git a/querydsl-jpa/src/test/java/com/mysema/query/jpa/JPAQueryMixinTest.java b/querydsl-jpa/src/test/java/com/mysema/query/jpa/JPAQueryMixinTest.java index 755af0a8b2..bbf0aa8b4d 100644 --- a/querydsl-jpa/src/test/java/com/mysema/query/jpa/JPAQueryMixinTest.java +++ b/querydsl-jpa/src/test/java/com/mysema/query/jpa/JPAQueryMixinTest.java @@ -1,17 +1,97 @@ package com.mysema.query.jpa; +import static org.junit.Assert.assertEquals; + +import java.util.Arrays; + import org.junit.Test; +import com.mysema.query.JoinExpression; +import com.mysema.query.JoinType; +import com.mysema.query.QueryMetadata; +import com.mysema.query.jpa.domain.QCat; +import com.mysema.query.types.PathMetadataFactory; import com.mysema.query.types.Predicate; public class JPAQueryMixinTest { - + private JPAQueryMixin mixin = new JPAQueryMixin(); @Test public void Where_Null() { mixin.where((Predicate)null); } - + @Test + public void OrderBy() { + QCat cat = QCat.cat; + QCat cat_mate = new QCat("cat_mate"); + mixin.from(cat); + mixin.orderBy(cat.mate.name.asc()); + + QueryMetadata md = mixin.getMetadata(); + assertEquals(Arrays.asList( + new JoinExpression(JoinType.DEFAULT, cat), + new JoinExpression(JoinType.LEFTJOIN, cat.mate.as(cat_mate))), + md.getJoins()); + assertEquals(Arrays.asList(cat_mate.name.asc()), + md.getOrderBy()); + } + + @Test + public void OrderBy_Long() { + QCat cat = QCat.cat; + QCat catMate = new QCat(PathMetadataFactory.forProperty(cat, "mate")); + QCat cat_mate = new QCat("cat_mate"); + QCat cat_mate_mate = new QCat("cat_mate_mate"); + mixin.from(cat); + mixin.orderBy(cat.mate.mate.name.asc()); + + QueryMetadata md = mixin.getMetadata(); + assertEquals(Arrays.asList( + new JoinExpression(JoinType.DEFAULT, cat), + new JoinExpression(JoinType.LEFTJOIN, cat.mate.as(cat_mate)), + new JoinExpression(JoinType.LEFTJOIN, cat_mate.mate.as(cat_mate_mate))), + md.getJoins()); + assertEquals(Arrays.asList(cat_mate_mate.name.asc()), + md.getOrderBy()); + } + + @Test + public void OrderBy_Reuse() { + QCat cat = QCat.cat; + QCat mate = new QCat("mate"); + mixin.from(cat); + mixin.leftJoin(cat.mate, mate); + mixin.orderBy(cat.mate.name.asc()); + + QueryMetadata md = mixin.getMetadata(); + assertEquals(Arrays.asList( + new JoinExpression(JoinType.DEFAULT, cat), + new JoinExpression(JoinType.LEFTJOIN, cat.mate.as(mate))), + md.getJoins()); + assertEquals(Arrays.asList(mate.name.asc()), + md.getOrderBy()); + } + + @Test + public void OrderBy_Long_Reuse() { + QCat cat = QCat.cat; + QCat mate = new QCat("mate"); + QCat mate_mate = new QCat("mate_mate"); + mixin.from(cat); + mixin.leftJoin(cat.mate, mate); + mixin.orderBy(cat.mate.mate.name.asc()); + + QueryMetadata md = mixin.getMetadata(); + assertEquals(Arrays.asList( + new JoinExpression(JoinType.DEFAULT, cat), + new JoinExpression(JoinType.LEFTJOIN, cat.mate.as(mate)), + new JoinExpression(JoinType.LEFTJOIN, mate.mate.as(mate_mate))), + md.getJoins()); + assertEquals(Arrays.asList(mate_mate.name.asc()), + md.getOrderBy()); + } + + // TODO test path.any() behaviour }