#175 improved path in collection handling

This commit is contained in:
Timo Westkämper 2012-06-21 23:51:42 +03:00
parent 58c7c88853
commit 6bb37746af
18 changed files with 157 additions and 72 deletions

View File

@ -42,7 +42,7 @@ import com.mysema.query.types.query.TimeSubQuery;
*/
public class DetachableQuery <Q extends DetachableQuery<Q>> extends QueryBase<Q> implements Detachable {
private final DetachableMixin detachableMixin;
private final Detachable detachableMixin;
public DetachableQuery(QueryMixin<Q> queryMixin) {
super(queryMixin);

View File

@ -191,7 +191,7 @@ public class AbstractJPQLSubQuery<Q extends AbstractJPQLSubQuery<Q>> extends Det
public String toString() {
if (!queryMixin.getMetadata().getJoins().isEmpty()) {
JPQLSerializer serializer = new JPQLSerializer(JPQLTemplates.DEFAULT);
JPQLSerializer serializer = new JPQLSerializer(JPQLTemplates.DEFAULT, null);
serializer.serialize(queryMixin.getMetadata(), false, null);
return serializer.toString().trim();
} else {

View File

@ -15,6 +15,9 @@ package com.mysema.query.jpa;
import java.util.Map;
import javax.annotation.Nullable;
import javax.persistence.EntityManager;
import com.mysema.query.QueryMetadata;
import com.mysema.query.support.ProjectableQuery;
import com.mysema.query.types.CollectionExpression;
@ -36,6 +39,18 @@ public abstract class JPQLQueryBase<Q extends JPQLQueryBase<Q>> extends Projecta
private final JPQLQueryMixin<Q> queryMixin;
private final JPQLTemplates templates;
@Nullable
protected final EntityManager entityManager;
@SuppressWarnings("unchecked")
public JPQLQueryBase(QueryMetadata md, JPQLTemplates templates, @Nullable EntityManager entityManager) {
super(new JPQLQueryMixin<Q>(md));
super.queryMixin.setSelf((Q) this);
this.queryMixin = (JPQLQueryMixin) super.queryMixin;
this.templates = templates;
this.entityManager = entityManager;
}
protected JPQLTemplates getTemplates() {
return templates;
@ -44,20 +59,12 @@ public abstract class JPQLQueryBase<Q extends JPQLQueryBase<Q>> extends Projecta
protected JPQLQueryMixin<Q> getQueryMixin() {
return queryMixin;
}
@SuppressWarnings("unchecked")
public JPQLQueryBase(QueryMetadata md, JPQLTemplates templates) {
super(new JPQLQueryMixin<Q>(md));
super.queryMixin.setSelf((Q) this);
this.queryMixin = (JPQLQueryMixin) super.queryMixin;
this.templates = templates;
}
protected String buildQueryString(boolean forCountRow) {
if (queryMixin.getMetadata().getJoins().isEmpty()) {
throw new IllegalArgumentException("No joins given");
}
JPQLSerializer serializer = new JPQLSerializer(templates);
JPQLSerializer serializer = new JPQLSerializer(templates, entityManager);
serializer.serialize(queryMixin.getMetadata(), forCountRow, null);
constants = serializer.getConstantToLabel();
return serializer.toString();

View File

@ -26,14 +26,33 @@ import java.util.Set;
import javax.annotation.Nullable;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.PersistenceUnitUtil;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.metamodel.SingularAttribute;
import com.mysema.query.JoinExpression;
import com.mysema.query.JoinType;
import com.mysema.query.QueryMetadata;
import com.mysema.query.support.SerializerBase;
import com.mysema.query.types.*;
import com.mysema.query.types.Constant;
import com.mysema.query.types.ConstantImpl;
import com.mysema.query.types.EntityPath;
import com.mysema.query.types.Expression;
import com.mysema.query.types.ExpressionUtils;
import com.mysema.query.types.FactoryExpression;
import com.mysema.query.types.Operator;
import com.mysema.query.types.Ops;
import com.mysema.query.types.OrderSpecifier;
import com.mysema.query.types.ParamExpression;
import com.mysema.query.types.Path;
import com.mysema.query.types.PathImpl;
import com.mysema.query.types.PathType;
import com.mysema.query.types.Predicate;
import com.mysema.query.types.SubQueryExpression;
import com.mysema.util.MathUtils;
/**
@ -80,6 +99,8 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
private final JPQLTemplates templates;
private final EntityManager entityManager;
private boolean inProjection = false;
static{
@ -94,8 +115,13 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
private boolean wrapElements = false;
public JPQLSerializer(JPQLTemplates templates) {
this(templates, null);
}
public JPQLSerializer(JPQLTemplates templates, EntityManager em) {
super(templates);
this.templates = templates;
this.entityManager = em;
}
private void handleJoinTarget(JoinExpression je) {
@ -304,6 +330,17 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
}
return null;
}
@SuppressWarnings("rawtypes")
private SingularAttribute<?,?> getIdProperty(EntityType entity) {
Set<SingularAttribute> singularAttributes = entity.getSingularAttributes();
for (SingularAttribute singularAttribute : singularAttributes) {
if (singularAttribute.isId()){
return singularAttribute;
}
}
return null;
}
@Override
@SuppressWarnings("unchecked")
@ -311,20 +348,13 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
boolean old = wrapElements;
wrapElements = templates.wrapElements(operator);
// TODO : refactor each case into own method
if (operator.equals(Ops.IN)) {
if (args.get(1) instanceof Path) {
if (!templates.isEnumInPathSupported() && args.get(0) instanceof Constant && Enum.class.isAssignableFrom(args.get(0).getType())) {
Enumerated enumerated = ((Path)args.get(1)).getAnnotatedElement().getAnnotation(Enumerated.class);
Enum constant = (Enum)((Constant)args.get(0)).getConstant();
if (enumerated == null || enumerated.value() == EnumType.ORDINAL) {
args = Arrays.asList(new ConstantImpl<Integer>(constant.ordinal()), args.get(1));
} else {
args = Arrays.asList(new ConstantImpl<String>(constant.name()), args.get(1));
}
}
super.visitOperation(type, JPQLTemplates.MEMBER_OF, args);
} else {
visitAnyInPath(type, args);
} else if (args.get(0) instanceof Path && args.get(1) instanceof Constant) {
visitPathInCollection(type, operator, args);
} else {
super.visitOperation(type, operator, args);
}
@ -368,7 +398,45 @@ public class JPQLSerializer extends SerializerBase<JPQLSerializer> {
wrapElements = old;
}
@SuppressWarnings("unchecked")
@SuppressWarnings({ "rawtypes", "unchecked" })
private void visitPathInCollection(Class<?> type, Operator<?> operator,
List<? extends Expression<?>> args) {
// NOTE turns entityPath in collection into entityPath.id in (collection of ids)
if (entityManager != null && args.get(0).getType().isAnnotationPresent(Entity.class)) {
Path<?> lhs = (Path<?>) args.get(0);
Constant<?> rhs = (Constant<?>) args.get(1);
Metamodel metamodel = entityManager.getMetamodel();
PersistenceUnitUtil util = entityManager.getEntityManagerFactory().getPersistenceUnitUtil();
EntityType<?> entityType = metamodel.entity(args.get(0).getType());
if (entityType.hasSingleIdAttribute()) {
SingularAttribute<?,?> id = getIdProperty(entityType);
lhs = new PathImpl(id.getJavaType(), lhs, id.getName());
Set ids = new HashSet();
for (Object entity : (Collection<?>)rhs.getConstant()) {
ids.add(util.getIdentifier(entity));
}
rhs = new ConstantImpl(ids);
args = Arrays.asList(lhs, rhs);
}
}
super.visitOperation(type, operator, args);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void visitAnyInPath(Class<?> type, List<? extends Expression<?>> args) {
if (!templates.isEnumInPathSupported() && args.get(0) instanceof Constant && Enum.class.isAssignableFrom(args.get(0).getType())) {
Enumerated enumerated = ((Path)args.get(1)).getAnnotatedElement().getAnnotation(Enumerated.class);
Enum constant = (Enum)((Constant)args.get(0)).getConstant();
if (enumerated == null || enumerated.value() == EnumType.ORDINAL) {
args = Arrays.asList(new ConstantImpl<Integer>(constant.ordinal()), args.get(1));
} else {
args = Arrays.asList(new ConstantImpl<String>(constant.name()), args.get(1));
}
}
super.visitOperation(type, JPQLTemplates.MEMBER_OF, args);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private List<? extends Expression<?>> normalizeNumericArgs(List<? extends Expression<?>> args) {
boolean hasConstants = false;
Class<? extends Number> numType = null;

View File

@ -39,7 +39,6 @@ import com.mysema.query.SearchResults;
import com.mysema.query.jpa.HQLTemplates;
import com.mysema.query.jpa.JPQLQueryBase;
import com.mysema.query.jpa.JPQLTemplates;
import com.mysema.query.types.ArrayConstructorExpression;
import com.mysema.query.types.Expression;
import com.mysema.query.types.FactoryExpression;
import com.mysema.query.types.FactoryExpressionUtils;
@ -78,7 +77,7 @@ public abstract class AbstractHibernateQuery<Q extends AbstractHibernateQuery<Q>
}
public AbstractHibernateQuery(SessionHolder session, JPQLTemplates patterns, QueryMetadata metadata) {
super(metadata, patterns);
super(metadata, patterns, null);
this.session = session;
}

View File

@ -63,7 +63,7 @@ public class HibernateDeleteClause implements DeleteClause<HibernateDeleteClause
@Override
public long execute() {
JPQLSerializer serializer = new JPQLSerializer(templates);
JPQLSerializer serializer = new JPQLSerializer(templates, null);
serializer.serializeForDelete(md);
Map<Object,String> constants = serializer.getConstantToLabel();
@ -80,7 +80,7 @@ public class HibernateDeleteClause implements DeleteClause<HibernateDeleteClause
@Override
public String toString() {
JPQLSerializer serializer = new JPQLSerializer(templates);
JPQLSerializer serializer = new JPQLSerializer(templates, null);
serializer.serializeForDelete(md);
return serializer.toString();
}

View File

@ -70,7 +70,7 @@ public class HibernateUpdateClause implements
@Override
public long execute() {
JPQLSerializer serializer = new JPQLSerializer(templates);
JPQLSerializer serializer = new JPQLSerializer(templates, null);
serializer.serializeForUpdate(metadata);
Map<Object, String> constants = serializer.getConstantToLabel();
@ -124,7 +124,7 @@ public class HibernateUpdateClause implements
@Override
public String toString() {
JPQLSerializer serializer = new JPQLSerializer(templates);
JPQLSerializer serializer = new JPQLSerializer(templates, null);
serializer.serializeForUpdate(metadata);
return serializer.toString();
}

View File

@ -65,8 +65,6 @@ public abstract class AbstractJPAQuery<Q extends AbstractJPAQuery<Q>> extends JP
}
}
private final JPASessionHolder sessionHolder;
protected final Map<String,Object> hints = new HashMap<String,Object>();
@Nullable
@ -78,12 +76,11 @@ public abstract class AbstractJPAQuery<Q extends AbstractJPAQuery<Q>> extends JP
protected boolean factoryExpressionUsed = false;
public AbstractJPAQuery(EntityManager em) {
this(new DefaultSessionHolder(em), HQLTemplates.DEFAULT, new DefaultQueryMetadata());
this(em, HQLTemplates.DEFAULT, new DefaultQueryMetadata());
}
public AbstractJPAQuery(JPASessionHolder sessionHolder, JPQLTemplates patterns, QueryMetadata metadata) {
super(metadata, patterns);
this.sessionHolder = sessionHolder;
public AbstractJPAQuery(EntityManager em, JPQLTemplates patterns, QueryMetadata metadata) {
super(metadata, patterns, em);
}
public long count() {
@ -135,7 +132,7 @@ public abstract class AbstractJPAQuery<Q extends AbstractJPAQuery<Q>> extends JP
}
private Query createQuery(String queryString, @Nullable QueryModifiers modifiers, boolean forCount) {
Query query = sessionHolder.createQuery(queryString);
Query query = entityManager.createQuery(queryString);
JPAUtil.setConstants(query, getConstants(), getMetadata().getParams());
if (modifiers != null && modifiers.isRestricting()) {
if (modifiers.getLimit() != null) {

View File

@ -54,7 +54,7 @@ public class JPADeleteClause implements DeleteClause<JPADeleteClause>{
@Override
public long execute() {
JPQLSerializer serializer = new JPQLSerializer(templates);
JPQLSerializer serializer = new JPQLSerializer(templates, entityManager);
serializer.serializeForDelete(metadata);
Map<Object,String> constants = serializer.getConstantToLabel();
@ -71,7 +71,7 @@ public class JPADeleteClause implements DeleteClause<JPADeleteClause>{
@Override
public String toString() {
JPQLSerializer serializer = new JPQLSerializer(templates);
JPQLSerializer serializer = new JPQLSerializer(templates, entityManager);
serializer.serializeForDelete(metadata);
return serializer.toString();
}

View File

@ -34,7 +34,7 @@ public final class JPAQuery extends AbstractJPAQuery<JPAQuery> implements JPQLQu
* The query can be attached via the clone method
*/
public JPAQuery() {
super(new NoSessionHolder(), HQLTemplates.DEFAULT, new DefaultQueryMetadata());
super(null, HQLTemplates.DEFAULT, new DefaultQueryMetadata());
}
/**
@ -43,8 +43,7 @@ public final class JPAQuery extends AbstractJPAQuery<JPAQuery> implements JPQLQu
* @param em
*/
public JPAQuery(EntityManager em) {
super(new DefaultSessionHolder(em), JPAProvider.getTemplates(em),
new DefaultQueryMetadata());
super(em, JPAProvider.getTemplates(em), new DefaultQueryMetadata());
}
/**
@ -53,7 +52,7 @@ public final class JPAQuery extends AbstractJPAQuery<JPAQuery> implements JPQLQu
* @param em
*/
public JPAQuery(EntityManager em, QueryMetadata metadata) {
super(new DefaultSessionHolder(em), JPAProvider.getTemplates(em), metadata);
super(em, JPAProvider.getTemplates(em), metadata);
}
/**
@ -63,18 +62,18 @@ public final class JPAQuery extends AbstractJPAQuery<JPAQuery> implements JPQLQu
* @param patterns
*/
public JPAQuery(EntityManager em, JPQLTemplates patterns) {
super(new DefaultSessionHolder(em), patterns, new DefaultQueryMetadata());
super(em, patterns, new DefaultQueryMetadata());
}
/**
* Creates a new query
*
* @param session
* @param em
* @param templates
* @param metadata
*/
public JPAQuery(JPASessionHolder session, JPQLTemplates templates, QueryMetadata metadata) {
super(session, templates, metadata);
public JPAQuery(EntityManager em, JPQLTemplates templates, QueryMetadata metadata) {
super(em, templates, metadata);
}
/**
@ -84,7 +83,7 @@ public final class JPAQuery extends AbstractJPAQuery<JPAQuery> implements JPQLQu
* @return
*/
public JPAQuery clone(EntityManager entityManager) {
JPAQuery q = new JPAQuery(new DefaultSessionHolder(entityManager), getTemplates(), getMetadata().clone());
JPAQuery q = new JPAQuery(entityManager, getTemplates(), getMetadata().clone());
q.factoryExpressionUsed = factoryExpressionUsed;
q.flushMode = flushMode;
q.hints.putAll(hints);

View File

@ -59,7 +59,7 @@ public class JPAUpdateClause implements UpdateClause<JPAUpdateClause>{
@Override
public long execute() {
JPQLSerializer serializer = new JPQLSerializer(templates);
JPQLSerializer serializer = new JPQLSerializer(templates, entityManager);
serializer.serializeForUpdate(metadata);
Map<Object,String> constants = serializer.getConstantToLabel();
@ -112,7 +112,7 @@ public class JPAUpdateClause implements UpdateClause<JPAUpdateClause>{
@Override
public String toString() {
JPQLSerializer serializer = new JPQLSerializer(templates);
JPQLSerializer serializer = new JPQLSerializer(templates, entityManager);
serializer.serializeForUpdate(metadata);
return serializer.toString();
}

View File

@ -35,8 +35,6 @@ import com.mysema.query.QueryModifiers;
import com.mysema.query.SearchResults;
import com.mysema.query.jpa.AbstractSQLQuery;
import com.mysema.query.jpa.NativeSQLSerializer;
import com.mysema.query.jpa.impl.DefaultSessionHolder;
import com.mysema.query.jpa.impl.JPASessionHolder;
import com.mysema.query.jpa.impl.JPAUtil;
import com.mysema.query.sql.SQLTemplates;
import com.mysema.query.sql.Union;
@ -61,7 +59,7 @@ public abstract class AbstractJPASQLQuery<Q extends AbstractJPASQLQuery<Q> & com
@Nullable
private Map<Object,String> constants;
private final JPASessionHolder session;
private final EntityManager entityManager;
protected final SQLTemplates templates;
@ -79,12 +77,12 @@ public abstract class AbstractJPASQLQuery<Q extends AbstractJPASQLQuery<Q> & com
protected FlushModeType flushMode;
public AbstractJPASQLQuery(EntityManager entityManager, SQLTemplates sqlTemplates) {
this(new DefaultSessionHolder(entityManager), sqlTemplates, new DefaultQueryMetadata());
this(entityManager, sqlTemplates, new DefaultQueryMetadata());
}
public AbstractJPASQLQuery(JPASessionHolder session, SQLTemplates sqlTemplates, QueryMetadata metadata) {
public AbstractJPASQLQuery(EntityManager entityManager, SQLTemplates sqlTemplates, QueryMetadata metadata) {
super(metadata);
this.session = session;
this.entityManager = entityManager;
this.templates = sqlTemplates;
}
@ -114,13 +112,13 @@ public abstract class AbstractJPASQLQuery<Q extends AbstractJPASQLQuery<Q> & com
Query query;
if (projection.get(0) instanceof EntityPath) {
if (projection.size() == 1) {
query = session.createSQLQuery(queryString, projection.get(0).getType());
query = entityManager.createNativeQuery(queryString, projection.get(0).getType());
} else {
throw new IllegalArgumentException("Only single element entity projections are supported");
}
} else {
query = session.createSQLQuery(queryString);
query = entityManager.createNativeQuery(queryString);
}
if (lockMode != null) {

View File

@ -16,8 +16,6 @@ package com.mysema.query.jpa.sql;
import javax.persistence.EntityManager;
import com.mysema.query.QueryMetadata;
import com.mysema.query.jpa.impl.DefaultSessionHolder;
import com.mysema.query.jpa.impl.JPASessionHolder;
import com.mysema.query.sql.SQLCommonQuery;
import com.mysema.query.sql.SQLTemplates;
@ -34,12 +32,12 @@ public final class JPASQLQuery extends AbstractJPASQLQuery<JPASQLQuery> implemen
super(entityManager, sqlTemplates);
}
public JPASQLQuery(JPASessionHolder session, SQLTemplates sqlTemplates, QueryMetadata metadata) {
super(session, sqlTemplates, metadata);
public JPASQLQuery(EntityManager entityManager, SQLTemplates sqlTemplates, QueryMetadata metadata) {
super(entityManager, sqlTemplates, metadata);
}
public JPASQLQuery clone(EntityManager entityManager) {
JPASQLQuery q = new JPASQLQuery(new DefaultSessionHolder(entityManager), templates, getMetadata().clone());
JPASQLQuery q = new JPASQLQuery(entityManager, templates, getMetadata().clone());
q.flushMode = flushMode;
q.hints.putAll(hints);
q.lockMode = lockMode;

View File

@ -39,6 +39,7 @@ import org.junit.Test;
import antlr.RecognitionException;
import antlr.TokenStreamException;
import com.google.common.collect.Lists;
import com.mysema.commons.lang.Pair;
import com.mysema.query.group.GroupBy;
import com.mysema.query.group.QPair;
@ -298,6 +299,28 @@ public abstract class AbstractStandardTest {
cat.kittens.any().bodyWeight.gt(10.0)).count());
}
@Test
public void Any_In1() {
//select cat from Cat cat where exists (
// select cat_kittens from Cat cat_kittens where cat_kittens member of cat.kittens and cat_kittens in ?1)
query().from(cat).where(cat.kittens.any().in(savedCats)).list(cat);
}
@Test
public void Any_In11() {
List<Integer> ids = Lists.newArrayList();
for (Cat cat : savedCats) ids.add(cat.getId());
query().from(cat).where(cat.kittens.any().id.in(ids)).list(cat);
}
@Test
public void Any_In2() {
query().from(cat).where(
cat.kittens.any().in(savedCats),
cat.kittens.any().in(savedCats.subList(0, 1)).not())
.list(cat);
}
@Test
public void JoinEmbeddable() {
QBookVersion bookVersion = QBookVersion.bookVersion;

View File

@ -19,14 +19,10 @@ import static org.junit.Assert.assertTrue;
import org.eclipse.persistence.config.HintValues;
import org.eclipse.persistence.config.QueryHints;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import com.mysema.query.AbstractJPATest;
import com.mysema.query.Target;
import com.mysema.query.jpa.EclipseLinkTemplates;
import com.mysema.query.jpa.JPQLTemplates;
import com.mysema.query.jpa.domain.QCat;
import com.mysema.testutil.JPAConfig;
import com.mysema.testutil.JPATestRunner;

View File

@ -28,7 +28,7 @@ public abstract class AbstractQueryTest implements Constants{
}
protected static void assertToString(String expected, Expression<?> expr) {
JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT);
JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT, null);
assertEquals(expected, serializer.handle(expr).toString().replace("\n", " "));
}

View File

@ -77,7 +77,7 @@ public class JPQLCollectionAnyVisitorTest {
private String serialize(Expression<?> expression){
Expression<?> transformed = expression.accept(JPQLCollectionAnyVisitor.DEFAULT, new Context());
JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT);
JPQLSerializer serializer = new JPQLSerializer(HQLTemplates.DEFAULT, null);
serializer.handle(transformed);
return serializer.toString();
}

View File

@ -34,7 +34,7 @@ import com.mysema.query.types.Expression;
class QueryHelper extends JPQLQueryBase<QueryHelper> {
public QueryHelper() {
super(new DefaultQueryMetadata(), HQLTemplates.DEFAULT);
super(new DefaultQueryMetadata(), HQLTemplates.DEFAULT, null);
}
public long count() {