diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/queries/ReportQuery.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/queries/ReportQuery.java index 4b670f01c9c..b81e6fa7322 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/queries/ReportQuery.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/queries/ReportQuery.java @@ -933,7 +933,12 @@ public Object executeDatabaseQuery() throws DatabaseException { return getDescriptor().getInterfacePolicy().selectAllObjectsUsingMultipleTableSubclassRead(this); } - return buildObjects(getQueryMechanism().selectAllReportQueryRows()); + List rows = getQueryMechanism().selectAllReportQueryRows(); + if ((this.batchFetchPolicy != null) && this.batchFetchPolicy.isIN()) { + this.batchFetchPolicy.setDataResults(rows); + } + + return buildObjects((Vector) rows); } /** diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/pom.xml b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/pom.xml new file mode 100644 index 00000000000..56cdf9994fe --- /dev/null +++ b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/pom.xml @@ -0,0 +1,141 @@ + + + org.eclipse.persistence.jpa.testapps + org.eclipse.persistence + 5.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + org.eclipse.persistence.jpa.testapps.batchfetch + + Test - batchfetch + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + default-compile + none + + + + compile-with-processor + compile + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + get-test-classpath-to-properties + process-test-classes + + + + + + org.carlspring.maven + derby-maven-plugin + + + start-derby + process-test-classes + + + stop-derby + prepare-package + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + default-test + + + -javaagent:${org.eclipse.persistence:org.eclipse.persistence.jpa:jar} @{argLine} + + + + server-test + + + + **/EntityManagerImplTest + **/EntityManagerFactoryImplTest + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + package-model + package + + + + package-se-tests + package + + + + + + + + + + oracle + + + + org.codehaus.mojo + sql-maven-plugin + + ${db.user} + ${db.pwd} + true + continue + + + + + struct-converter-sql + process-test-resources + + execute + + + + CREATE OR REPLACE TYPE MY_GEOMETRY AS OBJECT (id NUMBER, geom MDSYS.SDO_GEOMETRY); + + + + + + + + + + \ No newline at end of file diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/BatchFetchTableCreator.java b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/BatchFetchTableCreator.java new file mode 100644 index 00000000000..e24174204d7 --- /dev/null +++ b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/BatchFetchTableCreator.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2009, 2022 Oracle and/or its affiliates. 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 + */ + +// Contributors: + +package org.eclipse.persistence.jpa.testapps.batchfetch; + +import org.eclipse.persistence.tools.schemaframework.FieldDefinition; +import org.eclipse.persistence.tools.schemaframework.TableCreator; +import org.eclipse.persistence.tools.schemaframework.TableDefinition; + +public class BatchFetchTableCreator extends TableCreator { + public BatchFetchTableCreator() { + setName("BatchFetchProject"); + + addTableDefinition(buildCompanyTable()); + addTableDefinition(buildEmployeeTable()); + addTableDefinition(buildRecordTable()); + } + + public TableDefinition buildRecordTable() { + TableDefinition table = new TableDefinition(); + table.setName("RECORD"); + + FieldDefinition fieldID = new FieldDefinition(); + fieldID.setName("ID"); + fieldID.setTypeName("NUMBER"); + fieldID.setSize(19); + fieldID.setSubSize(0); + fieldID.setIsPrimaryKey(true); + fieldID.setIsIdentity(true); + fieldID.setShouldAllowNull(false); + table.addField(fieldID); + + FieldDefinition fieldUSER = new FieldDefinition(); + fieldUSER.setName("EMPLOYEE_ID"); + fieldUSER.setTypeName("NUMBER"); + fieldUSER.setSize(19); + fieldUSER.setSubSize(0); + fieldUSER.setIsPrimaryKey(false); + fieldUSER.setIsIdentity(false); + fieldUSER.setShouldAllowNull(false); + fieldUSER.setForeignKeyFieldName("EMPLOYEE.ID"); + table.addField(fieldUSER); + + return table; + } + + public TableDefinition buildCompanyTable() { + TableDefinition table = new TableDefinition(); + table.setName("COMPANY"); + + FieldDefinition fieldID = new FieldDefinition(); + fieldID.setName("ID"); + fieldID.setTypeName("NUMBER"); + fieldID.setSize(19); + fieldID.setSubSize(0); + fieldID.setIsPrimaryKey(true); + fieldID.setIsIdentity(true); + fieldID.setShouldAllowNull(false); + table.addField(fieldID); + + return table; + } + + + public TableDefinition buildEmployeeTable() { + TableDefinition table = new TableDefinition(); + table.setName("EMPLOYEE"); + + FieldDefinition fieldID = new FieldDefinition(); + fieldID.setName("ID"); + fieldID.setTypeName("NUMBER"); + fieldID.setSize(19); + fieldID.setSubSize(0); + fieldID.setIsPrimaryKey(true); + fieldID.setIsIdentity(true); + fieldID.setShouldAllowNull(false); + table.addField(fieldID); + + FieldDefinition fieldCompany = new FieldDefinition(); + fieldCompany.setName("COMPANY_ID"); + fieldCompany.setTypeName("NUMBER"); + fieldCompany.setSize(19); + fieldCompany.setSubSize(0); + fieldCompany.setIsPrimaryKey(false); + fieldCompany.setIsIdentity(false); + fieldCompany.setShouldAllowNull(false); + fieldCompany.setForeignKeyFieldName("COMPANY.ID"); + table.addField(fieldCompany); + + return table; + + } + +} diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/Company.java b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/Company.java new file mode 100644 index 00000000000..366f73bfba1 --- /dev/null +++ b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/Company.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1998, 2024 Oracle and/or its affiliates. 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 + */ + +// Contributors: + +package org.eclipse.persistence.jpa.testapps.batchfetch; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import org.eclipse.persistence.annotations.BatchFetch; +import org.eclipse.persistence.annotations.BatchFetchType; + +import java.util.List; + +@Entity +@Table(name = "COMPANY") +public class Company { + @Id + private long id; + + public Company() { + } + + public Company(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } +} diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/Employee.java b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/Employee.java new file mode 100644 index 00000000000..f377f35ebd7 --- /dev/null +++ b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/Employee.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 1998, 2024 Oracle and/or its affiliates. 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 + */ + +// Contributors: + +package org.eclipse.persistence.jpa.testapps.batchfetch; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import org.eclipse.persistence.annotations.BatchFetch; +import org.eclipse.persistence.annotations.BatchFetchType; + +@Entity +@Table(name = "EMPLOYEE") +public class Employee { + @Id + private long id; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "COMPANY_ID") + @BatchFetch(value = BatchFetchType.IN) + private Company company; + + public Employee() { + } + + public Employee(long id, Company company) { + this.id = id; + this.company = company; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Company getCompany() { + return company; + } + + public void setCompany(Company company) { + this.company = company; + } +} diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/Record.java b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/Record.java new file mode 100644 index 00000000000..802091335c7 --- /dev/null +++ b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/java/org/eclipse/persistence/jpa/testapps/batchfetch/Record.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 1998, 2024 Oracle and/or its affiliates. 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 + */ + +// Contributors: + +package org.eclipse.persistence.jpa.testapps.batchfetch; + +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import org.eclipse.persistence.annotations.BatchFetch; +import org.eclipse.persistence.annotations.BatchFetchType; + +@Entity +@Table(name = "RECORD") +public class Record { + @Id + private long id; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "EMPLOYEE_ID") + @BatchFetch(value = BatchFetchType.IN) + private Employee employee; + + public Record() { + } + + public Record(long id, Employee employee) { + this.id = id; + this.employee = employee; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Employee getEmployee() { + return employee; + } + + public void setEmployee(Employee employee) { + this.employee = employee; + } +} diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/resources/META-INF/persistence.xml b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/resources/META-INF/persistence.xml new file mode 100644 index 00000000000..0f95c589fbf --- /dev/null +++ b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/main/resources/META-INF/persistence.xml @@ -0,0 +1,29 @@ + + + + + org.eclipse.persistence.jpa.PersistenceProvider + org.eclipse.persistence.jpa.testapps.batchfetch.Company + org.eclipse.persistence.jpa.testapps.batchfetch.Employee + org.eclipse.persistence.jpa.testapps.batchfetch.Record + + + + + + + diff --git a/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/test/java/batchfetch/BatchFetchJUnitTest.java b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/test/java/batchfetch/BatchFetchJUnitTest.java new file mode 100644 index 00000000000..01bfe3f3523 --- /dev/null +++ b/jpa/eclipselink.jpa.testapps/jpa.test.batchfetch/src/test/java/batchfetch/BatchFetchJUnitTest.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 1998, 2024 Oracle and/or its affiliates. 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 + */ + +// Contributors: + +package batchfetch; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.TypedQuery; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.eclipse.persistence.jpa.testapps.batchfetch.BatchFetchTableCreator; +import org.eclipse.persistence.jpa.testapps.batchfetch.Company; +import org.eclipse.persistence.jpa.testapps.batchfetch.Employee; +import org.eclipse.persistence.jpa.testapps.batchfetch.Record; +import org.eclipse.persistence.testing.framework.jpa.junit.JUnitTestCase; + +import java.util.List; +import java.util.Objects; + +public class BatchFetchJUnitTest extends JUnitTestCase { + + public BatchFetchJUnitTest() { + super(); + } + + public BatchFetchJUnitTest(String name) { + super(name); + } + + public static Test suite() { + TestSuite suite = new TestSuite(); + suite.setName("BatchFetchJunitTest"); + suite.addTest(new BatchFetchJUnitTest("testSetup")); + suite.addTest(new BatchFetchJUnitTest("testSelectRoot")); + suite.addTest(new BatchFetchJUnitTest("testSelectNonRoot")); + return suite; + } + + /** + * The setup is done as a test, both to record its failure, and to allow execution in the server. + */ + public void testSetup() { + new BatchFetchTableCreator().replaceTables(JUnitTestCase.getServerSession("batchfetch")); + EntityManager em = createEntityManager(); + createRecords(em); + } + + public void createRecords(EntityManager em){ + try { + beginTransaction(em); + Company c1 = new Company(1); + Company c2 = new Company(2); + em.persist(c1); + em.persist(c2); + + Employee u1 = new Employee(1, c1); + Employee u2 = new Employee(2, c1); + Employee u3 = new Employee(3, c2); + em.persist(u1); + em.persist(u2); + em.persist(u3); + + Record r1 = new Record(1, u1); + Record r2 = new Record(2, u2); + Record r3 = new Record(3, u3); + em.persist(r1); + em.persist(r2); + em.persist(r3); + + commitTransaction(em); + } catch (RuntimeException ex) { + if (isTransactionActive(em)) { + rollbackTransaction(em); + } + throw ex; + } finally { + closeEntityManager(em); + } + } + + public void testSelectRoot() { + EntityManager em = createEntityManager(); + em.getEntityManagerFactory().getCache().evictAll(); + + try { + TypedQuery q = em.createQuery("SELECT r FROM Record r", Record.class); + List result = q.getResultList(); + assertEquals("Not all rows are selected", 3, result.size()); + List employees = result.stream().map(Record::getEmployee).filter(Objects::nonNull).toList(); + assertEquals("Not all rows have employees", 3, employees.size()); + List companies = employees.stream().map(Employee::getCompany).filter(Objects::nonNull).toList(); + assertEquals("Not all users have companies", 3, companies.size()); + } catch (RuntimeException e) { + closeEntityManager(em); + throw e; + } + } + + public void testSelectNonRoot() { + EntityManager em = createEntityManager(); + em.getEntityManagerFactory().getCache().evictAll(); + + try { + TypedQuery q = em.createQuery("SELECT r.employee FROM Record r", Employee.class); + List result = q.getResultList(); + assertEquals("Not all rows are selected", 3, result.size()); + List companies = result.stream().map(Employee::getCompany).filter(Objects::nonNull).toList(); + assertEquals("Not all employees have companies", 3, companies.size()); + } catch (RuntimeException e) { + closeEntityManager(em); + throw e; + } + } + + @Override + public String getPersistenceUnitName() { + return "batchfetch"; + } +} diff --git a/jpa/eclipselink.jpa.testapps/pom.xml b/jpa/eclipselink.jpa.testapps/pom.xml index 0fc9582dbd3..3ecf377b352 100644 --- a/jpa/eclipselink.jpa.testapps/pom.xml +++ b/jpa/eclipselink.jpa.testapps/pom.xml @@ -290,6 +290,7 @@ jpa.test.advanced.embeddable jpa.test.advanced.fetchgroup jpa.test.advanced.multitenant + jpa.test.batchfetch jpa.test.beanvalidation jpa.test.beanvalidation.dynamic jpa.test.cacheable