package org.hibernate.jpa.test.criteria.fetchscroll; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Join; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.hibernate.Query; import org.hibernate.ScrollableResults; import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; import org.junit.Test; import org.hibernate.testing.TestForIssue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; /** * @author Chris Cranford */ @TestForIssue(jiraKey = "HHH-10062") public class CriteriaToScrollableResultsFetchTest extends BaseEntityManagerFunctionalTestCase { @Override public Class[] getAnnotatedClasses() { return new Class[] { Customer.class, Order.class, OrderLine.class, Product.class, PurchaseOrg.class, Facility.class, Site.class }; } @Test public void testWithScroll() { // Creates data necessary for test Long facilityId = populate(); // Controller iterates the data for ( OrderLine line : getOrderLinesScrolled( facilityId ) ) { // This should ~NOT~ fail with a LazilyLoadException assertNotNull( line.getProduct().getFacility().getSite().getName() ); } } @Test public void testNoScroll() { // Creates data necessary for test. Long facilityId = populate(); // Controller iterates the data for ( OrderLine line : getOrderLinesJpaFetched( facilityId ) ) { assertNotNull( line.getProduct().getFacility().getSite().getName() ); } } private List<OrderLine> getOrderLinesScrolled(Long facilityId) { EntityManager em = getOrCreateEntityManager(); try { em.getTransaction().begin(); Set<PurchaseOrg> purchaseOrgs = getPurchaseOrgsByFacilityId( facilityId, em ); assertEquals( "Expected one purchase organization.", 1, purchaseOrgs.size() ); System.out.println( purchaseOrgs ); TypedQuery<OrderLine> query = getOrderLinesQuery( purchaseOrgs, em ); Query hibernateQuery = query.unwrap( Query.class ); hibernateQuery.setReadOnly( true ); hibernateQuery.setCacheable( false ); List<OrderLine> lines = new ArrayList<>(); ScrollableResults scrollableResults = hibernateQuery.scroll(); scrollableResults.last(); int rows = scrollableResults.getRowNumber() + 1; scrollableResults.beforeFirst(); while ( scrollableResults.next() ) { lines.add( (OrderLine) scrollableResults.get( 0 ) ); } assertNotNull( lines ); assertEquals( "Expected one order line", 1, lines.size() ); em.getTransaction().commit(); return lines; } catch ( Throwable t ) { if ( em.getTransaction().isActive() ) { em.getTransaction().rollback(); } throw t; } finally { em.close(); } } private List<OrderLine> getOrderLinesJpaFetched(Long facilityId) { EntityManager em = getOrCreateEntityManager(); try { em.getTransaction().begin(); Set<PurchaseOrg> purchaseOrgs = getPurchaseOrgsByFacilityId( facilityId, em ); assertEquals( "Expected one purchase organization.", 1, purchaseOrgs.size() ); System.out.println( purchaseOrgs ); TypedQuery<OrderLine> query = getOrderLinesQuery( purchaseOrgs, em ); List<OrderLine> lines = query.getResultList(); em.getTransaction().commit(); return lines; } catch ( Throwable t ) { if ( em.getTransaction().isActive() ) { em.getTransaction().rollback(); } throw t; } finally { em.close(); } } private Set<PurchaseOrg> getPurchaseOrgsByFacilityId(Long facilityId, EntityManager em) { Set<PurchaseOrg> orgs = new HashSet<>(); try { for ( PurchaseOrg purchaseOrg : findAll( PurchaseOrg.class, em ) ) { for ( Facility facility : purchaseOrg.getFacilities() ) { if ( facility.getId().equals( facilityId ) ) { orgs.add( purchaseOrg ); break; } } } } catch ( Exception e ) { } finally { return orgs; } } private <T> List<T> findAll(Class<T> clazz, EntityManager em) { return em.createQuery( "SELECT o FROM " + clazz.getSimpleName() + " o", clazz ).getResultList(); } private TypedQuery<OrderLine> getOrderLinesQuery(Collection<PurchaseOrg> purchaseOrgs, EntityManager em) { CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery<OrderLine> query = cb.createQuery( OrderLine.class ); Root<OrderLine> root = query.from( OrderLine.class ); Path<OrderLineId> idPath = root.get( OrderLine_.id ); Join<OrderLine, Product> productJoin = (Join<OrderLine, Product>) root.fetch( OrderLine_.product ); productJoin.fetch( Product_.facility ).fetch( Facility_.site ); Join<OrderLine, Order> orderJoin = (Join<OrderLine, Order>) root.fetch( OrderLine_.header ); orderJoin.fetch( Order_.purchaseOrg ); Set<Long> ids = new HashSet<>(); for ( PurchaseOrg org : purchaseOrgs ) ids.add( org.getId() ); List<Predicate> predicates = new ArrayList<>(); predicates.add( idPath.get( OrderLineId_.purchaseOrgId ).in( ids ) ); query.select( root ).where( predicates.toArray( new Predicate[ predicates.size() ] ) ); return em.createQuery( query ); } private Long populate() { final EntityManager em = getOrCreateEntityManager(); try { em.getTransaction().begin(); Customer customer = new Customer(); customer.setName( "MGM" ); em.persist( customer ); Site site = new Site(); site.setName( "NEW YORK" ); site.setCustomer( customer ); em.persist( site ); Facility facility = new Facility(); facility.setName( "ACME" ); facility.setSite( site ); facility.setCustomer( customer ); em.persist( facility ); PurchaseOrg purchaseOrg = new PurchaseOrg(); purchaseOrg.setName( "LOONEY TUNES" ); purchaseOrg.setCustomer( customer ); purchaseOrg.setFacilities( Arrays.asList( facility ) ); em.persist( purchaseOrg ); Product product = new Product( facility, "0000 0001" ); em.persist( product ); Order order = new Order( purchaseOrg, "12345" ); OrderLine line1 = new OrderLine( order, 1L, product ); Set<OrderLine> lines = new HashSet<>(); lines.add( line1 ); order.setLines( lines ); em.persist( order ); em.getTransaction().commit(); return facility.getId(); } catch( Throwable t) { if ( em.getTransaction().isActive() ) { em.getTransaction().rollback(); } throw t; } finally { em.close(); } } }