/* * Hibernate, Relational Persistence for Idiomatic Java * * Copyright (c) 2006-2011, Red Hat Inc. or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Inc. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ package org.hibernate.test.subselectfetch; import java.util.ArrayList; import java.util.List; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinTable; import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.Table; import org.junit.Test; import org.hibernate.Hibernate; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.annotations.BatchSize; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; /** * @author Stephen Fikes * @author Gail Badner */ public class SubselectFetchCollectionFromBatchTest extends BaseCoreFunctionalTestCase { @Override public void configure(Configuration cfg) { cfg.setProperty( Environment.GENERATE_STATISTICS, "true"); } @Test @TestForIssue( jiraKey = "HHH-10679") public void testSubselectFetchFromEntityBatch() { Session s = openSession(); Transaction t = s.beginTransaction(); EmployeeGroup group1 = new EmployeeGroup(); Employee employee1 = new Employee("Jane"); Employee employee2 = new Employee("Jeff"); group1.addEmployee(employee1); group1.addEmployee(employee2); EmployeeGroup group2 = new EmployeeGroup(); Employee employee3 = new Employee("Joan"); Employee employee4 = new Employee("John"); group2.addEmployee(employee3); group2.addEmployee( employee4 ); s.save( group1 ); s.save( group2 ); s.flush(); s.clear(); sessionFactory().getStatistics().clear(); EmployeeGroup[] groups = new EmployeeGroup[] { (EmployeeGroup) s.load(EmployeeGroup.class, group1.getId()), (EmployeeGroup) s.load(EmployeeGroup.class, group2.getId()) }; // groups should only contain proxies assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); for (EmployeeGroup group : groups) { assertFalse( Hibernate.isInitialized( group ) ); } assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); for (int i = 0 ; i < groups.length; i++ ) { // Both groups get initialized and are added to the PersistenceContext when i == 0; // Still need to call Hibernate.initialize( groups[i] ) for i > 0 so that the entity // in the PersistenceContext gets assigned to its respective proxy target (is this a // bug???) Hibernate.initialize( groups[ i ] ); assertTrue( Hibernate.isInitialized( groups[i] ) ); // the collections should be uninitialized assertFalse( Hibernate.isInitialized( groups[i].getEmployees() ) ); } // both Group proxies should have been loaded in the same batch; assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); sessionFactory().getStatistics().clear(); for (EmployeeGroup group : groups) { assertTrue( Hibernate.isInitialized( group ) ); assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); } assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); // now initialize the collection in the first; collections in both groups // should get initialized Hibernate.initialize( groups[0].getEmployees() ); assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); sessionFactory().getStatistics().clear(); // all collections should be initialized now for (EmployeeGroup group : groups) { assertTrue( Hibernate.isInitialized( group.getEmployees() ) ); } assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); t.rollback(); s.close(); } @Test public void testSubselectFetchFromQueryList() { Session s = openSession(); Transaction t = s.beginTransaction(); EmployeeGroup group1 = new EmployeeGroup(); Employee employee1 = new Employee("Jane"); Employee employee2 = new Employee("Jeff"); group1.addEmployee(employee1); group1.addEmployee(employee2); EmployeeGroup group2 = new EmployeeGroup(); Employee employee3 = new Employee("Joan"); Employee employee4 = new Employee("John"); group2.addEmployee(employee3); group2.addEmployee( employee4 ); s.save( group1 ); s.save( group2 ); s.flush(); s.clear(); sessionFactory().getStatistics().clear(); List<EmployeeGroup> results = s.createQuery( "from SubselectFetchCollectionFromBatchTest$EmployeeGroup where id in :groups" ).setParameterList( "groups", new Long[] { group1.getId(), group2.getId() } ).list(); assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); sessionFactory().getStatistics().clear(); for (EmployeeGroup group : results) { assertTrue( Hibernate.isInitialized( group ) ); assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); } assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); // now initialize the collection in the first; collections in both groups // should get initialized Hibernate.initialize( results.get( 0 ).getEmployees() ); assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); sessionFactory().getStatistics().clear(); // all collections should be initialized now for (EmployeeGroup group : results) { assertTrue( Hibernate.isInitialized( group.getEmployees() ) ); } assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); t.rollback(); s.close(); } @Test @TestForIssue( jiraKey = "HHH-10679") public void testMultiSubselectFetchSamePersisterQueryList() { Session s = openSession(); Transaction t = s.beginTransaction(); EmployeeGroup group1 = new EmployeeGroup(); Employee employee1 = new Employee("Jane"); Employee employee2 = new Employee("Jeff"); group1.addEmployee( employee1 ); group1.addEmployee( employee2 ); group1.setManager( new Employee( "group1 manager" ) ); group1.getManager().addCollaborator( new Employee( "group1 manager's collaborator#1" ) ); group1.getManager().addCollaborator( new Employee( "group1 manager's collaborator#2" ) ); group1.setLead( new Employee( "group1 lead" ) ); group1.getLead().addCollaborator( new Employee( "group1 lead's collaborator#1" ) ); EmployeeGroup group2 = new EmployeeGroup(); Employee employee3 = new Employee("Joan"); Employee employee4 = new Employee("John"); group2.addEmployee( employee3 ); group2.addEmployee( employee4 ); group2.setManager( new Employee( "group2 manager" ) ); group2.getManager().addCollaborator( new Employee( "group2 manager's collaborator#1" ) ); group2.getManager().addCollaborator( new Employee( "group2 manager's collaborator#2" ) ); group2.getManager().addCollaborator( new Employee( "group2 manager's collaborator#3" ) ); group2.setLead( new Employee( "group2 lead" ) ); group2.getLead().addCollaborator( new Employee( "group2 lead's collaborator#1" ) ); group2.getLead().addCollaborator( new Employee( "group2 lead's collaborator#2" ) ); s.save( group1 ); s.save( group2 ); s.flush(); s.clear(); sessionFactory().getStatistics().clear(); EmployeeGroup[] groups = new EmployeeGroup[] { (EmployeeGroup) s.load(EmployeeGroup.class, group1.getId()), (EmployeeGroup) s.load(EmployeeGroup.class, group2.getId()) }; // groups should only contain proxies assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); for (EmployeeGroup group : groups) { assertFalse( Hibernate.isInitialized( group ) ); } assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); for (int i = 0 ; i < groups.length; i++ ) { // Both groups get initialized and are added to the PersistenceContext when i == 0; // Still need to call Hibernate.initialize( groups[i] ) for i > 0 so that the entity // in the PersistenceContext gets assigned to its respective proxy target (is this a // bug???) Hibernate.initialize( groups[ i ] ); assertTrue( Hibernate.isInitialized( groups[i] ) ); assertTrue( Hibernate.isInitialized( groups[i].getLead() ) ); assertFalse( Hibernate.isInitialized( groups[i].getLead().getCollaborators() ) ); assertTrue( Hibernate.isInitialized( groups[i].getManager() ) ); assertFalse( Hibernate.isInitialized( groups[i].getManager().getCollaborators() ) ); // the collections should be uninitialized assertFalse( Hibernate.isInitialized( groups[i].getEmployees() ) ); } // both Group proxies should have been loaded in the same batch; assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); sessionFactory().getStatistics().clear(); for (EmployeeGroup group : groups) { assertTrue( Hibernate.isInitialized( group ) ); assertFalse( Hibernate.isInitialized( group.getEmployees() ) ); } assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); // now initialize the collection in the first; collections in both groups // should get initialized Hibernate.initialize( groups[0].getEmployees() ); assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); sessionFactory().getStatistics().clear(); // all EmployeeGroup#employees should be initialized now for (EmployeeGroup group : groups) { assertTrue( Hibernate.isInitialized( group.getEmployees() ) ); assertFalse( Hibernate.isInitialized( group.getLead().getCollaborators() ) ); assertFalse( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); } assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); // now initialize groups[0].getLead().getCollaborators(); // groups[1].getLead().getCollaborators() should also be initialized Hibernate.initialize( groups[0].getLead().getCollaborators() ); assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); sessionFactory().getStatistics().clear(); for (EmployeeGroup group : groups) { assertTrue( Hibernate.isInitialized( group.getLead().getCollaborators() ) ); assertFalse( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); } assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); // now initialize groups[0].getManager().getCollaborators(); // groups[1].getManager().getCollaborators() should also be initialized Hibernate.initialize( groups[0].getManager().getCollaborators() ); assertEquals( 1, sessionFactory().getStatistics().getPrepareStatementCount() ); sessionFactory().getStatistics().clear(); for (EmployeeGroup group : groups) { assertTrue( Hibernate.isInitialized( group.getManager().getCollaborators() ) ); } assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); assertEquals( group1.getLead().getCollaborators().size(), groups[0].getLead().getCollaborators().size() ); assertEquals( group2.getLead().getCollaborators().size(), groups[1].getLead().getCollaborators().size() ); assertEquals( group1.getManager().getCollaborators().size(), groups[0].getManager().getCollaborators().size() ); assertEquals( group2.getManager().getCollaborators().size(), groups[1].getManager().getCollaborators().size() ); assertEquals( 0, sessionFactory().getStatistics().getPrepareStatementCount() ); t.rollback(); s.close(); } public Class<?>[] getAnnotatedClasses() { return new Class<?>[] { EmployeeGroup.class, Employee.class }; } @Entity @Table(name = "EmployeeGroup") @BatchSize(size = 1000) private static class EmployeeGroup { @Id @GeneratedValue private Long id; @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) private Employee manager; @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL) private Employee lead; @OneToMany(cascade = CascadeType.ALL) @Fetch(FetchMode.SUBSELECT) @JoinTable(name="EmployeeGroup_employees") private List<Employee> employees = new ArrayList<Employee>(); public EmployeeGroup(long id) { this.id = id; } @SuppressWarnings("unused") protected EmployeeGroup() { } public Employee getManager() { return manager; } public void setManager(Employee manager) { this.manager = manager; } public Employee getLead() { return lead; } public void setLead(Employee lead) { this.lead = lead; } public boolean addEmployee(Employee employee) { return employees.add(employee); } public List<Employee> getEmployees() { return employees; } public long getId() { return id; } @Override public String toString() { return id.toString(); } } @Entity @Table(name = "Employee") private static class Employee { @Id @GeneratedValue private Long id; private String name; @OneToMany(cascade = CascadeType.ALL) @Fetch(FetchMode.SUBSELECT) private List<Employee> collaborators = new ArrayList<Employee>(); public String getName() { return name; } @SuppressWarnings("unused") private Employee() { } public Employee(String name) { this.name = name; } public boolean addCollaborator(Employee employee) { return collaborators.add(employee); } public List<Employee> getCollaborators() { return collaborators; } @Override public String toString() { return name; } } }