Open Riva-Tholoor-Philip opened 2 months ago
I could recreate the issue.
Below is the NaturalNumber entity we have by referencing the entity present in Jakarta Data.
@jakarta.persistence.Entity
public class NaturalNumber implements Serializable {
private static final long serialVersionUID = 1L;
public enum NumberType {
ONE, PRIME, COMPOSITE
}
@jakarta.persistence.Id
private long id; //AKA the value
private boolean isOdd;
private Short numBitsRequired;
private NumberType numType; // enum of ONE | PRIME | COMPOSITE
private long floorOfSquareRoot;
public static NaturalNumber of(int value) {
boolean isOne = value == 1;
boolean isOdd = value % 2 == 1;
long sqrRoot = squareRoot(value);
boolean isPrime = isOdd ? isPrime(value, sqrRoot) : (value == 2);
NaturalNumber inst = new NaturalNumber();
inst.id = value;
inst.isOdd = isOdd;
inst.numBitsRequired = bitsRequired(value);
inst.numType = isOne ? NumberType.ONE : isPrime ? NumberType.PRIME : NumberType.COMPOSITE;
inst.floorOfSquareRoot = sqrRoot;
return inst;
}
private static Short bitsRequired(int value) {
return (short) (Math.floor(Math.log(value) / Math.log(2)) + 1);
}
private static long squareRoot(int value) {
return (long) Math.floor(Math.sqrt(value));
}
private static boolean isPrime(int value, long largestPossibleFactor) {
if (value == 1)
return false;
for (int i = 2; i <= largestPossibleFactor; i++) {
if (value % i == 0)
return false;
}
return true;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public boolean isOdd() {
return isOdd;
}
public void setOdd(boolean isOdd) {
this.isOdd = isOdd;
}
public Short getNumBitsRequired() {
return numBitsRequired;
}
public void setNumBitsRequired(Short numBitsRequired) {
this.numBitsRequired = numBitsRequired;
}
public NumberType getNumType() {
return numType;
}
public void setNumType(NumberType numType) {
this.numType = numType;
}
public long getFloorOfSquareRoot() {
return floorOfSquareRoot;
}
public void setFloorOfSquareRoot(long floorOfSquareRoot) {
this.floorOfSquareRoot = floorOfSquareRoot;
}
}
Here is the code where we executed the JPQL query:
public void testOLGH28874() throws Exception {
NaturalNumber two = NaturalNumber.of(2);
NaturalNumber three = NaturalNumber.of(3);
NaturalNumber result1 = null, result2 = null;
List<Exception> exceptions = new ArrayList<>();
tx.begin();
em.persist(two);
em.persist(three);
tx.commit();
tx.begin();
try {
result1 = em.createQuery("FROM NaturalNumber WHERE isOdd = false AND numType = io.openliberty.jpa.data.tests.models.NaturalNumber.NumberType.PRIME",
NaturalNumber.class)
.getSingleResult();
tx.commit();
} catch (Exception e) {
tx.rollback();
exceptions.add(e);
}
tx.begin();
try {
result2 = em.createQuery("FROM NaturalNumber WHERE this.isOdd = false AND this.numType = io.openliberty.jpa.data.tests.models.NaturalNumber.NumberType.PRIME",
NaturalNumber.class)
.getSingleResult();
tx.commit();
} catch (Exception e) {
tx.rollback();
exceptions.add(e);
}
if (!exceptions.isEmpty()) {
throw exceptions.get(0);
}
This resulted in the following exception stack, which is the same as described in the issue:
junit.framework.AssertionFailedError: 2024-07-24-17:35:14:205 ERROR: Caught exception attempting to call test method testOLGH28874 on servlet io.openliberty.jpa.data.tests.web.JakartaDataRecreateServlet
Local Exception Stack:
Exception [EclipseLink-6076] (Eclipse Persistence Services - 5.0.0-B02.v202404111748): 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[numType-->NATURALNUMBER.NUMTYPE]]
Expression: [
Query Key numType
Base io.openliberty.jpa.data.tests.models.NaturalNumber]
Query: ReadAllQuery(referenceClass=NaturalNumber jpql="FROM NaturalNumber WHERE isOdd = false AND numType = io.openliberty.jpa.data.tests.models.NaturalNumber.NumberType.PRIME")
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.buildNormalSelectStatement(ExpressionQueryMechanism.java:611)
at org.eclipse.persistence.internal.queries.ExpressionQueryMechanism.prepareSelectAllRows(ExpressionQueryMechanism.java:1783)
at org.eclipse.persistence.queries.ReadAllQuery.prepareSelectAllRows(ReadAllQuery.java:910)
at org.eclipse.persistence.queries.ReadAllQuery.prepare(ReadAllQuery.java:841)
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 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)
JPQL that omits the optional entity identifier variable or uses
this
. fails when the entity attribute is an enum type.For example, from https://github.com/jakartaee/data/blob/77ff9f15920cacd4b51818260ddd4cfe41accebb/tck/src/main/java/ee/jakarta/tck/data/framework/read/only/NaturalNumber.java
which has,
when the following JPQL is used,
or the following JPQL is used (including this.),
FROM NaturalNumber WHERE this.isOdd = false AND this.numType = ee.jakarta.tck.data.framework.read.only.NaturalNumber.NumberType.PRIME
it fails with this error: