eclipselink icon indicating copy to clipboard operation
eclipselink copied to clipboard

id(this) not accepted in SELECT and WHERE clauses

Open anija-anil opened this issue 1 year ago • 4 comments

EclipseLink is not accepting the id(this) operation, which is new in Jakarta Persistence 3.2, when it is placed within a JPQL SELECT or WHERE clause. (It's possible other clauses might not work either -- this query only tried SELECT and WHERE),

SELECT ID(THIS) FROM Prime o WHERE (o.name = :numberName OR :numeral=o.romanNumeral OR o.hex =:hex OR ID(THIS)=:num) ORDER BY o.numberId

Here is the error:

Caused by: java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager: 
Exception Description: Problem compiling [SELECT ID(THIS) FROM Prime o WHERE (o.name = :numberName OR :numeral=o.romanNumeral OR o.hex =:hex OR ID(THIS)=:num) ORDER BY o.numberId]. 
[10, 14] The identification variable 'THIS' is not defined in the FROM clause.
[108, 112] The identification variable 'THIS' is not defined in the FROM clause.
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1848)
	at org.eclipse.persistence.internal.jpa.EntityManagerImpl.createQuery(EntityManagerImpl.java:1869)
	at io.openliberty.data.internal.persistence.RepositoryImpl.invoke(RepositoryImpl.java:871)
	... 42 more
Caused by: Exception [EclipseLink-0] (Eclipse Persistence Services - 5.0.0-B02.v202404111748): org.eclipse.persistence.exceptions.JPQLException
Exception Description: Problem compiling [SELECT ID(THIS) FROM Prime o WHERE (o.name = :numberName OR :numeral=o.romanNumeral OR o.hex =:hex OR ID(THIS)=:num) ORDER BY o.numberId]. 
[10, 14] The identification variable 'THIS' is not defined in the FROM clause.
[108, 112] The identification variable 'THIS' is not defined in the FROM clause.
	at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildException(HermesParser.java:169)
	at org.eclipse.persistence.internal.jpa.jpql.HermesParser.validate(HermesParser.java:358)
	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)
	... 44 more

anija-anil avatar Jul 02 '24 06:07 anija-anil

I could recreate the issue.

Below is the Prime entity we have by referencing the entity present in Jakarta Data.

@Entity
public class Prime {
   public String binaryDigits;
   public boolean even;
   public String hex;
   public String name;

    @Id
    public long numberId;
    public String romanNumeral;
    public ArrayList<String> romanNumeralSymbols;
    public int sumOfBits;

    public static Prime of(long number, String romanNumeral, String name) {
        Prime inst = new Prime();
        inst.binaryDigits = Long.toBinaryString(number);
        inst.even = number % 2 == 0;
        inst.hex = Long.toHexString(number);
        inst.name = name;
        inst.romanNumeral = romanNumeral;
        inst.numberId = number;
        inst.sumOfBits = Long.bitCount(number);
        if (romanNumeral != null) {
            inst.romanNumeralSymbols = new ArrayList<>(romanNumeral.length());
            for (int i = 0; i < romanNumeral.length(); i++)
                inst.romanNumeralSymbols.add(romanNumeral.substring(i, i + 1));
        }

        return inst;
    }
}

Here is the code where we executed the JPQL query:

public void testOLGH28925() throws Exception {
        Prime two = Prime.of(2, "II", "two");
        Prime three = Prime.of(3, "III", "three");
        Prime five = Prime.of(5, "V", "five");
        Prime seven = Prime.of(7, "VII", "seven");

        List<Prime> primes;

        tx.begin();
        em.persist(two);
        em.persist(three);
        em.persist(five);
        em.persist(seven);
        tx.commit();

        tx.begin();
        try {
            primes = em.createQuery("SELECT ID(THIS) FROM Prime o WHERE (o.name = :numberName OR :numeral=o.romanNumeral OR o.hex =:hex OR ID(THIS)=:num) ORDER BY o.numberId",
                                    Prime.class)
                            .setParameter("numberName", "two")
                            .setParameter("numeral", "III")
                            .setParameter("hex", "5")
                            .setParameter("num", 7)
                            .getResultList();
            tx.commit();
        } catch (Exception e) {
            tx.rollback();

            throw e;
        }

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: Problem compiling [SELECT ID(THIS) FROM Prime o WHERE (o.name = :numberName OR :numeral=o.romanNumeral OR o.hex =:hex OR ID(THIS)=:num) ORDER BY o.numberId].
[10, 14] The identification variable 'THIS' is not defined in the FROM clause.
[108, 112] The identification variable 'THIS' is not defined in the FROM clause.
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.buildException(HermesParser.java:169)
at org.eclipse.persistence.internal.jpa.jpql.HermesParser.validate(HermesParser.java:358)
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)

Riva-Tholoor-Philip avatar Jul 24 '24 09:07 Riva-Tholoor-Philip

Sorry but mentioned query SELECT ID(THIS) FROM Prime o WHERE (o.name = :numberName OR :numeral=o.romanNumeral OR o.hex =:hex OR ID(THIS)=:num) ORDER BY o.numberId is incorrect. There is explicit entity alias ...FROM Prime o..... This one should not be used as there is mix of o and this. JPQL parser expect explicitly defined Prime o Correct queries are:

  • explicit o: SELECT ID(o) FROM Prime o WHERE (o.name = :numberName OR :numeral=o.romanNumeral OR o.hex =:hex OR ID(o)=:num) ORDER BY o.numberId
  • explicit this: SELECT ID(THIS) FROM Prime this WHERE (thid.name = :numberName OR :numeral=this.romanNumeral OR thiss.hex =:hex OR ID(THIS)=:num) ORDER BY this.numberId
  • implicit this: SELECT ID(THIS) FROM Prime WHERE (thid.name = :numberName OR :numeral=this.romanNumeral OR thiss.hex =:hex OR ID(THIS)=:num) ORDER BY this.numberId

rfelcman avatar Oct 07 '24 16:10 rfelcman

@rfelcman You mentioned that the query is incorrect due to the explicit entity alias “FROM Prime o” and the mixing of o with `THIS.” However, upon reviewing the Jakarta Persistence 3.2 Specification, we were unable to find a definitive statement prohibiting the use of ID(THIS) in conjunction with entity identifier variables.

Could you please provide clarification on whether this query is indeed invalid?

anija-anil avatar Oct 09 '24 12:10 anija-anil

@rfelcman You mentioned that the query is incorrect due to the explicit entity alias “FROM Prime o” and the mixing of o with `THIS.” However, upon reviewing the Jakarta Persistence 3.2 Specification, we were unable to find a definitive statement prohibiting the use of ID(THIS) in conjunction with entity identifier variables.

Could you please provide clarification on whether this query is indeed invalid?

Do You mean that two aliases identification_variable for one entity should be valid? What about 4.4. The FROM Clause and Navigational Declarations from_clause ::= FROM {this_implicit_variable | identification_variable_declarations}.

rfelcman avatar Oct 09 '24 13:10 rfelcman