JPQL SELECT with constructor args misinterprets id and version entity attributes as identification variables/operations
When omitting the optional entity identification variable for entity attributes named id and version which are arguments to a constructor in a SELECT clause, EclipseLink misinterprets these as the id(...) and version(...) operations. They should not be interpreted this way because they are not followed by a ( character.
SELECT NEW test.jakarta.data.jpa.web.Rebate(id, amount, customerId, purchaseMadeAt, purchaseMadeOn, status, updatedAt, version) FROM RebateEntity WHERE customerId=?1 AND status=test.jakarta.data.jpa.web.Rebate.Status.PAID ORDER BY amount DESC, id ASC
Error stack is:
jakarta.data.exceptions.MappingException: java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing [SELECT NEW test.jakarta.data.jpa.web.Rebate(id, amount, customerId, purchaseMadeAt, purchaseMadeOn, status, updatedAt, version) FROM RebateEntity WHERE customerId=?1 AND status=test.jakarta.data.jpa.web.Rebate.Status.PAID ORDER BY amount DESC, id ASC].
[44, 46] The identification variable 'id' cannot be a reserved word.
[119, 126] The identification variable 'version' cannot be a reserved word.
at jdk.proxy9/jdk.proxy9.$Proxy88.paidTo(Unknown Source)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at org.jboss.weld.bean.proxy.AbstractBeanInstance.invoke(AbstractBeanInstance.java:38)
at org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:109)
at test.jakarta.data.jpa.web.Rebates$1445090713$Proxy$_$$_WeldClientProxy.paidTo(Unknown Source)
at test.jakarta.data.jpa.web.DataJPATestServlet.testRecordQueryInfersSelectClause(DataJPATestServlet.java:2647)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
at componenttest.app.FATServlet.doGet(FATServlet.java:74)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:633)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:723)
Caused by: java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing [SELECT NEW test.jakarta.data.jpa.web.Rebate(id, amount, customerId, purchaseMadeAt, purchaseMadeOn, status, updatedAt, version) FROM RebateEntity WHERE customerId=?1 AND status=test.jakarta.data.jpa.web.Rebate.Status.PAID ORDER BY amount DESC, id ASC].
[44, 46] The identification variable 'id' cannot be a reserved word.
[119, 126] The identification variable 'version' cannot be a reserved word.
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1848)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1869)
Caused by: Exception [EclipseLink-0] (Eclipse Persistence Services - 5.0.0-B02.v202404111748): org.eclipse.persistence.exceptions.JPQLException
Exception Description: Syntax error parsing [SELECT NEW test.jakarta.data.jpa.web.Rebate(id, amount, customerId, purchaseMadeAt, purchaseMadeOn, status, updatedAt, version) FROM RebateEntity WHERE customerId=?1 AND status=test.jakarta.data.jpa.web.Rebate.Status.PAID ORDER BY amount DESC, id ASC].
[44, 46] The identification variable 'id' cannot be a reserved word.
[119, 126] The identification variable 'version' cannot be a reserved word.
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildException(HermesParser.java:169)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.validate(HermesParser.java:345)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.populateQueryImp(HermesParser.java:292)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildQuery(HermesParser.java:174)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:144)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:120)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:107)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:91)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1846)
Got the same error on a slightly different query
H2EclipseLinkTest$JPA>AbstractJPATest.alias:289 ? IllegalArgument An exception occurred while creating a query in EntityManager:
Exception Description: Syntax error parsing [select cat.id as id
from Cat cat].
[17, 19] The identification variable 'id' cannot be a reserved word.
Works fine on hibernate or previous version of eclipselink
We were able to recreate this issue. Below is the Rebate entity we created by referencing the entity present in Jakarta Data.
@Entity
public class Rebate {
@Id
@GeneratedValue
public Integer id;
public double amount;
public String customerId;
public LocalTime purchaseMadeAt;
public LocalDate purchaseMadeOn;
public Rebate.Status status;
public LocalDateTime updatedAt;
@Version
Integer version;
public static Rebate of(double amount, String customerId, LocalTime purchaseMadeAt, LocalDate purchaseMadeOn, Status status, LocalDateTime updatedAt, int version) {
Rebate inst = new Rebate();
inst.amount = amount;
inst.customerId = customerId;
inst.purchaseMadeAt = purchaseMadeAt;
inst.purchaseMadeOn = purchaseMadeOn;
inst.status = status;
inst.updatedAt = updatedAt;
inst.version = version;
return inst;
}
Here is the code where we executed the JPQL query:
List<Rebate> paidRebates;
tx.begin();
em.persist(r1);
em.persist(r2);
em.persist(r3);
tx.commit();
tx.begin();
try {
paidRebates = em.createQuery(
"SELECT NEW io.openliberty.jpa.data.tests.models.Rebate(id, amount, customerId, purchaseMadeAt, purchaseMadeOn, status, updatedAt, version) "
+ "FROM Rebate "
+ "WHERE customerId=?1 AND status=io.openliberty.jpa.data.tests.models.Rebate.Status.PAID "
+ "ORDER BY amount DESC, id ASC", Rebate.class)
.setParameter(1, "testOLGH28920")
.getResultList();
tx.commit();
This resulted in the following exception stack, which is the same as described in the issue:
Caused by: Exception [EclipseLink-0] (Eclipse Persistence Services - 5.0.0-B02.v202404111748): org.eclipse.persistence.exceptions.JPQLException
Exception Description: Syntax error parsing [SELECT NEW io.openliberty.jpa.data.tests.models.Rebate(id, amount, customerId, purchaseMadeAt, purchaseMadeOn, status, updatedAt, version) FROM Rebate WHERE customerId=?1 AND status=io.openliberty.jpa.data.tests.models.Rebate.Status.PAID ORDER BY amount DESC, id ASC].
[55, 57] The identification variable 'id' cannot be a reserved word.
[130, 137] The identification variable 'version' cannot be a reserved word.
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildException(HermesParser.java:169)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.validate(HermesParser.java:345)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.populateQueryImp(HermesParser.java:292)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildQuery(HermesParser.java:174)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:144)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:120)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:107)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:91)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1846)
at componenttest.topology.utils.FATServletClient.assertTestResponse(FATServletClient.java:106)
at componenttest.topology.utils.FATServletClient.runTest(FATServletClient.java:91)
at componenttest.custom.junit.runner.SyntheticServletTest.invokeExplosively(SyntheticServletTest.java:49)
at componenttest.custom.junit.runner.FATRunner$1.evaluate(FATRunner.java:204)
at componenttest.custom.junit.runner.FATRunner$2.evaluate(FATRunner.java:364)
at componenttest.custom.junit.runner.FATRunner.run(FATRunner.java:178)
at org.testcontainers.containers.FailureDetectingExternalResource$1.evaluate(FailureDetectingExternalResource.java:29)
at componenttest.rules.repeater.RepeatTests$CompositeRepeatTestActionStatement.evaluate(RepeatTests.java:145)
It is fixed in 5.0.0-B03 . In case of any issues with query like mentioned please leave a message there.
@rfelcman We tested with B03, it is still failing
E.g. there is my code which is passing:
...
private final long ID = 1;
private final String NAME = "abcde";
private final int VERSION = 11;
...
@Test
public void queryID6Test() {
EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory(PU_NAME);
assertNotNull(entityManagerFactory);
EntityManager em = entityManagerFactory.createEntityManager();
PersistenceUnitUtil puu = entityManagerFactory.getPersistenceUnitUtil();
try {
Query query = em.createQuery("SELECT NEW com.oracle.jpa.bugtest.IdVersionNewEntity(id, name, version) FROM IdVersionEntity WHERE id = :idParam ORDER BY name DESC, id ASC");
query.setParameter("idParam", ID);
IdVersionNewEntity result = (IdVersionNewEntity) query.getSingleResult();
assertEquals(ID, result.id);
assertEquals(VERSION, result.version);
assertEquals(NAME, result.name);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
} finally {
if (em.getTransaction().isActive()) {
em.getTransaction().rollback();
}
em.close();
}
}
for
package com.oracle.jpa.bugtest;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
@Entity
@Table(name = "TEST_TAB_IDVERSION")
public class IdVersionEntity {
private long id;
private String name;
private int version;
protected IdVersionEntity() {
}
@Id
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Version
public int getVersion() {
return version;
}
public void setVersion(int version) {
this.version = version;
}
}
and
package com.oracle.jpa.bugtest;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Version;
@Entity
public class IdVersionNewEntity {
@Id
public long id;
public String name;
@Version
public int version;
protected IdVersionNewEntity() {
}
public IdVersionNewEntity(long id, String name, int version) {
this.id = id;
this.name = name;
this.version = version;
}
}
@rfelcman Please find our test case below :
@Test
// @Ignore("Reference issue: https://github.com/OpenLiberty/open-liberty/issues/28920")
public void testOLGH28920() throws Exception {
Rebate r1 = Rebate.of(10.00, "testOLGH28920", LocalTime.now().minusHours(1), LocalDate.now(), Status.SUBMITTED,
LocalDateTime.now(), 1);
Rebate r2 = Rebate.of(12.00, "testOLGH28920", LocalTime.now().minusHours(1), LocalDate.now(), Status.PAID,
LocalDateTime.now(), 2);
Rebate r3 = Rebate.of(14.00, "testOLGH28920", LocalTime.now().minusHours(1), LocalDate.now(), Status.PAID,
LocalDateTime.now(), 2);
List<Rebate> paidRebates;
tx.begin();
em.persist(r1);
em.persist(r2);
em.persist(r3);
tx.commit();
tx.begin();
try {
paidRebates = em.createQuery(
"SELECT NEW io.openliberty.jpa.data.tests.models.Rebate(id, amount, customerId, purchaseMadeAt, purchaseMadeOn, status, updatedAt, version) "
+ "FROM Rebate "
+ "WHERE customerId=?1 AND status=io.openliberty.jpa.data.tests.models.Rebate.Status.PAID "
+ "ORDER BY amount DESC, id ASC",
Rebate.class)
.setParameter(1, "testOLGH28920")
.getResultList();
tx.commit();
} catch (Exception e) {
tx.rollback();
throw e;
}
assertEquals(2, paidRebates.size());
assertEquals(14.00, paidRebates.get(0).amount, 0.001);
}
Model class:
@Entity
public class Rebate {
@Id
@GeneratedValue
public Integer id;
public double amount;
public String customerId;
public LocalTime purchaseMadeAt;
public LocalDate purchaseMadeOn;
public Rebate.Status status;
public LocalDateTime updatedAt;
@Version
Integer version;
public static Rebate of(double amount, String customerId, LocalTime purchaseMadeAt, LocalDate purchaseMadeOn, Status status, LocalDateTime updatedAt, int version) {
Rebate inst = new Rebate();
inst.amount = amount;
inst.customerId = customerId;
inst.purchaseMadeAt = purchaseMadeAt;
inst.purchaseMadeOn = purchaseMadeOn;
inst.status = status;
inst.updatedAt = updatedAt;
inst.version = version;
return inst;
}
public static enum Status {
DENIED, SUBMITTED, VERIFIED, PAID
}
}
@rfelcman The exception I'm getting now is different from the issue description.
junit.framework.AssertionFailedError: 2024-09-18-21:51:00:901 ERROR: Caught exception attempting to call test method testOLGH28920 on servlet io.openliberty.jpa.data.tests.web.JakartaDataRecreateServlet
Local Exception Stack:
Exception [EclipseLink-6076] (Eclipse Persistence Services - 5.0.0-B03.v202409121024-4a7149f0cd04d7466837d70f68abb743c88acb83): org.eclipse.persistence.exceptions.QueryException
Exception Description: Object comparisons can only be used with OneToOneMappings. Other mapping comparisons must be done through query keys or direct attribute level comparisons.
Mapping: [org.eclipse.persistence.mappings.DirectToFieldMapping[status-->REBATE.STATUS]]
Expression: [
Query Key status
Base io.openliberty.jpa.data.tests.models.Rebate]
Query: ReportQuery(referenceClass=Rebate jpql="SELECT NEW io.openliberty.jpa.data.tests.models.Rebate(id, amount, customerId, purchaseMadeAt, purchaseMadeOn, status, updatedAt, version) FROM Rebate WHERE customerId=?1 AND status=io.openliberty.jpa.data.tests.models.Rebate.Status.PAID ORDER BY amount DESC, id ASC")
at org.eclipse.persistence.exceptions.QueryException.unsupportedMappingForObjectComparison(QueryException.java:1184)
at org.eclipse.persistence.mappings.DatabaseMapping.buildObjectJoinExpression(DatabaseMapping.java:323)
at org.eclipse.persistence.internal.expressions.RelationExpression.normalize(RelationExpression.java:846)
at org.eclipse.persistence.internal.expressions.CompoundExpression.normalize(CompoundExpression.java:257)
at org.eclipse.persistence.internal.expressions.SQLSelectStatement.normalize(SQLSelectStatement.java:1544)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.buildReportQuerySelectStatement(ExpressionQueryMechanism.java:694)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.buildReportQuerySelectStatement(ExpressionQueryMechanism.java:648)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.prepareReportQuerySelectAllRows(ExpressionQueryMechanism.java:1755)
at org.eclipse.persistence.queries.ReportQuery.prepareSelectAllRows(ReportQuery.java:1291)
at org.eclipse.persistence.queries.ReadAllQuery.prepare(ReadAllQuery.java:841)
at org.eclipse.persistence.queries.ReportQuery.prepare(ReportQuery.java:1160)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:696)
at org.eclipse.persistence.queries.ObjectLevelReadQuery.checkPrepare(ObjectLevelReadQuery.java:1031)
at org.eclipse.persistence.queries.DatabaseQuery.checkPrepare(DatabaseQuery.java:645)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:196)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.buildEJBQLDatabaseQuery(EJBQueryImpl.java:120)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:107)
at org.eclipse.persistence.internal.jpa.EJBQueryImpl.<init>(EJBQueryImpl.java:91)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1846)
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1869)
at com.ibm.ws.jpa.management.JPATxEmInvocation.createQuery(JPATxEmInvocation.java:354)
at com.ibm.ws.jpa.management.JPAEntityManager.createQuery(JPAEntityManager.java:596)
at io.openliberty.jpa.data.tests.web.JakartaDataRecreateServlet.testOLGH28920(JakartaDataRecreateServlet.java:300)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at componenttest.app.FATServlet.doGet(FATServlet.java:80)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:633)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:723)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1266)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:754)
at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:451)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1362)
at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1078)
at com.ibm.ws.webcontainer.servlet.CacheServletWrapper.handleRequest(CacheServletWrapper.java:77)
at com.ibm.ws.webcontainer40.servlet.CacheServletWrapper40.handleRequest(CacheServletWrapper40.java:87)
at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:978)
at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost$2.run(DynamicVirtualHost.java:293)
at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink$TaskWrapper.run(HttpDispatcherLink.java:1260)
at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.wrapHandlerAndExecute(HttpDispatcherLink.java:476)
at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink.ready(HttpDispatcherLink.java:435)
at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleDiscrimination(HttpInboundLink.java:569)
at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.handleNewRequest(HttpInboundLink.java:503)
at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.processRequest(HttpInboundLink.java:363)
at com.ibm.ws.http.channel.internal.inbound.HttpInboundLink.ready(HttpInboundLink.java:330)
at com.ibm.ws.tcpchannel.internal.NewConnectionInitialReadCallback.sendToDiscriminators(NewConnectionInitialReadCallback.java:169)
at com.ibm.ws.tcpchannel.internal.NewConnectionInitialReadCallback.complete(NewConnectionInitialReadCallback.java:77)
at com.ibm.ws.tcpchannel.internal.WorkQueueManager.requestComplete(WorkQueueManager.java:516)
at com.ibm.ws.tcpchannel.internal.WorkQueueManager.attemptIO(WorkQueueManager.java:586)
at com.ibm.ws.tcpchannel.internal.WorkQueueManager.workerRun(WorkQueueManager.java:970)
at com.ibm.ws.tcpchannel.internal.WorkQueueManager$Worker.run(WorkQueueManager.java:1059)
at com.ibm.ws.threading.internal.ExecutorServiceImpl$RunnableWrapper.run(ExecutorServiceImpl.java:298)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:857)
Seems like similar exception is already raised on another Eclipselink bug here : https://github.com/eclipse-ee4j/eclipselink/issues/2185
Closing this issue as it is duplicated with #2185 .