diff --git a/foundation/org.eclipse.persistence.core.test.framework/src/main/java/org/eclipse/persistence/testing/framework/TogglingFastTableCreator.java b/foundation/org.eclipse.persistence.core.test.framework/src/main/java/org/eclipse/persistence/testing/framework/TogglingFastTableCreator.java index 3902983d29d..4c0ec270dc9 100644 --- a/foundation/org.eclipse.persistence.core.test.framework/src/main/java/org/eclipse/persistence/testing/framework/TogglingFastTableCreator.java +++ b/foundation/org.eclipse.persistence.core.test.framework/src/main/java/org/eclipse/persistence/testing/framework/TogglingFastTableCreator.java @@ -241,15 +241,17 @@ protected static FieldDefinition createNumericFk( * with given name and size and without any additional constraints. * @param name Column name. * @param size Column numeric type size. + * @param subSize Column numeric type sub size. * @param allowNull Allow {@code null} values for column. * @return Initialized {@link FieldDefinition} instance. */ protected static FieldDefinition createNumericColumn( - final String name, final int size, final boolean allowNull) { + final String name, final int size, final int subSize, final boolean allowNull) { final FieldDefinition field = new FieldDefinition(); field.setName(name); field.setTypeName("NUMERIC"); field.setSize(size); + field.setSubSize(subSize); field.setShouldAllowNull(allowNull); field.setIsPrimaryKey(false); field.setUnique(false); @@ -257,6 +259,19 @@ protected static FieldDefinition createNumericColumn( return field; } + /** + * Helper method to create {@link FieldDefinition} instance for numeric column + * with given name and size and without any additional constraints. + * @param name Column name. + * @param size Column numeric type size. + * @param allowNull Allow {@code null} values for column. + * @return Initialized {@link FieldDefinition} instance. + */ + protected static FieldDefinition createNumericColumn( + final String name, final int size, final boolean allowNull) { + return createNumericColumn(name, size, 0, allowNull); + } + /** * Helper method to create {@link FieldDefinition} instance for numeric column * with given name, size of {@code 15}, with {@code null} value allowed and @@ -269,6 +284,18 @@ protected static FieldDefinition createNumericColumn( return createNumericColumn(name, 15, true); } + /** + * Helper method to create {@link FieldDefinition} instance for numeric column + * with given name to store float or double type values. + * @param name Column name. + * @param allowNull Allow {@code null} values for column. + * @return Initialized {@link FieldDefinition} instance. + */ + protected static FieldDefinition createDoubleColumn( + final String name, final boolean allowNull) { + return createNumericColumn(name, 19, 4, allowNull); + } + /** * Helper method to create {@link FieldDefinition} instance for DTYPE * column used for inheritance in model. @@ -317,7 +344,7 @@ protected static FieldDefinition createStringColumn( */ protected static FieldDefinition createStringColumn( final String name) { - return createStringColumn(name, 32, true); + return createStringColumn(name, 255, true); } /** diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/OraclePlatform.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/OraclePlatform.java index f4199e82ad5..f71c846bc43 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/OraclePlatform.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/platform/database/OraclePlatform.java @@ -1077,8 +1077,16 @@ public void printSQLSelectStatement(DatabaseCall call, ExpressionSQLPrinter prin printer.printString(primaryKeyFields); printer.printString(FROM_ID); printer.printString(queryString); - printer.printString(ORDER_BY_ID); - printer.printString(primaryKeyFields); + if (statement.hasOrderByExpressions()) { + try { + statement.printSQLOrderByClause(printer); + } catch (IOException exception) { + throw ValidationException.fileError(exception); + } + } else { + printer.printString(ORDER_BY_ID); + printer.printString(primaryKeyFields); + } printer.printString(END_FROM_ID); printer.printString(MAX_ROW); printer.printParameter(DatabaseCall.MAXROW_FIELD); 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..db813339b8b 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 @@ -37,6 +37,7 @@ import org.eclipse.persistence.logging.SessionLogEntry; import org.eclipse.persistence.sessions.DatabaseSession; import org.eclipse.persistence.testing.framework.TogglingFastTableCreator; +import org.eclipse.persistence.testing.models.jpa.advanced.entities.EntityFloat; import org.eclipse.persistence.tools.schemaframework.FieldDefinition; import org.eclipse.persistence.tools.schemaframework.ForeignKeyConstraint; import org.eclipse.persistence.tools.schemaframework.TableDefinition; @@ -130,6 +131,7 @@ public AdvancedTableCreator() { addTableDefinition(buildPlanarbeitsgangTable()); addTableDefinition(buildPlanarbeitsgangHistTable()); addTableDefinition(buildMaterialReignisTable()); + addTableDefinition(EntityFloat.Populator.buildTable()); } public TableDefinition buildADDRESSTable() { @@ -3639,7 +3641,7 @@ public TableDefinition buildMaterialReignisTable() { return tabledefinition; } - + @Override public void replaceTables(DatabaseSession session) { DatabasePlatform dbPlatform = session.getPlatform(); diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/entities/EntityFloat.java b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/entities/EntityFloat.java new file mode 100644 index 00000000000..7a39f7911a4 --- /dev/null +++ b/jpa/eclipselink.jpa.testapps/jpa.test.advanced/src/main/java/org/eclipse/persistence/testing/models/jpa/advanced/entities/EntityFloat.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 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 + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +package org.eclipse.persistence.testing.models.jpa.advanced.entities; + +import java.util.Arrays; +import java.util.List; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import org.eclipse.persistence.sessions.Session; +import org.eclipse.persistence.sessions.UnitOfWork; +import org.eclipse.persistence.testing.framework.TogglingFastTableCreator; +import org.eclipse.persistence.testing.models.jpa.advanced.Employee; +import org.eclipse.persistence.tools.schemaframework.PopulationManager; +import org.eclipse.persistence.tools.schemaframework.TableDefinition; + +// Based on reproduction scenario from issue #2301 (https://github.com/eclipse-ee4j/eclipselink/issues/2301) +@Entity +@Table(name = EntityFloat.TABLE_NAME) +public class EntityFloat { + + static final String TABLE_NAME = "ADV_ENTITY_FLOAT"; + + @Id + @Column(name = "ID") + private int id; + + @Column(name = "HEIGHT") + private float height; + + @Column(name = "LENGTH") + private float length; + + @Column(name = "WIDTH") + private float width; + + @Column(name = "DESCRIPTION") + private String description; + + public EntityFloat() { + this(-1, 0f, 0f, 0f, null); + } + + public EntityFloat(int id, float length, float width, float height, String description) { + this.id = id; + this.length = length; + this.width = width; + this.height = height; + this.description = description; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public float getHeight() { + return height; + } + + public void setHeight(float height) { + this.height = height; + } + + public float getLength() { + return length; + } + + public void setLength(float length) { + this.length = length; + } + + public float getWidth() { + return width; + } + + public void setWidth(float width) { + this.width = width; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + @Override + public String toString() { + return String.format( + "EntityFloat{ id=%d, length=%f, width=%f, height=%f, description=\"%s\"}", + id, length, width, height, description); + } + + // Based on reproduction scenario from issue #2301 (https://github.com/eclipse-ee4j/eclipselink/issues/2301) + public static final class Populator extends TogglingFastTableCreator { + + public static EntityFloat[] ENTITY_FLOAT = new EntityFloat[] { + // Tallest and smallest length + new EntityFloat(70071, 17.0f, 17.1f, 7.7f, "testOLGH28289#70071"), + // Tallest and largest length + new EntityFloat(70077, 77.0f, 17.7f, 7.7f, "testOLGH28289#70077"), + new EntityFloat(70007, 70.0f, 10.7f, 0.7f, "testOLGH28289#70007") + }; + + public static void populate(Session session) { + List entities = Arrays.asList(ENTITY_FLOAT); + UnitOfWork unitOfWork = session.acquireUnitOfWork(); + unitOfWork.removeAllReadOnlyClasses(); + unitOfWork.registerAllObjects(entities); + unitOfWork.commit(); + } + + // Supported data types according to https://docs.oracle.com/cd/E19501-01/819-3659/gcmaz/ + public static TableDefinition buildTable() { + TableDefinition table = new TableDefinition(); + table.setName(TABLE_NAME); + table.addField(createNumericPk("ID", 10)); + table.addField(createDoubleColumn("HEIGHT", false)); + table.addField(createDoubleColumn("LENGTH", false)); + table.addField(createDoubleColumn("WIDTH", false)); + table.addField(createStringColumn("DESCRIPTION", 255,false)); + return table; + } + + private Populator() { + throw new UnsupportedOperationException("No instances of EntityFloatPopulator are allowed"); + } + + } + +} diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/main/resources/META-INF/persistence.xml b/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/main/resources/META-INF/persistence.xml index 28ad1e40da0..c1176d3575b 100644 --- a/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/main/resources/META-INF/persistence.xml +++ b/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/main/resources/META-INF/persistence.xml @@ -47,6 +47,7 @@ org.eclipse.persistence.testing.models.jpa.advanced.entities.EntyC org.eclipse.persistence.testing.models.jpa.advanced.entities.EntyD org.eclipse.persistence.testing.models.jpa.advanced.entities.EntyE + org.eclipse.persistence.testing.models.jpa.advanced.entities.EntityFloat org.eclipse.persistence.testing.models.jpa.advanced.order.select.where.Order true @@ -95,6 +96,7 @@ org.eclipse.persistence.testing.models.jpa.advanced.entities.EntyC org.eclipse.persistence.testing.models.jpa.advanced.entities.EntyD org.eclipse.persistence.testing.models.jpa.advanced.entities.EntyE + org.eclipse.persistence.testing.models.jpa.advanced.entities.EntityFloat org.eclipse.persistence.testing.models.jpa.advanced.order.select.where.Order true diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/test/java/org/eclipse/persistence/testing/tests/jpa/jpql/advanced/AdvancedQueryTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/test/java/org/eclipse/persistence/testing/tests/jpa/jpql/advanced/AdvancedQueryTest.java index b4fadd13bf4..7d3ee1e82a6 100644 --- a/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/test/java/org/eclipse/persistence/testing/tests/jpa/jpql/advanced/AdvancedQueryTest.java +++ b/jpa/eclipselink.jpa.testapps/jpa.test.jpql/src/test/java/org/eclipse/persistence/testing/tests/jpa/jpql/advanced/AdvancedQueryTest.java @@ -54,6 +54,7 @@ import org.eclipse.persistence.testing.models.jpa.advanced.Employee; import org.eclipse.persistence.testing.models.jpa.advanced.Employee.Gender; import org.eclipse.persistence.testing.models.jpa.advanced.EmployeePopulator; +import org.eclipse.persistence.testing.models.jpa.advanced.entities.EntityFloat; import org.eclipse.persistence.testing.tests.jpa.jpql.JUnitDomainObjectComparer; import org.junit.Assert; @@ -162,6 +163,7 @@ public static Test suite() { suite.addTest(new AdvancedQueryTest("testVersionChangeWithReadLock")); suite.addTest(new AdvancedQueryTest("testVersionChangeWithWriteLock")); suite.addTest(new AdvancedQueryTest("testNamedQueryAnnotationOverwritePersistenceXML")); + suite.addTest(new AdvancedQueryTest("testFloatSortWithPessimisticLock")); suite.addTest(new AdvancedQueryTest("testTearDown")); return suite; } @@ -184,6 +186,8 @@ public void testSetup() { employeePopulator.buildExamples(); //Persist the examples in the database employeePopulator.persistExample(session); + // EntityFloat instances to test issue #2301 + EntityFloat.Populator.populate(session); } public void testTearDown() { @@ -2707,4 +2711,27 @@ public void run() { closeEntityManager(em); } } + + // Based on reproduction scenario from issue #2301 (https://github.com/eclipse-ee4j/eclipselink/issues/2301) + public void testFloatSortWithPessimisticLock() { + EntityManager em = createEntityManager(); + beginTransaction(em); + List entities; + try { + entities = em.createQuery("SELECT f FROM EntityFloat f WHERE (f.height < ?1) ORDER BY f.height DESC, f.length", + EntityFloat.class) + .setParameter(1, 8.0) + .setLockMode(LockModeType.PESSIMISTIC_WRITE) // Cause of issue + .setMaxResults(2) + .getResultList(); + commitTransaction(em); + } catch (PersistenceException ex) { + rollbackTransaction(em); + throw ex; + } + assertEquals(2, entities.size()); + assertEquals(70071, entities.get(0).getId()); + assertEquals(70077, entities.get(1).getId()); + } + }