eclipselink icon indicating copy to clipboard operation
eclipselink copied to clipboard

JPQL SELECT with constructor args misinterprets id and version entity attributes as identification variables/operations

Open Riva-Tholoor-Philip opened this issue 1 year ago • 1 comments

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) 

Riva-Tholoor-Philip avatar Jul 02 '24 06:07 Riva-Tholoor-Philip

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

velo avatar Jul 04 '24 13:07 velo

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)

anija-anil avatar Jul 24 '24 08:07 anija-anil

It is fixed in 5.0.0-B03 . In case of any issues with query like mentioned please leave a message there.

rfelcman avatar Sep 18 '24 11:09 rfelcman

@rfelcman We tested with B03, it is still failing

ajaypaul-ibm avatar Sep 18 '24 12:09 ajaypaul-ibm

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 avatar Sep 18 '24 13:09 rfelcman

@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
    }
}

ajaypaul-ibm avatar Sep 18 '24 13:09 ajaypaul-ibm

@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

ajaypaul-ibm avatar Sep 18 '24 16:09 ajaypaul-ibm

Closing this issue as it is duplicated with #2185 .

rfelcman avatar Sep 19 '24 07:09 rfelcman