From e9bdec3a63110e4aeda5861f8b7af54f5f6132f7 Mon Sep 17 00:00:00 2001 From: Radek Felcman Date: Tue, 24 Sep 2024 10:58:23 +0200 Subject: [PATCH] Enum attribute in JPQL is broken when entity identifier is omitted or is 'this.' - bugfix and unit tests Signed-off-by: Radek Felcman --- .../jpa/advanced/AdvancedTableCreator.java | 14 ++++++-- .../testing/models/jpa/advanced/Room.java | 26 ++++++++++++++ .../JUnitJPQLJakartaDataNoAliasTest.java | 26 +++++++++++--- .../jpql/parser/AbstractPathExpression.java | 2 +- .../parser/JPQLExpressionTestJakartaData.java | 36 +++++++++++++++++++ 5 files changed, 96 insertions(+), 8 deletions(-) diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/AdvancedTableCreator.java b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/AdvancedTableCreator.java index e7aec71acfa..6cd2f7c453f 100644 --- a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/AdvancedTableCreator.java +++ b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/AdvancedTableCreator.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved. - * Copyright (c) 1998, 2022 IBM Corporation. All rights reserved. + * Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2024 IBM Corporation. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at @@ -2656,6 +2656,16 @@ public TableDefinition buildCMP3_ROOMTable() { fieldHEIGHT.setShouldAllowNull(true); table.addField(fieldHEIGHT); + FieldDefinition fieldSTATUS = new FieldDefinition(); + fieldSTATUS.setName("STATUS"); + fieldSTATUS.setTypeName("VARCHAR"); + fieldSTATUS.setSize(32); + fieldSTATUS.setIsPrimaryKey(false); + fieldSTATUS.setIsIdentity(false); + fieldSTATUS.setUnique(false); + fieldSTATUS.setShouldAllowNull(true); + table.addField(fieldSTATUS); + return table; } diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Room.java b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Room.java index 82f4467e82a..61b7c786c69 100644 --- a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Room.java +++ b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/Room.java @@ -43,15 +43,33 @@ }) }) public class Room implements Serializable, Cloneable { + + public enum Status { + FREE, OCCUPIED; + } + @Id private int id; private int width; private int length; private int height; + private Status status; @OneToMany(mappedBy="room", cascade=CascadeType.ALL, fetch=FetchType.LAZY) private Collection doors; + public Room() { + } + + public Room(int id, int width, int length, int height, Status status) { + this.id = id; + this.width = width; + this.length = length; + this.height = height; + this.status = status; + this.doors = doors; + } + public int getId() { return id; } @@ -84,6 +102,14 @@ public void setHeight(int height) { this.height = height; } + public Status getStatus() { + return status; + } + + public void setStatus(Status status) { + this.status = status; + } + public Collection getDoors() { return doors; } diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/test/java/org/eclipse/persistence/testing/tests/jpa/jpql/advanced/JUnitJPQLJakartaDataNoAliasTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/test/java/org/eclipse/persistence/testing/tests/jpa/jpql/advanced/JUnitJPQLJakartaDataNoAliasTest.java index 5a0bf3d1083..9f3d05cf940 100644 --- a/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/test/java/org/eclipse/persistence/testing/tests/jpa/jpql/advanced/JUnitJPQLJakartaDataNoAliasTest.java +++ b/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/test/java/org/eclipse/persistence/testing/tests/jpa/jpql/advanced/JUnitJPQLJakartaDataNoAliasTest.java @@ -55,10 +55,10 @@ public class JUnitJPQLJakartaDataNoAliasTest extends JUnitTestCase { private static final String STRING_DATA_LIKE_EXPRESSION = "A%"; // should match STRING_DATA private static final Room[] ROOMS = new Room[]{ null, // Skip array index 0 - aRoom(1, 1, 1, 1), - aRoom(2, 1, 1, 1), - aRoom(3, 1, 1, 1), - aRoom(4, 1, 1, 1) + aRoom(1, 1, 1, 1, Room.Status.FREE), + aRoom(2, 1, 1, 1, Room.Status.FREE), + aRoom(3, 1, 1, 1, Room.Status.OCCUPIED), + aRoom(4, 1, 1, 1, Room.Status.OCCUPIED) }; private static final long ROOMS_COUNT = ROOMS.length - 1; // we ignore the first one with index 0 @@ -110,6 +110,7 @@ public static Test suite() { suite.addTest(new JUnitJPQLJakartaDataNoAliasTest("testSelectQueryImplicitThisVariableInPath")); suite.addTest(new JUnitJPQLJakartaDataNoAliasTest("testSelectQueryImplicitThisVariableInAggregateFunctionPath")); suite.addTest(new JUnitJPQLJakartaDataNoAliasTest("testSelectQueryImplicitThisVariableInArithmeticExpression")); + suite.addTest(new JUnitJPQLJakartaDataNoAliasTest("testSelectQueryImplicitThisVariableAndEnum")); suite.addTest(new JUnitJPQLJakartaDataNoAliasTest("testUpdateImplicitVariableInArithmeticExpression")); suite.addTest(new JUnitJPQLJakartaDataNoAliasTest("testDeleteQueryLengthInExpressionOnLeft")); suite.addTest(new JUnitJPQLJakartaDataNoAliasTest("testDeleteQueryLengthInExpressionOnRight")); @@ -367,6 +368,20 @@ public void testSelectQueryImplicitThisVariableInArithmeticExpression() { assertEquals(ROOMS[1].getLength() * ROOMS[1].getWidth() * ROOMS[1].getHeight(), roomCapacity); } + // Covers https://github.com/eclipse-ee4j/eclipselink/issues/2185 + public void testSelectQueryImplicitThisVariableAndEnum() { + resetRooms(); + List rooms = getEntityManagerFactory().callInTransaction(em -> em.createQuery( + "SELECT NEW org.eclipse.persistence.testing.models.jpa.advanced.Room(id, width, length, height, status) " + + "FROM Room " + + "WHERE id = :idParam AND status=org.eclipse.persistence.testing.models.jpa.advanced.Room.Status.FREE " + + "ORDER BY width DESC, id ASC", + Room.class) + .setParameter("idParam", ROOMS[1].getId()) + .getResultList()); + assertEquals(ROOMS[1], rooms.get(0)); + } + public void testUpdateImplicitVariableInArithmeticExpression() { resetRooms(); int numberOfChanges = getEntityManagerFactory().callInTransaction(em -> em.createQuery( @@ -466,12 +481,13 @@ private Room findRoomById(int i) { }); } - private static Room aRoom(int id, int width, int length, int height) { + private static Room aRoom(int id, int width, int length, int height, Room.Status status) { Room room = new Room(); room.setId(id); room.setWidth(width); room.setLength(length); room.setHeight(height); + room.setStatus(status); return room; } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractPathExpression.java b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractPathExpression.java index 3da402d699c..9ff9e5af942 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractPathExpression.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/main/java/org/eclipse/persistence/jpa/jpql/parser/AbstractPathExpression.java @@ -212,7 +212,7 @@ else if (!identificationVariable.isNull() && identificationVariable = buildNullExpression(); } else { - identificationVariable = new IdentificationVariable(this, paths.get(0)); + identificationVariable = new IdentificationVariable(this, paths.get(0), false); } } } diff --git a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLExpressionTestJakartaData.java b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLExpressionTestJakartaData.java index 1b9c92f6137..f31bb4c990a 100644 --- a/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLExpressionTestJakartaData.java +++ b/jpa/org.eclipse.persistence.jpa.jpql/src/test/java/org/eclipse/persistence/jpa/tests/jpql/parser/JPQLExpressionTestJakartaData.java @@ -25,7 +25,11 @@ import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.inputParameter; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.isNotNull; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.max; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.new_; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.numeric; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.orderBy; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.orderByItemAsc; +import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.orderByItemDesc; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.path; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.select; import static org.eclipse.persistence.jpa.tests.jpql.parser.JPQLParserTester.selectStatement; @@ -238,6 +242,38 @@ public void testFunctionNameAsImplicitStateFieldInSelectArithmeticExpression() { testJakartaDataQuery(inputJPQLQuery, selectStatement); } + // Covers https://github.com/eclipse-ee4j/eclipselink/issues/2185 + @Test + public void testSelectWithImplicitThisAliasAndEnum() { + + String inputJPQLQuery = "SELECT NEW com.oracle.jpa.bugtest.Rebate(id, amount, customerId, purchaseMadeAt, purchaseMadeOn, status, updatedAt, version) " + + "FROM Rebate " + + "WHERE customerId=:customerIdParam AND status=com.oracle.jpa.bugtest.Rebate.Status.PAID " + + "ORDER BY amount DESC, id ASC"; + + SelectStatementTester selectStatement = selectStatement( + select( + new_( + "com.oracle.jpa.bugtest.Rebate", + virtualVariable("this", "id"), + virtualVariable("this", "amount"), + virtualVariable("this", "customerId"), + virtualVariable("this", "purchaseMadeAt"), + virtualVariable("this", "purchaseMadeOn"), + virtualVariable("this", "status"), + virtualVariable("this", "updatedAt"), + virtualVariable("this", "version") + ) + ), + from("Rebate", "{this}"), + where(and(equal(virtualVariable("this", "customerId"), inputParameter(":customerIdParam")), + equal(virtualVariable("this", "status"), path("com.oracle.jpa.bugtest.Rebate.Status.PAID")))), + orderBy(orderByItemDesc(virtualVariable("this", "amount")), orderByItemAsc(virtualVariable("this", "id"))) + ); + + testJakartaDataQuery(inputJPQLQuery, selectStatement); + } + @Test public void testUpdateFunctionNameAsImplicitStateFieldInNumericExpression() {