| Summary: | JPA query - NOT MEMBER OF exception (ClassCastException) | ||||||
|---|---|---|---|---|---|---|---|
| Product: | z_Archived | Reporter: | Dennis Fuglsang <dffuglsang> | ||||
| Component: | Eclipselink | Assignee: | Nobody - feel free to take it <nobody> | ||||
| Status: | CLOSED FIXED | QA Contact: | |||||
| Severity: | normal | ||||||
| Priority: | P2 | CC: | andrei.ilitchev, dffuglsang, martin.grebac, tom.ware | ||||
| Version: | unspecified | ||||||
| Target Milestone: | --- | ||||||
| Hardware: | All | ||||||
| OS: | Linux | ||||||
| Whiteboard: | |||||||
| Attachments: |
|
||||||
Setting target and priority. See the following page for the meanings of these fields: http://wiki.eclipse.org/EclipseLink/Development/Bugs/Guidelines Community: Please vote for this bug if it is important to you. Votes are one of the main criteria we use to determine which bugs to fix next. I reproduced SELECT a FROM Zinstance a WHERE ?1 NOT MEMBER OF a.groupUUIDs <-Fail CCE that's a bug. To workaround use: SELECT e FROM Zinstance a WHERE NOT EXISTS (SELECT g FROM a.groupUUIDs g WHERE g = ?1) The following jpql is illegal: SELECT a FROM Zinstance a WHERE a.groupUUIDs = ?1 SELECT a FROM Zinstance a WHERE ?1 LIKE a.groupUUIDs SELECT a FROM Zinstance a WHERE ?1 NOT LIKE a.groupUUIDs as per spec.: 4.6.3 Path Expressions It is illegal to use a collection_valued_path_expression within a WHERE or HAVING clause as part of a conditional expression except in an empty_collection_comparison_expression, in a collection_member_expression, or as an argument to the SIZE operator. Created attachment 228155 [details]
Suggested patch.
The patch idea was suggested by James.
The original code didn't work because the target of DirectCollection has no descriptor.
"SELECT e FROM Employee e WHERE 'a' NOT MEMBER OF e.responsibilities"
Function operator [NOT EXISTS , ]
SubSelect ReportQuery()
Logical operator [ AND ]
Relation operator [ = ]
Base QUERY OBJECT
Query Key responsibilities
Base org.eclipse.persistence.testing.models.jpa.advanced.Employee
Relation operator [ = ]
Base QUERY OBJECT
Constant a
To accommodate this the code was altered so that sub query is always against the same type as the parent expression:
Function operator [NOT EXISTS , ]
SubSelect ReportQuery()
Logical operator [ AND ]
Relation operator [ = ]
Base QUERY OBJECT
Base org.eclipse.persistence.testing.models.jpa.advanced.Employee
Relation operator [ = ]
Query Key responsibilities
Base QUERY OBJECT
Constant a
Expression.noneOf method could achieve that, but the code is very similar.
Also added support for nested collection path (project.teamLeader.responsibilities).
The following tests were added to JUnitJPQLSimpleTestSuite:
selectSimpleNotMemberOfWithParameterNestedTest;
selectDirectCollectionNotMemberTest;
selectDirectCollectionNonMemberNestedTest
Correction: "Expression.noneOf method could NOT achieve that.." Checked the patch into 2.4.2, trunk is pending. Checked the patch into trunk (2.5). The Eclipselink project has moved to Github: https://github.com/eclipse-ee4j/eclipselink |
Build Identifier: 2.3.1 I have a very simple test case involving a single Java class, two database tables, a basic collection mapping and JPA queries. The SQL for the DB schema is shown below CREATE TABLE Z_INSTANCES ( INSTANCE_UUID CHAR(36) NOT NULL , NAME VARCHAR2(256) , CONSTRAINT Z_INSTANCES_PK PRIMARY KEY (INSTANCE_UUID) using index tablespace &INDEX_TABLESPACE ENABLE) tablespace &DATA_TABLESPACE; CREATE TABLE Z_GROUPS( INSTANCE_UUID CHAR(36) NOT NULL , GROUP_UUID CHAR(36) NOT NULL , CONSTRAINT GROUPS_UK1 UNIQUE (INSTANCE_UUID,GROUP_UUID) using index tablespace &INDEX_TABLESPACE ENABLE , CONSTRAINT Z_INSTANCES_FK1 FOREIGN KEY (INSTANCE_UUID) REFERENCES Z_INSTANCES(INSTANCE_UUID) ON DELETE CASCADE ENABLE) tablespace &DATA_TABLESPACE; The Java class is very simple public class Zinstance { private String uuid; private String name; private Set<String> groupUUIDs; public Zinstance() { this.uuid = UUID.randomUUID().toString(); this.groupUUIDs = new HashSet<String>(); } } The OR mapping is shown below: <entity class="my.temp.Zinstance"> <table name="Z_INSTANCES" /> <attributes> <id name="uuid"> <column name="INSTANCE_UUID" updatable="false" /> </id> <basic name="name"> <column name="NAME" /> </basic> <basic-collection name="groupUUIDs"> <value-column name="GROUP_UUID" /> <collection-table name="Z_GROUPS"> <primary-key-join-column>INSTANCE_UUID</primary-key-join-column> </collection-table> <private-owned /> <join-fetch>OUTER</join-fetch> </basic-collection> </attributes> </entity> I attempted the following JPA queries SELECT a FROM Zinstance a <-Success SELECT a FROM Zinstance a WHERE a.groupUUIDs = ?1 <-Success SELECT a FROM Zinstance a WHERE ?1 LIKE a.groupUUIDs <-Success SELECT a FROM Zinstance a WHERE ?1 NOT LIKE a.groupUUIDs <-Fail incorrect results SELECT a FROM Zinstance a WHERE a.groupUUIDs IS EMPTY <-Success SELECT a FROM Zinstance a WHERE a.groupUUIDs IS NOT EMPTY <-Success SELECT a FROM Zinstance a WHERE ?1 MEMBER OF a.groupUUIDs <-Success SELECT a FROM Zinstance a WHERE ?1 NOT MEMBER OF a.groupUUIDs <-Fail CCE I am using EclipseLink 2.3.1. Is the ClassCastException for the NOT MEMBER OF a bug? Is there an alternative syntax for this type of query? Exception [EclipseLink-6168] (Eclipse Persistence Services - 2.3.1.v20111018-r10243): org.eclipse.persistence.exceptions.QueryException Exception Description: Query failed to prepare, unexpected error occurred: [java.lang.ClassCastException: java.lang.String cannot be cast to org.eclipse.persistence.internal.descriptors.PersistenceObject]. Internal Exception: java.lang.ClassCastException: java.lang.String cannot be cast to org.eclipse.persistence.internal.descriptors.PersistenceObject Query: ReadAllQuery(referenceClass=Zinstance jpql="SELECT a FROM Zinstance a WHERE 'b54f6d66-07fe-4d04-97f6-acff6722236a' NOT MEMBER OF a.groupUUIDs") at org.eclipse.persistence.exceptions.QueryException.prepareFailed(QueryException.java:1555) at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:625) at org.eclipse.persistence.queries.ObjectLevelReadQuery.checkPrepare(ObjectLevelReadQuery.java:823) at org.eclipse.persistence.queries.DatabaseQuery.prepareCall(DatabaseQuery.java:1741) at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:268) at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:190) at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:142) at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:126) at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1475) at oracle.oer.orc.persistence.temp.BasicCollectionIntegTest.query(BasicCollectionIntegTest.java:131) Reproducible: Always Steps to Reproduce: 1. Create single java class with collection attribute 2. Create two DB tables to hold instance of Java class and values of collection 3. Use basic collection mapping 4. Execute JPA query using NOT MEMBER OF operator