/* * Hibernate, Relational Persistence for Idiomatic Java * * License: GNU Lesser General Public License (LGPL), version 2.1 or later. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. */ package org.hibernate.test.hql; import org.hibernate.HibernateException; import org.hibernate.QueryException; import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.dialect.AbstractHANADialect; import org.hibernate.dialect.CUBRIDDialect; import org.hibernate.dialect.SybaseASE15Dialect; import org.hibernate.testing.DialectChecks; import org.hibernate.testing.RequiresDialectFeature; import org.hibernate.testing.SkipForDialect; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.junit.Test; import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * Tests the new functionality of allowing scrolling of results which * contain collection fetches. * * @author Steve Ebersole */ public class ScrollableCollectionFetchingTest extends BaseCoreFunctionalTestCase { public String[] getMappings() { return new String[] { "hql/Animal.hbm.xml" }; } @Test public void testTupleReturnFails() { Session s = openSession(); Transaction txn = s.beginTransaction(); try { s.createQuery( "select a, a.weight from Animal a inner join fetch a.offspring" ).scroll(); fail( "scroll allowed with collection fetch and reurning tuples" ); } catch (IllegalArgumentException e) { assertTyping( QueryException.class, e.getCause() ); } catch( HibernateException e ) { // expected result... } txn.commit(); s.close(); } @Test @SkipForDialect(value = SybaseASE15Dialect.class , jiraKey = "HHH-5229") @SkipForDialect(value = { AbstractHANADialect.class }, comment = "HANA only supports forward-only cursors.") public void testScrollingJoinFetchesEmptyResultSet() { Session s = openSession(); Transaction txn = s.beginTransaction(); final String query = "from Animal a left join fetch a.offspring where a.description like :desc order by a.id"; // first, as a control, make sure there are no results int size = s.createQuery( query ).setString( "desc", "root%" ).list().size(); assertEquals( 0, size ); // now get the scrollable results ScrollableResults results = s.createQuery( query ).setString( "desc", "root%" ).scroll(); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.next() ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.previous() ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); results.beforeFirst(); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.next() ); assertFalse( results.first() ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.next() ); results.afterLast(); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.next() ); assertFalse( results.last() ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.next() ); for ( int i=1; i<3; i++ ) { assertFalse( results.scroll( i ) ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.scroll( - i ) ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.setRowNumber( i ) ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.setRowNumber( - i ) ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); } txn.commit(); s.close(); } @Test @SkipForDialect(value = CUBRIDDialect.class, comment = "As of verion 8.4.1 CUBRID doesn't support temporary tables. This test fails with" + "HibernateException: cannot doAfterTransactionCompletion multi-table deletes using dialect not supporting temp tables") @SkipForDialect(value = AbstractHANADialect.class, comment = "HANA only supports forward-only cursors") public void testScrollingJoinFetchesSingleRowResultSet() { Session s = openSession(); Transaction txn = s.beginTransaction(); Animal mother = new Animal(); mother.setDescription( "root-1" ); Animal daughter = new Animal(); daughter.setDescription( "daughter" ); daughter.setMother( mother ); mother.addOffspring( daughter ); s.save( mother ); s.save( daughter ); txn.commit(); s.close(); s = openSession(); txn = s.beginTransaction(); assertNotNull(s .createQuery( "from Animal a left join fetch a.offspring where a.description like :desc order by a.id" ) .setString( "desc", "root%" ) .uniqueResult() ); ScrollableResults results = s .createQuery( "from Animal a left join fetch a.offspring where a.description like :desc order by a.id" ) .setString( "desc", "root%" ).scroll(); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.previous() ); assertTrue( results.next() ); assertTrue( results.isFirst() ); assertTrue( results.isLast() ); assertFalse( results.next() ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertTrue( results.previous() ); assertTrue( results.isFirst() ); assertTrue( results.isLast() ); assertFalse( results.previous() ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertTrue( results.next() ); assertTrue( results.isFirst() ); assertTrue( results.isLast() ); results.beforeFirst(); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.previous() ); assertTrue( results.first() ); assertTrue( results.isFirst() ); assertTrue( results.isLast() ); assertFalse( results.next() ); results.afterLast(); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.next() ); assertTrue( results.last() ); assertTrue( results.isFirst() ); assertTrue( results.isLast() ); assertFalse( results.next() ); assertTrue( results.first() ); assertTrue( results.isFirst() ); assertTrue( results.isLast() ); for ( int i=1; i<3; i++ ) { assertTrue( results.setRowNumber( 1 ) ); assertTrue( results.isFirst() ); assertTrue( results.isLast() ); assertFalse( results.scroll( i ) ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertTrue( results.setRowNumber( 1 ) ); assertTrue( results.isFirst() ); assertTrue( results.isLast() ); assertFalse( results.scroll( - i ) ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); if ( i != 1 ) { assertFalse( results.setRowNumber( i ) ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); assertFalse( results.setRowNumber( - i ) ); assertFalse( results.isFirst() ); assertFalse( results.isLast() ); } } txn.commit(); s.close(); s = openSession(); txn = s.beginTransaction(); s.createQuery( "delete Animal where not description like 'root%'" ).executeUpdate(); s.createQuery( "delete Animal" ).executeUpdate(); txn.commit(); s.close(); } @Test @RequiresDialectFeature( value = DialectChecks.SupportsResultSetPositioningOnForwardOnlyCursorCheck.class, comment = "Driver does not support result set positioning methods on forward-only cursors" ) public void testScrollingJoinFetchesForward() { TestData data = new TestData(); data.prepare(); Session s = openSession(); Transaction txn = s.beginTransaction(); ScrollableResults results = s .createQuery( "from Animal a left join fetch a.offspring where a.description like :desc order by a.id" ) .setString( "desc", "root%" ) .scroll( ScrollMode.FORWARD_ONLY ); int counter = 0; while ( results.next() ) { counter++; Animal animal = ( Animal ) results.get( 0 ); checkResult( animal ); } assertEquals( "unexpected result count", 2, counter ); txn.commit(); s.close(); data.cleanup(); } @Test @SkipForDialect(value = CUBRIDDialect.class, comment = "As of verion 8.4.1 CUBRID doesn't support temporary tables. This test fails with" + "HibernateException: cannot doAfterTransactionCompletion multi-table deletes using dialect not supporting temp tables") @SkipForDialect(value = AbstractHANADialect.class, comment = "HANA only supports forward-only cursors.") public void testScrollingJoinFetchesReverse() { TestData data = new TestData(); data.prepare(); Session s = openSession(); Transaction txn = s.beginTransaction(); ScrollableResults results = s .createQuery( "from Animal a left join fetch a.offspring where a.description like :desc order by a.id" ) .setString( "desc", "root%" ).scroll(); results.afterLast(); int counter = 0; while ( results.previous() ) { counter++; Animal animal = ( Animal ) results.get( 0 ); checkResult( animal ); } assertEquals( "unexpected result count", 2, counter ); txn.commit(); s.close(); data.cleanup(); } @Test @SkipForDialect(value = CUBRIDDialect.class, comment = "As of verion 8.4.1 CUBRID doesn't support temporary tables. This test fails with" + "HibernateException: cannot doAfterTransactionCompletion multi-table deletes using dialect not supporting temp tables") @SkipForDialect(value = AbstractHANADialect.class, comment = "HANA only supports forward-only cursors.") public void testScrollingJoinFetchesPositioning() { TestData data = new TestData(); data.prepare(); Session s = openSession(); Transaction txn = s.beginTransaction(); ScrollableResults results = s .createQuery( "from Animal a left join fetch a.offspring where a.description like :desc order by a.id" ) .setString( "desc", "root%" ) .scroll(); results.first(); Animal animal = ( Animal ) results.get( 0 ); assertEquals( "first() did not return expected row", data.root1Id, animal.getId() ); results.scroll( 1 ); animal = ( Animal ) results.get( 0 ); assertEquals( "scroll(1) did not return expected row", data.root2Id, animal.getId() ); results.scroll( -1 ); animal = ( Animal ) results.get( 0 ); assertEquals( "scroll(-1) did not return expected row", data.root1Id, animal.getId() ); results.setRowNumber( 1 ); animal = ( Animal ) results.get( 0 ); assertEquals( "setRowNumber(1) did not return expected row", data.root1Id, animal.getId() ); results.setRowNumber( 2 ); animal = ( Animal ) results.get( 0 ); assertEquals( "setRowNumber(2) did not return expected row", data.root2Id, animal.getId() ); txn.commit(); s.close(); data.cleanup(); } private void checkResult(Animal animal) { if ( "root-1".equals( animal.getDescription() ) ) { assertEquals( "root-1 did not contain both children", 2, animal.getOffspring().size() ); } else if ( "root-2".equals( animal.getDescription() ) ) { assertEquals( "root-2 did not contain zero children", 0, animal.getOffspring().size() ); } } private class TestData { private Long root1Id; private Long root2Id; private void prepare() { Session s = openSession(); Transaction txn = s.beginTransaction(); Animal mother = new Animal(); mother.setDescription( "root-1" ); Animal another = new Animal(); another.setDescription( "root-2" ); Animal son = new Animal(); son.setDescription( "son"); Animal daughter = new Animal(); daughter.setDescription( "daughter" ); Animal grandson = new Animal(); grandson.setDescription( "grandson" ); Animal grandDaughter = new Animal(); grandDaughter.setDescription( "granddaughter" ); son.setMother( mother ); mother.addOffspring( son ); daughter.setMother( mother ); mother.addOffspring( daughter ); grandson.setMother( daughter ); daughter.addOffspring( grandson ); grandDaughter.setMother( daughter ); daughter.addOffspring( grandDaughter ); s.save( mother ); s.save( another ); s.save( son ); s.save( daughter ); s.save( grandson ); s.save( grandDaughter ); txn.commit(); s.close(); root1Id = mother.getId(); root2Id = another.getId(); } private void cleanup() { Session s = openSession(); Transaction txn = s.beginTransaction(); s.createQuery( "delete Animal where description like 'grand%'" ).executeUpdate(); s.createQuery( "delete Animal where not description like 'root%'" ).executeUpdate(); s.createQuery( "delete Animal" ).executeUpdate(); txn.commit(); s.close(); } } }