Merge pull request #1203 from querydsl/i1198

Transform containsKey and containsValue via JPAMapAccessVisitor
This commit is contained in:
Ruben Dijkstra 2015-02-21 15:59:55 +01:00
commit bf1c36e7cb
12 changed files with 73 additions and 113 deletions

View File

@ -0,0 +1,36 @@
/*
* Copyright 2015, Timo Westkämper
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.querydsl.core.types;
/**
*
* @param <T>
*/
public class ParameterizedPathImpl<T> extends PathImpl<T> implements ParameterizedExpression<T> {
private static final long serialVersionUID = -498707460985111265L;
private final Class<?>[] parameterTypes;
public ParameterizedPathImpl(Class<? extends T> type, PathMetadata metadata, Class<?>... parameterTypes) {
super(type, metadata);
this.parameterTypes = parameterTypes;
}
@Override
public Class<?> getParameter(int index) {
return parameterTypes[index];
}
}

View File

@ -18,11 +18,7 @@ import java.util.Collection;
import javax.annotation.Nullable;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.PathImpl;
import com.querydsl.core.types.PathMetadata;
import com.querydsl.core.types.PathMetadataFactory;
import com.querydsl.core.types.Visitor;
import com.querydsl.core.types.*;
import com.querydsl.core.types.expr.SimpleExpression;
/**
@ -60,7 +56,7 @@ public class CollectionPath<E, Q extends SimpleExpression<? super E>> extends Co
@SuppressWarnings("unchecked")
public CollectionPath(Class<? super E> type, Class<Q> queryType, PathMetadata metadata, PathInits inits) {
super(new PathImpl<Collection<E>>((Class)Collection.class, metadata), inits);
super(new ParameterizedPathImpl<Collection<E>>((Class)Collection.class, metadata, type), inits);
this.elementType = (Class<E>)type;
this.queryType = queryType;
this.pathMixin = (PathImpl<Collection<E>>)mixin;

View File

@ -20,12 +20,7 @@ import java.util.Map;
import javax.annotation.Nullable;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.PathImpl;
import com.querydsl.core.types.PathMetadata;
import com.querydsl.core.types.PathMetadataFactory;
import com.querydsl.core.types.Visitor;
import com.querydsl.core.types.*;
import com.querydsl.core.types.expr.ListExpression;
import com.querydsl.core.types.expr.SimpleExpression;
@ -66,7 +61,7 @@ public class ListPath<E, Q extends SimpleExpression<? super E>> extends Collecti
@SuppressWarnings("unchecked")
public ListPath(Class<? super E> elementType, Class<Q> queryType, PathMetadata metadata, PathInits inits) {
super(new PathImpl<List<E>>((Class)List.class, metadata), inits);
super(new ParameterizedPathImpl<List<E>>((Class)List.class, metadata, elementType), inits);
this.elementType = (Class<E>)elementType;
this.queryType = queryType;
this.pathMixin = (PathImpl<List<E>>)mixin;

View File

@ -20,13 +20,7 @@ import java.util.Map;
import javax.annotation.Nullable;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.ExpressionException;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.PathImpl;
import com.querydsl.core.types.PathMetadata;
import com.querydsl.core.types.PathMetadataFactory;
import com.querydsl.core.types.Visitor;
import com.querydsl.core.types.*;
import com.querydsl.core.types.expr.MapExpressionBase;
import com.querydsl.core.types.expr.SimpleExpression;
@ -63,7 +57,7 @@ public class MapPath<K, V, E extends SimpleExpression<? super V>> extends MapExp
@SuppressWarnings("unchecked")
public MapPath(Class<? super K> keyType, Class<? super V> valueType, Class<E> queryType, PathMetadata metadata) {
super(new PathImpl<Map<K,V>>((Class)Map.class, metadata));
super(new ParameterizedPathImpl<Map<K,V>>((Class)Map.class, metadata, keyType, valueType));
this.keyType = (Class<K>) keyType;
this.valueType = (Class<V>) valueType;
this.queryType = queryType;

View File

@ -18,11 +18,7 @@ import java.util.Set;
import javax.annotation.Nullable;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.PathImpl;
import com.querydsl.core.types.PathMetadata;
import com.querydsl.core.types.PathMetadataFactory;
import com.querydsl.core.types.Visitor;
import com.querydsl.core.types.*;
import com.querydsl.core.types.expr.SimpleExpression;
/**
@ -60,7 +56,7 @@ public class SetPath<E, Q extends SimpleExpression<? super E>> extends Collectio
@SuppressWarnings("unchecked")
public SetPath(Class<? super E> type, Class<Q> queryType, PathMetadata metadata, PathInits inits) {
super(new PathImpl<Set<E>>((Class)Set.class, metadata), inits);
super(new ParameterizedPathImpl<Set<E>>((Class)Set.class, metadata, type), inits);
this.elementType = (Class<E>)type;
this.queryType = queryType;
this.pathMixin = (PathImpl<Set<E>>)mixin;

View File

@ -23,7 +23,6 @@ import java.util.Map;
import com.google.common.collect.ImmutableMap;
import com.querydsl.core.types.Operator;
import com.querydsl.core.types.Ops;
import com.querydsl.core.types.PathType;
/**
* HQLTemplates extends JPQLTemplates with Hibernate specific extensions
@ -75,23 +74,6 @@ public class HQLTemplates extends JPQLTemplates {
builder.put(BigDecimal.class, "big_decimal");
typeNames = builder.build();
// TODO : remove this when Hibernate supports member of properly
add(JPQLOps.MEMBER_OF, "{0} in elements({1})");
add(JPQLOps.NOT_MEMBER_OF, "{0} not in elements({1})");
// path types
for (PathType type : new PathType[] {
PathType.LISTVALUE,
PathType.MAPVALUE,
PathType.MAPVALUE_CONSTANT }) {
add(type, "{0}[{1}]");
}
add(PathType.LISTVALUE_CONSTANT, "{0}[{1s}]");
add(PathType.COLLECTION_ANY, "any elements({0})");
add(Ops.CONTAINS_KEY, "{1} in indices({0})");
add(Ops.CONTAINS_VALUE, "{1} in elements({0})");
// add Hibernate Spatial mappings, if on classpath
try {
Class cl = Class.forName("com.querydsl.spatial.hibernate.HibernateSpatialSupport");

View File

@ -21,6 +21,28 @@ class JPAMapAccessVisitor extends ReplaceVisitor<Void> {
this.metadata = metadata;
}
@Override
public Expression<?> visit(Operation<?> expr, @Nullable Void context) {
if (expr.getOperator() == Ops.CONTAINS_KEY) {
ParameterizedExpression map = (ParameterizedExpression<?>) expr.getArg(0);
Expression key = expr.getArg(1);
Path replacement = new PathImpl<Object>(map.getParameter(1),
ExpressionUtils.createRootVariable((Path<?>)map));
metadata.addJoin(JoinType.LEFTJOIN, ExpressionUtils.as(map, replacement));
metadata.addJoinCondition(ExpressionUtils.eq(
Expressions.operation(map.getParameter(0), JPQLOps.KEY, replacement),
key));
return ExpressionUtils.isNotNull(replacement);
} else if (expr.getOperator() == Ops.CONTAINS_VALUE) {
ParameterizedExpression<?> map = (ParameterizedExpression<?>) expr.getArg(0);
Expression<?> value = expr.getArg(1);
return Expressions.predicate(JPQLOps.MEMBER_OF, value, map);
} else {
return super.visit(expr, context);
}
}
@Override
public Expression<?> visit(Path<?> expr, @Nullable Void context) {
expr = (Path<?>) super.visit(expr, null);
PathMetadata pathMetadata = expr.getMetadata();
@ -33,7 +55,7 @@ class JPAMapAccessVisitor extends ReplaceVisitor<Void> {
ParameterizedExpression parExpr = (ParameterizedExpression) parent;
replacement = new PathImpl(parExpr.getParameter(1),
ExpressionUtils.createRootVariable(parent));
metadata.addJoin(JoinType.JOIN, ExpressionUtils.as(parent, replacement));
metadata.addJoin(JoinType.LEFTJOIN, ExpressionUtils.as(parent, replacement));
metadata.addJoinCondition(ExpressionUtils.eq(
Expressions.operation(parExpr.getParameter(0), JPQLOps.KEY, replacement),
ExpressionUtils.toExpression(pathMetadata.getElement())));

View File

@ -907,42 +907,36 @@ public abstract class AbstractJPATest {
}
@Test
@NoEclipseLink @NoOpenJPA @NoBatooJPA
public void Map_ContainsKey() {
QShow show = QShow.show;
assertEquals(1l, query().from(show).where(show.acts.containsKey("a")).count());
}
@Test
@NoEclipseLink @NoOpenJPA @NoBatooJPA
public void Map_ContainsKey2() {
QShow show = QShow.show;
assertEquals(1l, query().from(show).where(show.acts.containsKey("b")).count());
}
@Test
@NoEclipseLink @NoOpenJPA @NoBatooJPA
public void Map_ContainsKey3() {
QShow show = QShow.show;
assertEquals(0l, query().from(show).where(show.acts.containsKey("c")).count());
}
@Test
@NoEclipseLink @NoOpenJPA @NoBatooJPA
public void Map_ContainsValue() {
QShow show = QShow.show;
assertEquals(1l, query().from(show).where(show.acts.containsValue("A")).count());
}
@Test
@NoEclipseLink @NoOpenJPA @NoBatooJPA
public void Map_ContainsValue2() {
QShow show = QShow.show;
assertEquals(1l, query().from(show).where(show.acts.containsValue("B")).count());
}
@Test
@NoEclipseLink @NoOpenJPA @NoBatooJPA
public void Map_ContainsValue3() {
QShow show = QShow.show;
assertEquals(0l, query().from(show).where(show.acts.containsValue("C")).count());

View File

@ -21,39 +21,22 @@ public class CollectionTest extends AbstractQueryTest{
@Test
public void Constant_InElements_Set() {
assertToString("?1 in elements(cat.kittensSet)", cat.kittensSet.contains(new Cat()));
assertToString("?1 member of cat.kittensSet", cat.kittensSet.contains(new Cat()));
}
@Test
public void Constant_InElements_List() {
assertToString("?1 in elements(cat.kittens)", cat.kittens.contains(new Cat()));
assertToString("?1 member of cat.kittens", cat.kittens.contains(new Cat()));
}
@Test
public void Path_InElements_List() {
assertToString("cat in elements(cat1.kittens)", cat.in(cat1.kittens));
assertToString("cat member of cat1.kittens", cat.in(cat1.kittens));
}
@Test
public void Path_InElements_Set() {
assertToString("cat in elements(cat1.kittensSet)", cat.in(cat1.kittensSet));
assertToString("cat member of cat1.kittensSet", cat.in(cat1.kittensSet));
}
@Test
public void CollectionOperations() {
// HQL functions that take collection-valued path expressions: size(),
// minelement(), maxelement(), minindex(), maxindex(), along with the
// special elements() and indices functions which may be quantified
// using some, all, exists, any, in.
cat.kittens.size();
// minelement(cat.kittens);
// maxelement(cat.kittens);
// minindex(cat.kittens);
// maxindex(cat.kittens);
assertToString("cat.kittens[0]", cat.kittens(0));
assertToString("cat.kittens[0]", cat.kittens.get(0));
// some, all, exists, any, in.
}
}

View File

@ -129,7 +129,7 @@ public class FeaturesTest extends AbstractQueryTest {
// toString("cat.bodyWeight as bw", cat.bodyWeight.as("bw"));
assertToString("kitten in elements(cat.kittens)", kitten.in(cat.kittens));
assertToString("kitten member of cat.kittens", kitten.in(cat.kittens));
// toString("distinct cat.bodyWeight", distinct(cat.bodyWeight));
}

View File

@ -154,7 +154,7 @@ public class JPQLSerializerTest {
serializer.serializeForDelete(md);
assertEquals("delete from Cat kitten\n" +
"where kitten.id = ?1 and exists (select 1\n" +
"from Cat cat\nwhere cat.id = ?2 and kitten in elements(cat.kittens))", serializer.toString());
"from Cat cat\nwhere cat.id = ?2 and kitten member of cat.kittens)", serializer.toString());
}
@Test

View File

@ -1,38 +0,0 @@
/*
* Copyright 2011, Mysema Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.querydsl.jpa;
import org.junit.Test;
import com.querydsl.jpa.domain.QShow;
public class MapTest extends AbstractQueryTest {
private QShow show = QShow.show;
@Test
public void Contains() {
assertToString("show.acts[?1] = ?2", show.acts.contains("x", "y"));
}
@Test
public void Contains_Key() {
assertToString("?1 in indices(show.acts)", show.acts.containsKey("x"));
}
@Test
public void Contains_Value() {
assertToString("?1 in elements(show.acts)", show.acts.containsValue("y"));
}
}