diff --git a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReportQuery.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReportQuery.java index 7bd6d9c6a1f..9c2f9e281c8 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReportQuery.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReportQuery.java @@ -891,7 +891,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/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReportQueryResult.java b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReportQueryResult.java index f72bfe34487..118fdac296d 100644 --- a/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReportQueryResult.java +++ b/foundation/org.eclipse.persistence.core/src/org/eclipse/persistence/queries/ReportQueryResult.java @@ -239,9 +239,26 @@ protected Object processItem(ReportQuery query, AbstractRecord row, Vector toMan AbstractRecord subRow = row; // Check if at the start of the row, then avoid building a subRow. if (itemIndex > 0) { - Vector trimedFields = new NonSynchronizedSubVector(row.getFields(), itemIndex, rowSize); - Vector trimedValues = new NonSynchronizedSubVector(row.getValues(), itemIndex, rowSize); - subRow = new DatabaseRecord(trimedFields, trimedValues); + BatchFetchPolicy batchFetchPolicy = query.getBatchFetchPolicy(); + if (batchFetchPolicy != null && batchFetchPolicy.isIN()) { + + List subRows = new ArrayList(toManyData.size()); + for (AbstractRecord parentRow : (Vector) toManyData) { + Vector trimedParentFields = new NonSynchronizedSubVector(parentRow.getFields(), itemIndex, rowSize); + Vector trimedParentValues = new NonSynchronizedSubVector(parentRow.getValues(), itemIndex, rowSize); + subRows.add(new DatabaseRecord(trimedParentFields, trimedParentValues)); + } + + for (DatabaseMapping subMapping : descriptor.getMappings()) { + batchFetchPolicy.setDataResults(subMapping, subRows); + } + + subRow = subRows.get(toManyData.indexOf(row)); + } else { + Vector trimedFields = new NonSynchronizedSubVector(row.getFields(), itemIndex, rowSize); + Vector trimedValues = new NonSynchronizedSubVector(row.getValues(), itemIndex, rowSize); + subRow = new DatabaseRecord(trimedFields, trimedValues); + } } if (mapping != null && mapping.isAggregateObjectMapping()){ value = ((AggregateObjectMapping)mapping).buildAggregateFromRow(subRow, null, null, joinManager, query, false, query.getSession(), true); diff --git a/jpa/eclipselink.jpa.test/antbuild.properties b/jpa/eclipselink.jpa.test/antbuild.properties index c93fc6a7c43..f880db911c6 100644 --- a/jpa/eclipselink.jpa.test/antbuild.properties +++ b/jpa/eclipselink.jpa.test/antbuild.properties @@ -131,6 +131,7 @@ jpa.performance=jpa-performance jpa.performance2=jpa-performance2 eclipselink.delimited.model=eclipselink-delimited-model eclipselink.extensibility.model=eclipselink-extensibility-model +eclipselink.batchfetch.model=eclipselink-batchfetch-model eclipselink.beanvalidation.model=eclipselink-beanvalidation-model eclipselink.beanvalidation.dynamic.model=eclipselink-beanvalidation-dynamic-model diff --git a/jpa/eclipselink.jpa.test/antbuild.xml b/jpa/eclipselink.jpa.test/antbuild.xml index fd5f675fc98..420dce048ed 100644 --- a/jpa/eclipselink.jpa.test/antbuild.xml +++ b/jpa/eclipselink.jpa.test/antbuild.xml @@ -593,6 +593,7 @@ + @@ -656,6 +657,7 @@ + @@ -935,7 +937,7 @@ + + + + + + + + + + + + @@ -1609,6 +1625,7 @@ + @@ -1646,6 +1663,7 @@ + @@ -1683,6 +1701,7 @@ + @@ -2204,6 +2223,7 @@ + @@ -2803,6 +2823,13 @@ + + + + + @@ -3997,6 +4024,7 @@ + @@ -4142,6 +4170,20 @@ *.jdbc"/> + + + + + + + + + + + + + + diff --git a/jpa/eclipselink.jpa.test/resource/eclipselink-batchfetch-model/persistence.xml b/jpa/eclipselink.jpa.test/resource/eclipselink-batchfetch-model/persistence.xml new file mode 100644 index 00000000000..fe64df50062 --- /dev/null +++ b/jpa/eclipselink.jpa.test/resource/eclipselink-batchfetch-model/persistence.xml @@ -0,0 +1,27 @@ + + + + + org.eclipse.persistence.jpa.PersistenceProvider + org.eclipse.persistence.testing.models.jpa.batchfetch.Company + org.eclipse.persistence.testing.models.jpa.batchfetch.Employee + org.eclipse.persistence.testing.models.jpa.batchfetch.Record + + + + + + + diff --git a/jpa/eclipselink.jpa.test/resource/eclipselink-batchfetch-model/server/persistence.xml b/jpa/eclipselink.jpa.test/resource/eclipselink-batchfetch-model/server/persistence.xml new file mode 100644 index 00000000000..355a9085c32 --- /dev/null +++ b/jpa/eclipselink.jpa.test/resource/eclipselink-batchfetch-model/server/persistence.xml @@ -0,0 +1,29 @@ + + + + + org.eclipse.persistence.jpa.PersistenceProvider + <@datasource-type@>@data-source-name@ + org.eclipse.persistence.testing.models.jpa.batchfetch.Company + org.eclipse.persistence.testing.models.jpa.batchfetch.Employee + org.eclipse.persistence.testing.models.jpa.batchfetch.Record + + + + + + + + diff --git a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/BatchFetchTableCreator.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/BatchFetchTableCreator.java new file mode 100644 index 00000000000..d9157684e47 --- /dev/null +++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/BatchFetchTableCreator.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 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.testing.models.jpa.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("BATCH_IN_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("BATCH_IN_EMPLOYEE.ID"); + table.addField(fieldUSER); + + return table; + } + + public TableDefinition buildCompanyTable() { + TableDefinition table = new TableDefinition(); + table.setName("BATCH_IN_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("BATCH_IN_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("BATCH_IN_COMPANY.ID"); + table.addField(fieldCompany); + + return table; + + } + +} diff --git a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Company.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Company.java new file mode 100644 index 00000000000..599c7b25411 --- /dev/null +++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Company.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 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.testing.models.jpa.batchfetch; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Table; +import org.eclipse.persistence.annotations.BatchFetch; +import org.eclipse.persistence.annotations.BatchFetchType; + +import java.util.List; + +@Entity +@Table(name = "BATCH_IN_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.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Count.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Count.java new file mode 100644 index 00000000000..9d825986381 --- /dev/null +++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Count.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 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.testing.models.jpa.batchfetch; + +public class Count { + + private long value; + private Employee emp; + + public Count(long value, Employee emp) { + this.value = value; + this.emp = emp; + } + + public long getValue() { + return value; + } + + public void setValue(long value) { + this.value = value; + } + + public Employee getEmp() { + return emp; + } + + public void setEmp(Employee emp) { + this.emp = emp; + } +} diff --git a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Employee.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Employee.java new file mode 100644 index 00000000000..bdabc3d46de --- /dev/null +++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Employee.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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.testing.models.jpa.batchfetch; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import org.eclipse.persistence.annotations.BatchFetch; +import org.eclipse.persistence.annotations.BatchFetchType; + +@Entity +@Table(name = "BATCH_IN_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.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Record.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Record.java new file mode 100644 index 00000000000..c1fbbfab1c2 --- /dev/null +++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/models/jpa/batchfetch/Record.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 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.testing.models.jpa.batchfetch; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; +import javax.persistence.Table; +import org.eclipse.persistence.annotations.BatchFetch; +import org.eclipse.persistence.annotations.BatchFetchType; + +@Entity +@Table(name = "BATCH_IN_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.test/src/org/eclipse/persistence/testing/tests/jpa/FullRegressionTestSuite.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/FullRegressionTestSuite.java index 11b5ba79de6..2190883c2ca 100644 --- a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/FullRegressionTestSuite.java +++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/FullRegressionTestSuite.java @@ -61,6 +61,7 @@ import org.eclipse.persistence.testing.tests.jpa.advanced.fetchgroup.AdvancedFetchGroupJunitTest; import org.eclipse.persistence.testing.tests.jpa.advanced.multitenant.AdvancedMultiTenantJunitTest; import org.eclipse.persistence.testing.tests.jpa.advanced.multitenant.AdvancedMultiTenantSchemaJunitTest; +import org.eclipse.persistence.testing.tests.jpa.batchfetch.BatchFetchJUnitTest; import org.eclipse.persistence.testing.tests.jpa.cacheable.CacheableModelJunitTest; import org.eclipse.persistence.testing.tests.jpa.cacheable.CacheableModelJunitTestEnableSelective; import org.eclipse.persistence.testing.tests.jpa.cascadedeletes.CascadeDeletesJUnitTestSuite; @@ -373,6 +374,9 @@ public static TestSuite suite4() { // Persistence Unit Processor tests. fullSuite.addTest(PersistenceUnitProcessorTest.suite()); + // Batchfetch tests + fullSuite.addTest(BatchFetchJUnitTest.suite()); + return fullSuite; } } diff --git a/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/batchfetch/BatchFetchJUnitTest.java b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/batchfetch/BatchFetchJUnitTest.java new file mode 100644 index 00000000000..ca7dd177df5 --- /dev/null +++ b/jpa/eclipselink.jpa.test/src/org/eclipse/persistence/testing/tests/jpa/batchfetch/BatchFetchJUnitTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 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.testing.tests.jpa.batchfetch; + +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.eclipse.persistence.config.QueryHints; +import org.eclipse.persistence.testing.models.jpa.batchfetch.BatchFetchTableCreator; +import org.eclipse.persistence.testing.models.jpa.batchfetch.Company; +import org.eclipse.persistence.testing.models.jpa.batchfetch.Count; +import org.eclipse.persistence.testing.models.jpa.batchfetch.Employee; +import org.eclipse.persistence.testing.models.jpa.batchfetch.Record; +import org.eclipse.persistence.testing.framework.junit.JUnitTestCase; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +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")); + suite.addTest(new BatchFetchJUnitTest("testSelectNonRootWithOffsetAndLimit")); + 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).collect(Collectors.toList()); + assertEquals("Not all rows have employees", 3, employees.size()); + List companies = employees.stream().map(Employee::getCompany).filter(Objects::nonNull).collect(Collectors.toList()); + assertEquals("Not all employees 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).collect(Collectors.toList()); + assertEquals("Not all employees have companies", 3, companies.size()); + } catch (RuntimeException e) { + closeEntityManager(em); + throw e; + } + } + + public void testSelectNonRootWithOffsetAndLimit() { + EntityManager em = createEntityManager(); + em.getEntityManagerFactory().getCache().evictAll(); + + try { + TypedQuery q = em.createQuery("SELECT new org.eclipse.persistence.testing.models.jpa.batchfetch.Count(count(r.employee), r.employee) FROM Record r group by r.employee", Count.class); + q.setHint(QueryHints.BATCH_SIZE, 1); + List result = q.getResultList(); + assertEquals("Not all rows are selected", 3, result.size()); + List employees = result.stream().map(Count::getEmp).filter(Objects::nonNull).collect(Collectors.toList()); + assertEquals("Not all counts have employees", 3, result.size()); + List companies = employees.stream().map(Employee::getCompany).filter(Objects::nonNull).collect(Collectors.toList()); + assertEquals("Not all employees have companies", 3, companies.size()); + } catch (RuntimeException e) { + closeEntityManager(em); + throw e; + } + } + + @Override + public String getPersistenceUnitName() { + return "batchfetch"; + } +}