1

ここで概説されているように、JPA エンティティがあります: QueryDSL JPA syntax error with contains on Set?

Set tags今、私は単一のクエリで複数の制限を設けようとしています:

Set<Tag> withTags = ...;
Set<Tag> withoutTags = ...;

q.where(license.tags.any().in(withTags));
q.where(license.tags.any().in(withoutTags).not());

クエリが実行されると、次の例外が発生します。

Exception [EclipseLink-8019] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.JPQLException
Exception Description: Error compiling the query [select distinct license
from License license
where exists (select license_tags
from Tag license_tags
where license_tags member of license.tags and license_tags = ?1)
and not exists (select license_tags
from Tag license_tags
where license_tags member of license.tags and license_tags = ?2)]
multiple declaration of identification variable [license_tags], previously declared as [Tag license_tags].

as("withTags")クエリに挿入しようとしましたが、それができるany()場所は、解決しようとしている重複の問題に関して、JPQL の AS を間違った場所に挿入することです。そして、後で挿入できますが、実行できない as returntagsを取得します。SimpleExpressionany()

この識別変数の重複をどのように防ぐことができるか、他の考えはありますか?

さらに、上記のステートメントは、指定されたSet withTags/withoutTagsに単一の値しか含まれていない場合にのみ機能します。複数の値が存在する場合、次の例外がスローされます。

Exception [EclipseLink-6075] (Eclipse Persistence Services - 2.0.1.v20100213-r6600): org.eclipse.persistence.exceptions.QueryException
Exception Description: Object comparisons can only use the equal() or notEqual() operators.  Other comparisons must be done through query keys or direct attribute level comparisons. 
Expression: [Relation operator  IN   Base my.package.Tag   Parameter 1]
select distinct license
from License license
where exists (select license_tags
from Tag license_tags
where license_tags member of license.tags and license_tags in ?1)
    at org.eclipse.persistence.exceptions.QueryException.invalidOperatorForObjectComparison(QueryException.java:614)
    at org.eclipse.persistence.internal.expressions.RelationExpression.normalize(RelationExpression.java:393)
    at org.eclipse.persistence.internal.expressions.CompoundExpression.normalize(CompoundExpression.java:226)
    at org.eclipse.persistence.internal.expressions.CompoundExpression.normalize(CompoundExpression.java:218)
    at org.eclipse.persistence.internal.expressions.SQLSelectStatement.normalize(SQLSelectStatement.java:1306)
    at org.eclipse.persistence.internal.expressions.SubSelectExpression.normalizeSubSelect(SubSelectExpression.java:134)
    at org.eclipse.persistence.internal.expressions.ExpressionNormalizer.normalizeSubSelects(ExpressionNormalizer.java:93)
    at org.eclipse.persistence.internal.expressions.SQLSelectStatement.normalize(SQLSelectStatement.java:1379)
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.buildNormalSelectStatement(ExpressionQueryMechanism.java:482)
    at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.prepareSelectAllRows(ExpressionQueryMechanism.java:1553)
    at org.eclipse.persistence.queries.ReadAllQuery.prepareSelectAllRows(ReadAllQuery.java:793)
    at org.eclipse.persistence.queries.ReadAllQuery.prepare(ReadAllQuery.java:734)
    at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:464)
    at org.eclipse.persistence.queries.ObjectLevelReadQuery.checkPrepare(ObjectLevelReadQuery.java:732)
    at org.eclipse.persistence.queries.DatabaseQuery.prepareCall(DatabaseQuery.java:1577)
    at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:240)
    at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:173)
    at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:125)
    at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:109)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1326)
    at sun.reflect.GeneratedMethodAccessor552.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.jboss.weld.util.reflection.SecureReflections$13.work(SecureReflections.java:304)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.run(SecureReflectionAccess.java:54)
    at org.jboss.weld.util.reflection.SecureReflectionAccess.runAsInvocation(SecureReflectionAccess.java:163)
    at org.jboss.weld.util.reflection.SecureReflections.invoke(SecureReflections.java:298)
    at org.jboss.weld.bean.proxy.ClientProxyMethodHandler.invoke(ClientProxyMethodHandler.java:113)
    at org.jboss.weld.util.CleanableMethodHandler.invoke(CleanableMethodHandler.java:43)
    at javax.persistence.EntityManager_$$_javassist_131.createQuery(EntityManager_$$_javassist_131.java)
    at com.mysema.query.jpa.impl.DefaultSessionHolder.createQuery(DefaultSessionHolder.java:35)
    at com.mysema.query.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:139)
    at com.mysema.query.jpa.impl.AbstractJPAQuery.createQuery(AbstractJPAQuery.java:108)
    at com.mysema.query.jpa.impl.AbstractJPAQuery.list(AbstractJPAQuery.java:276)

そしてEclipseLink 2.4で

Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.4.0.v20120608-r11652): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: org.postgresql.util.PSQLException: The SQL datatype to be used for an instance of mypackage.Tag cannot be determined. Use 'setObject()' with an explizit type, to define it.
Error Code: 0
Call: SELECT DISTINCT t0.ID, ...all the other properties...
FROM LICENSE t0, WHERE ((NOT EXISTS (SELECT ? FROM LicenseTags t5, TAG t4, TAG t3 WHERE (((t3.ID = t4.ID) AND (t3.ID IN (?,?))) AND ((t5.License_ID = t0.ID) AND (t4.ID = t5.tags_ID))))))

今のところ、次の QueryDSL 構文を使用して、これを回避しようとしました。

for (Tag tag : withTags) {
    q.where(license.tags.contains(tag));
}

for (Tag tag : withoutTags) {
    q.where(license.tags.contains(tag).not());
}

前者は魅力的に機能しますが、後者は期待した結果を返しません。タグが存在するライセンスは、本来withoutTagsあるべき結果セットから除外されません。

後者のステートメントの JPQL と SQL は次のようになります。

select distinct license
from License license
where not ?1 member of license.tags

SELECT DISTINCT t1.ID, ...all the other properties...
FROM LicenseTags t2, LICENSE t1, TAG t0
WHERE (NOT (133170 = t0.ID) AND (t2.License_ID = t1.ID) AND (t0.ID = t2.tags_ID))

JPQL は私には良さそうに見えますが、ライセンスに複数のタグが関連付けられている場合、SQL は明らかに失敗します。ですから、これは実際には EclipseLink の翻訳が失敗しているケースだと思います。これが私が使用しているバージョンの既知のバグであるかどうかを確認します。この論文は、Criteria API を使用する JPQL "NOT MEMBER OF" クエリによってある程度サポートされますが、その場合、JPQL ではなくクレテリア API を使用しているときにのみ問題が発生します。この誤った変換は EclipseLink 2.4 RC 2 でも残っています。最終的に、「タグなし」の部分に対して意図されていることを行う回避策を次に示します。

Collection<Integer> tagIds = new ArrayList<Integer>();
for (Tag tag : withoutTags) {
    tagIds.add(tag.getId());
}
q.where(license.tags.any().id.in(tagIds).not());

よろしく、 ティルマン

4

2 に答える 2

2

副選択の代わりに結合を使用できますか? これもはるかに効率的です。

これらの問題が発生しない可能性がある EclipseLink 2.4 を試すこともできます。

QueryDSL は、innerJoin() と leftJoin() の両方の結合をサポートしています。代わりにこれを使用する必要があります。

于 2012-06-19T14:58:51.043 に答える
1

現在、EclipseLink と QueryDSL には、単純明快なステートメントの使用を拒否するバグがあります。

q.where(license.tags.any().in(withTags));
q.where(license.tags.any().in(withoutTags).not());

代わりに、回避策を使用する必要があります。

Collection<Integer> withTagIds = new ArrayList<Integer>();
for (Tag tag : withTags) {
    withTagIds.add(tag.getId());
}
q.where(license.tags.any().id.in(withTagIds));

Collection<Integer> withoutTagIds = new ArrayList<Integer>();
for (Tag tag : withoutTags) {
    withoutTagIds .add(tag.getId());
}
q.where(license.tags.any().id.in(withoutTagIds ).not());
于 2012-06-25T10:50:10.837 に答える