From 8b412b91914c1b8185e9b44f2a1339371c7cab4e Mon Sep 17 00:00:00 2001 From: Gail Badner Date: Mon, 12 Apr 2010 18:20:20 +0000 Subject: [PATCH] HHH-1804 HHH-5096 : FetchingScrollableResultsImpl throws SQLException when empty; last() does not move cursor if it is after last git-svn-id: https://svn.jboss.org/repos/hibernate/core/branches/Branch_3_3@19216 1b8cb986-b30d-0410-93ca-fae66ebed9b2 --- .../hql/ast/tree/BinaryLogicOperatorNode.java | 13 +- .../impl/FetchingScrollableResultsImpl.java | 25 ++- .../test/hql/ASTParserLoadingTest.java | 26 +++ .../hql/ScrollableCollectionFetchingTest.java | 191 ++++++++++++++++++ 4 files changed, 252 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java b/core/src/main/java/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java index 769b240ff1..40944ebaf6 100644 --- a/core/src/main/java/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java +++ b/core/src/main/java/org/hibernate/hql/ast/tree/BinaryLogicOperatorNode.java @@ -23,6 +23,7 @@ */ package org.hibernate.hql.ast.tree; +import org.hibernate.type.OneToOneType; import org.hibernate.type.Type; import org.hibernate.Hibernate; import org.hibernate.TypeMismatchException; @@ -80,8 +81,8 @@ public class BinaryLogicOperatorNode extends HqlSqlWalkerNode implements BinaryO // resolve an expected type SessionFactoryImplementor sessionFactory = getSessionFactoryHelper().getFactory(); if ( lhsType != null && rhsType != null ) { - int lhsColumnSpan = lhsType.getColumnSpan( sessionFactory ); - if ( lhsColumnSpan != rhsType.getColumnSpan( sessionFactory ) ) { + int lhsColumnSpan = getColumnSpan( lhsType, sessionFactory ); + if ( lhsColumnSpan != getColumnSpan( rhsType, sessionFactory ) ) { throw new TypeMismatchException( "left and right hand sides of a binary logic operator were incompatibile [" + lhsType.getName() + " : "+ rhsType.getName() + "]" @@ -97,6 +98,14 @@ public class BinaryLogicOperatorNode extends HqlSqlWalkerNode implements BinaryO } } + private int getColumnSpan(Type type, SessionFactoryImplementor sfi) { + int columnSpan = type.getColumnSpan( sfi ); + if ( columnSpan == 0 && type instanceof OneToOneType ) { + columnSpan = ( ( OneToOneType ) type ).getIdentifierOrUniqueKeyType( sfi ).getColumnSpan( sfi ); + } + return columnSpan; + } + /** * Mutate the subtree relating to a row-value-constructor to instead use * a series of ANDed predicates. This allows multi-column type comparisons diff --git a/core/src/main/java/org/hibernate/impl/FetchingScrollableResultsImpl.java b/core/src/main/java/org/hibernate/impl/FetchingScrollableResultsImpl.java index 3bdebe5f4a..d715e04dcf 100644 --- a/core/src/main/java/org/hibernate/impl/FetchingScrollableResultsImpl.java +++ b/core/src/main/java/org/hibernate/impl/FetchingScrollableResultsImpl.java @@ -75,6 +75,12 @@ public class FetchingScrollableResultsImpl extends AbstractScrollableResults { return false; } + if ( isResultSetEmpty() ) { + currentRow = null; + currentPosition = 0; + return false; + } + Object row = getLoader().loadSequentialRowsForward( getResultSet(), getSession(), @@ -183,13 +189,16 @@ public class FetchingScrollableResultsImpl extends AbstractScrollableResults { public boolean last() throws HibernateException { boolean more = false; if ( maxPosition != null ) { + if ( currentPosition > maxPosition.intValue() ) { + more = previous(); + } for ( int i = currentPosition; i < maxPosition.intValue(); i++ ) { more = next(); } } else { try { - if ( getResultSet().isAfterLast() ) { + if ( isResultSetEmpty() || getResultSet().isAfterLast() ) { // should not be able to reach last without maxPosition being set // unless there are no results return false; @@ -313,4 +322,18 @@ public class FetchingScrollableResultsImpl extends AbstractScrollableResults { } return scroll( rowNumber - currentPosition ); } + + private boolean isResultSetEmpty() { + try { + return currentPosition == 0 && ! getResultSet().isBeforeFirst() && ! getResultSet().isAfterLast(); + } + catch( SQLException e ) { + throw JDBCExceptionHelper.convert( + getSession().getFactory().getSQLExceptionConverter(), + e, + "Could not determine if resultset is empty due to exception calling isBeforeFirst or isAfterLast()" + ); + } + } + } diff --git a/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java b/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java index 15a6f42caa..842a3eb717 100644 --- a/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java +++ b/testsuite/src/test/java/org/hibernate/test/hql/ASTParserLoadingTest.java @@ -391,6 +391,32 @@ public class ASTParserLoadingTest extends FunctionalTestCase { return count; } + public void testEntityAndOneToOneReturnedByQuery() { + Session s = openSession(); + s.beginTransaction(); + Human h = new Human(); + h.setName( new Name( "Gail", null, "Badner" ) ); + s.save( h ); + User u = new User(); + u.setUserName( "gbadner" ); + u.setHuman( h ); + s.save( u ); + s.getTransaction().commit(); + s.close(); + + s = openSession(); + s.beginTransaction(); + Object [] result = ( Object [] ) s.createQuery( "from User u, Human h where u.human = h" ).uniqueResult(); + assertNotNull( result ); + assertEquals( u.getUserName(), ( ( User ) result[ 0 ] ).getUserName() ); + assertEquals( h.getName().getFirst(), ( ( Human ) result[ 1 ] ).getName().getFirst() ); + assertSame( ( ( User ) result[ 0 ] ).getHuman(), result[ 1 ] ); + s.createQuery( "delete User" ).executeUpdate(); + s.createQuery( "delete Human" ).executeUpdate(); + s.getTransaction().commit(); + s.close(); + } + public void testNestedComponentIsNull() { // (1) From MapTest originally... // (2) Was then moved into HQLTest... diff --git a/testsuite/src/test/java/org/hibernate/test/hql/ScrollableCollectionFetchingTest.java b/testsuite/src/test/java/org/hibernate/test/hql/ScrollableCollectionFetchingTest.java index dbade1ac73..28a2e4c1d4 100644 --- a/testsuite/src/test/java/org/hibernate/test/hql/ScrollableCollectionFetchingTest.java +++ b/testsuite/src/test/java/org/hibernate/test/hql/ScrollableCollectionFetchingTest.java @@ -1,6 +1,8 @@ // $Id: ScrollableCollectionFetchingTest.java 10977 2006-12-12 23:28:04Z steve.ebersole@jboss.com $ package org.hibernate.test.hql; +import java.util.List; + import junit.framework.Test; import org.hibernate.HibernateException; @@ -47,6 +49,195 @@ public class ScrollableCollectionFetchingTest extends FunctionalTestCase { s.close(); } + public void testScrollingJoinFetchesEmptyResultSet() { + Session s = openSession(); + Transaction txn = s.beginTransaction(); + + assertTrue(s + .createQuery( "from Animal a left join fetch a.offspring where a.description like :desc order by a.id" ) + .setString( "desc", "root%" ) + .list() + .isEmpty() ); + + 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.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(); + } + + 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(); + } + public void testScrollingJoinFetchesForward() { if ( ! supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() ) { return;