Re-enabled stream tests

This commit is contained in:
Andrea Boriero 2021-11-24 18:21:29 +01:00 committed by Andrea Boriero
parent acd78256eb
commit 36fa3fca8e
5 changed files with 470 additions and 334 deletions

View File

@ -132,6 +132,10 @@ public interface Query<R> extends TypedQuery<R>, CommonQueryContract {
return list(); return list();
} }
default Stream<R> getResultStream() {
return stream();
}
/** /**
* Convenience method to return a single instance that matches * Convenience method to return a single instance that matches
* the query, or {@code null} if the query returns no results. * the query, or {@code null} if the query returns no results.

View File

@ -0,0 +1,196 @@
/*
* 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.orm.test.stream.basic;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hibernate.testing.orm.junit.ExtraAssertions.assertTyping;
/**
* @author Steve Ebersole
*/
@DomainModel(
annotatedClasses = BasicStreamTest.MyEntity.class
)
@SessionFactory
public class BasicStreamTest {
@Test
public void basicStreamTest(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
// mainly we want to make sure that closing the Stream releases the ScrollableResults too
assertThat( ( (SessionImplementor) session ).getJdbcCoordinator()
.getLogicalConnection()
.getResourceRegistry()
.hasRegisteredResources(), is( false ) );
final Stream<MyEntity> stream = session.createQuery( "from MyEntity", MyEntity.class ).stream();
try {
stream.forEach( System.out::println );
assertThat( session.getJdbcCoordinator()
.getLogicalConnection()
.getResourceRegistry()
.hasRegisteredResources(), is( true ) );
}
finally {
stream.close();
assertThat( session.getJdbcCoordinator()
.getLogicalConnection()
.getResourceRegistry()
.hasRegisteredResources(), is( false ) );
}
}
);
}
@Test
@TestForIssue(jiraKey = "HHH-10824")
public void testQueryStream(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
MyEntity e = new MyEntity();
e.id = 1;
e.name = "Test";
session.persist( e );
}
);
scope.inSession(
session -> {
// Test stream query without type.
try (Stream stream = session.createQuery( "From MyEntity" ).stream()) {
Object result = stream.findFirst().orElse( null );
assertTyping( MyEntity.class, result );
}
// Test stream query with type.
try (final Stream<MyEntity> stream = session.createQuery( "From MyEntity", MyEntity.class )
.stream()) {
assertTyping( MyEntity.class, stream.findFirst().orElse( null ) );
}
// Test stream query using forEach
try (Stream<MyEntity> stream = session.createQuery( "From MyEntity", MyEntity.class )
.stream()) {
stream.forEach( i -> {
assertTyping( MyEntity.class, i );
} );
}
try (Stream<Object[]> stream = session.createQuery( "SELECT me.id, me.name FROM MyEntity me" )
.stream()) {
stream.forEach( i -> {
assertTyping( Integer.class, i[0] );
assertTyping( String.class, i[1] );
} );
}
}
);
}
@Test
@TestForIssue(jiraKey = "HHH-11743")
public void testTupleStream(SessionFactoryScope scope) {
scope.inTransaction( session -> {
MyEntity entity = new MyEntity();
entity.id = 2;
entity.name = "an entity";
session.persist( entity );
} );
//test tuple stream using criteria
scope.inTransaction( session -> {
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = cb.createTupleQuery();
Root<MyEntity> me = criteria.from( MyEntity.class );
criteria.multiselect( me.get( "id" ), me.get( "name" ) );
try (Stream<Tuple> data = session.createQuery( criteria ).stream()) {
data.forEach( tuple -> assertTyping( Tuple.class, tuple ) );
}
} );
//test tuple stream using JPQL
scope.inTransaction( session -> {
try (Stream<Tuple> data = session.createQuery( "SELECT me.id, me.name FROM MyEntity me", Tuple.class )
.stream()) {
data.forEach( tuple -> assertTyping( Tuple.class, tuple ) );
}
} );
}
@Test
public void basicStreamTestWithExplicitOnClose(SessionFactoryScope scope) {
scope.inTransaction(
session -> {
AtomicInteger onCloseCount = new AtomicInteger();
// mainly we want to make sure that closing the Stream releases the ScrollableResults too
assertThat( session.getJdbcCoordinator()
.getLogicalConnection()
.getResourceRegistry()
.hasRegisteredResources(), is( false ) );
assertThat( onCloseCount.get(), equalTo( 0 ) );
try (final Stream<MyEntity> stream = session.createQuery( "from MyEntity", MyEntity.class )
.stream()
.onClose(
onCloseCount::incrementAndGet )) {
assertThat( onCloseCount.get(), equalTo( 0 ) );
stream.forEach( System.out::println );
assertThat( session.getJdbcCoordinator()
.getLogicalConnection()
.getResourceRegistry()
.hasRegisteredResources(), is( true ) );
}
assertThat( session.getJdbcCoordinator()
.getLogicalConnection()
.getResourceRegistry()
.hasRegisteredResources(), is( false ) );
assertThat( onCloseCount.get(), equalTo( 1 ) );
}
);
}
@Entity(name = "MyEntity")
@Table(name = "MyEntity")
public static class MyEntity {
@Id
public Integer id;
public String name;
}
}

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later * 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 * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/ */
package org.hibernate.test.stream.basic; package org.hibernate.orm.test.stream.basic;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -17,9 +17,6 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.H2Dialect;
@ -28,46 +25,53 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.ReflectHelper; import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.resource.jdbc.ResourceRegistry; import org.hibernate.resource.jdbc.ResourceRegistry;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; import org.hibernate.testing.orm.junit.DomainModel;
import org.junit.Test; import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping; import static org.hibernate.testing.orm.junit.ExtraAssertions.assertTyping;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.Assert.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.Assert.fail;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class JpaStreamTest extends BaseNonConfigCoreFunctionalTestCase { @DomainModel(
annotatedClasses = JpaStreamTest.MyEntity.class
@Override )
protected Class[] getAnnotatedClasses() { @SessionFactory
return new Class[] { MyEntity.class }; public class JpaStreamTest {
}
@Test @Test
@TestForIssue(jiraKey = "HHH-11907") @TestForIssue(jiraKey = "HHH-11907")
public void testQueryStream() { public void testQueryStream(SessionFactoryScope scope) {
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
MyEntity e= new MyEntity(); MyEntity e = new MyEntity();
e.id = 1; e.id = 1;
e.name = "Test"; e.name = "Test";
session.persist( e ); session.persist( e );
} ); } );
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
// Test stream query without type. // Test stream query without type.
Object result = session.createQuery( "From MyEntity" ).getResultStream().findFirst().orElse( null ); Object result = session.createQuery( "From MyEntity" ).getResultStream().findFirst().orElse( null );
assertTyping( MyEntity.class, result ); assertTyping( MyEntity.class, result );
// Test stream query with type. // Test stream query with type.
result = session.createQuery( "From MyEntity", MyEntity.class ).getResultStream().findFirst().orElse( null ); result = session.createQuery( "From MyEntity", MyEntity.class )
.getResultStream()
.findFirst()
.orElse( null );
assertTyping( MyEntity.class, result ); assertTyping( MyEntity.class, result );
// Test stream query using forEach // Test stream query using forEach
@ -79,15 +83,15 @@ public class JpaStreamTest extends BaseNonConfigCoreFunctionalTestCase {
data.forEach( i -> { data.forEach( i -> {
assertTyping( Integer.class, i[0] ); assertTyping( Integer.class, i[0] );
assertTyping( String.class, i[1] ); assertTyping( String.class, i[1] );
}); } );
} ); } );
} }
@Test @Test
@RequiresDialect(H2Dialect.class) @RequiresDialect(H2Dialect.class)
@TestForIssue( jiraKey = {"HHH-13872", "HHH-14449"}) @TestForIssue(jiraKey = { "HHH-13872", "HHH-14449" })
public void testStreamCloseOnTerminalOperation() { public void testStreamCloseOnTerminalOperation(SessionFactoryScope scope) {
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
session.createQuery( "delete from MyEntity" ).executeUpdate(); session.createQuery( "delete from MyEntity" ).executeUpdate();
for ( int i = 1; i <= 10; i++ ) { for ( int i = 1; i <= 10; i++ ) {
@ -104,7 +108,7 @@ public class JpaStreamTest extends BaseNonConfigCoreFunctionalTestCase {
// run without onClose callbacks // run without onClose callbacks
this.runTerminalOperationTests(noOp, Collections.emptyList(), noOp, false, false); this.runTerminalOperationTests( noOp, Collections.emptyList(), noOp, false, false, scope );
AtomicInteger onClose1Count = new AtomicInteger(); AtomicInteger onClose1Count = new AtomicInteger();
AtomicInteger onClose2Count = new AtomicInteger(); AtomicInteger onClose2Count = new AtomicInteger();
@ -131,7 +135,8 @@ public class JpaStreamTest extends BaseNonConfigCoreFunctionalTestCase {
assertThat( onClose3Count ).hasValue( 1 ); assertThat( onClose3Count ).hasValue( 1 );
}, },
false, // no flatMap before onClose false, // no flatMap before onClose
false // no flatMap after onClose false, // no flatMap after onClose
scope
); );
this.runTerminalOperationTests( this.runTerminalOperationTests(
@ -153,7 +158,8 @@ public class JpaStreamTest extends BaseNonConfigCoreFunctionalTestCase {
assertThat( onClose3Count ).hasValue( 1 ); assertThat( onClose3Count ).hasValue( 1 );
}, },
true, // run a flatMap operation before onClose true, // run a flatMap operation before onClose
false // no flatMap after onClose false, // no flatMap after onClose
scope
); );
this.runTerminalOperationTests( this.runTerminalOperationTests(
@ -175,7 +181,8 @@ public class JpaStreamTest extends BaseNonConfigCoreFunctionalTestCase {
assertThat( onClose3Count ).hasValue( 1 ); assertThat( onClose3Count ).hasValue( 1 );
}, },
false, // no flatMap before onClose false, // no flatMap before onClose
true // run a flatMap operation after onClose true, // run a flatMap operation after onClose
scope
); );
this.runTerminalOperationTests( this.runTerminalOperationTests(
@ -197,192 +204,290 @@ public class JpaStreamTest extends BaseNonConfigCoreFunctionalTestCase {
assertThat( onClose3Count ).hasValue( 1 ); assertThat( onClose3Count ).hasValue( 1 );
}, },
true, // run a flatMap operation before onClose true, // run a flatMap operation before onClose
true // run a flatMap operation after onClose true, // run a flatMap operation after onClose
scope
); );
} }
private void runTerminalOperationTests( private void runTerminalOperationTests(
Runnable prepare, List<Runnable> onCloseCallbacks, Runnable onCloseAssertion, Runnable prepare, List<Runnable> onCloseCallbacks,
boolean flatMapBefore, boolean flatMapAfter) { Runnable onCloseAssertion,
boolean flatMapBefore,
boolean flatMapAfter,
SessionFactoryScope scope) {
// collect as list // collect as list
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Stream<MyEntity> stream = getMyEntityStream(prepare, session, onCloseCallbacks, flatMapBefore, flatMapAfter); Stream<MyEntity> stream = getMyEntityStream(
prepare,
ResourceRegistry resourceRegistry = resourceRegistry(session); session,
assertTrue( resourceRegistry.hasRegisteredResources() ); onCloseCallbacks,
flatMapBefore,
List<MyEntity> entities = stream.collect( Collectors.toList() ) ; flatMapAfter
assertEquals(10, entities.size()); );
ResourceRegistry resourceRegistry = resourceRegistry( session );
assertFalse( resourceRegistry.hasRegisteredResources() ); try {
List<MyEntity> entities = stream.collect( Collectors.toList() );
assertTrue( resourceRegistry.hasRegisteredResources() );
assertEquals( 10, entities.size() );
}
finally {
stream.close();
assertFalse( resourceRegistry.hasRegisteredResources() );
}
onCloseAssertion.run(); onCloseAssertion.run();
} ); } );
// forEach (TestCase based on attachment EntityManagerIllustrationTest.java in HHH-14449) // forEach (TestCase based on attachment EntityManagerIllustrationTest.java in HHH-14449)
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Stream<MyEntity> stream = getMyEntityStream(prepare, session, onCloseCallbacks, flatMapBefore, flatMapAfter); Stream<MyEntity> stream = getMyEntityStream(
prepare,
session,
onCloseCallbacks,
flatMapBefore,
flatMapAfter
);
ResourceRegistry resourceRegistry = resourceRegistry(session); ResourceRegistry resourceRegistry = resourceRegistry( session );
assertTrue( resourceRegistry.hasRegisteredResources() ); try {
AtomicInteger count = new AtomicInteger();
AtomicInteger count = new AtomicInteger(); stream.forEach( myEntity -> count.incrementAndGet() );
assertTrue( resourceRegistry.hasRegisteredResources() );
stream.forEach(myEntity -> count.incrementAndGet()); assertEquals( 10, count.get() );
}
assertEquals(10, count.get()); finally {
stream.close();
assertFalse( resourceRegistry.hasRegisteredResources() ); assertFalse( resourceRegistry.hasRegisteredResources() );
}
onCloseAssertion.run(); onCloseAssertion.run();
} ); } );
// filter (always true) + forEach (TestCase based on attachment EntityManagerIllustrationTest.java in HHH-14449) // filter (always true) + forEach (TestCase based on attachment EntityManagerIllustrationTest.java in HHH-14449)
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Stream<MyEntity> stream = getMyEntityStream(prepare, session, onCloseCallbacks, flatMapBefore, flatMapAfter); Stream<MyEntity> stream = getMyEntityStream(
prepare,
session,
onCloseCallbacks,
flatMapBefore,
flatMapAfter
);
ResourceRegistry resourceRegistry = resourceRegistry(session); ResourceRegistry resourceRegistry = resourceRegistry( session );
assertTrue( resourceRegistry.hasRegisteredResources() );
AtomicInteger count = new AtomicInteger(); try {
AtomicInteger count = new AtomicInteger();
stream.filter(Objects::nonNull).forEach(myEntity -> count.incrementAndGet()); stream.filter( Objects::nonNull ).forEach( myEntity -> count.incrementAndGet() );
assertTrue( resourceRegistry.hasRegisteredResources() );
assertEquals(10, count.get()); assertEquals( 10, count.get() );
}
assertFalse( resourceRegistry.hasRegisteredResources() ); finally {
stream.close();
assertFalse( resourceRegistry.hasRegisteredResources() );
}
onCloseAssertion.run(); onCloseAssertion.run();
} ); } );
// filter (partially true) + forEach (TestCase based on attachment EntityManagerIllustrationTest.java in HHH-14449) // filter (partially true) + forEach (TestCase based on attachment EntityManagerIllustrationTest.java in HHH-14449)
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Stream<MyEntity> stream = getMyEntityStream(prepare, session, onCloseCallbacks, flatMapBefore, flatMapAfter); Stream<MyEntity> stream = getMyEntityStream(
prepare,
session,
onCloseCallbacks,
flatMapBefore,
flatMapAfter
);
ResourceRegistry resourceRegistry = resourceRegistry(session); ResourceRegistry resourceRegistry = resourceRegistry( session );
assertTrue( resourceRegistry.hasRegisteredResources() );
AtomicInteger count = new AtomicInteger(); try {
AtomicInteger count = new AtomicInteger();
stream.filter(entity -> entity.getId() % 2 == 0).forEach(myEntity -> count.incrementAndGet()); stream.filter( entity -> entity.getId() % 2 == 0 ).forEach( myEntity -> count.incrementAndGet() );
assertTrue( resourceRegistry.hasRegisteredResources() );
assertEquals(5, count.get()); assertEquals( 5, count.get() );
}
assertFalse( resourceRegistry.hasRegisteredResources() ); finally {
stream.close();
assertFalse( resourceRegistry.hasRegisteredResources() );
}
onCloseAssertion.run(); onCloseAssertion.run();
} ); } );
// multiple chained operations (TestCase based on attachment EntityManagerIllustrationTest.java in HHH-14449) // multiple chained operations (TestCase based on attachment EntityManagerIllustrationTest.java in HHH-14449)
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Stream<MyEntity> stream = getMyEntityStream(prepare, session, onCloseCallbacks, flatMapBefore, flatMapAfter); Stream<MyEntity> stream = getMyEntityStream(
prepare,
session,
onCloseCallbacks,
flatMapBefore,
flatMapAfter
);
ResourceRegistry resourceRegistry = resourceRegistry(session); ResourceRegistry resourceRegistry = resourceRegistry( session );
assertTrue( resourceRegistry.hasRegisteredResources() );
AtomicInteger count = new AtomicInteger(); try {
AtomicInteger count = new AtomicInteger();
stream stream
.filter(Objects::nonNull) .filter( Objects::nonNull )
.map(Optional::of) .map( Optional::of )
.filter(Optional::isPresent) .filter( Optional::isPresent )
.map(Optional::get) .map( Optional::get )
.forEach(myEntity -> count.incrementAndGet()); .forEach( myEntity -> count.incrementAndGet() );
assertTrue( resourceRegistry.hasRegisteredResources() );
assertEquals(10, count.get()); assertEquals( 10, count.get() );
}
assertFalse( resourceRegistry.hasRegisteredResources() ); finally {
stream.close();
assertFalse( resourceRegistry.hasRegisteredResources() );
}
onCloseAssertion.run(); onCloseAssertion.run();
} ); } );
// mapToInt // mapToInt
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Stream<MyEntity> stream = getMyEntityStream(prepare, session, onCloseCallbacks, flatMapBefore, flatMapAfter); Stream<MyEntity> stream = getMyEntityStream(
prepare,
session,
onCloseCallbacks,
flatMapBefore,
flatMapAfter
);
ResourceRegistry resourceRegistry = resourceRegistry(session); ResourceRegistry resourceRegistry = resourceRegistry( session );
assertTrue( resourceRegistry.hasRegisteredResources() ); try {
int sum = stream.mapToInt( MyEntity::getId ).sum();
assertTrue( resourceRegistry.hasRegisteredResources() );
int sum = stream.mapToInt( MyEntity::getId ).sum(); assertEquals( 55, sum );
assertEquals(55, sum); }
finally {
assertFalse( resourceRegistry.hasRegisteredResources() ); stream.close();
assertFalse( resourceRegistry.hasRegisteredResources() );
}
onCloseAssertion.run(); onCloseAssertion.run();
} ); } );
// mapToLong // mapToLong
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Stream<MyEntity> stream = getMyEntityStream(prepare, session, onCloseCallbacks, flatMapBefore, flatMapAfter); Stream<MyEntity> stream = getMyEntityStream(
prepare,
session,
onCloseCallbacks,
flatMapBefore,
flatMapAfter
);
ResourceRegistry resourceRegistry = resourceRegistry(session); ResourceRegistry resourceRegistry = resourceRegistry( session );
assertTrue( resourceRegistry.hasRegisteredResources() );
long result = stream.mapToLong( entity -> entity.id * 10 ).min().getAsLong(); try {
assertEquals(10, result); long result = stream.mapToLong( entity -> entity.id * 10 ).min().getAsLong();
assertTrue( resourceRegistry.hasRegisteredResources() );
assertFalse( resourceRegistry.hasRegisteredResources() ); assertEquals( 10, result );
}
finally {
stream.close();
assertFalse( resourceRegistry.hasRegisteredResources() );
}
onCloseAssertion.run(); onCloseAssertion.run();
} ); } );
// mapToDouble // mapToDouble
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Stream<MyEntity> stream = getMyEntityStream(prepare, session, onCloseCallbacks, flatMapBefore, flatMapAfter); Stream<MyEntity> stream = getMyEntityStream(
prepare,
session,
onCloseCallbacks,
flatMapBefore,
flatMapAfter
);
ResourceRegistry resourceRegistry = resourceRegistry(session); ResourceRegistry resourceRegistry = resourceRegistry( session );
assertTrue( resourceRegistry.hasRegisteredResources() ); try {
double result = stream.mapToDouble( entity -> entity.id * 0.1D ).max().getAsDouble();
assertTrue( resourceRegistry.hasRegisteredResources() );
double result = stream.mapToDouble( entity -> entity.id * 0.1D ).max().getAsDouble(); assertEquals( 1, result, 0.1 );
assertEquals(1, result, 0.1); }
finally {
assertFalse( resourceRegistry.hasRegisteredResources() ); stream.close();
assertFalse( resourceRegistry.hasRegisteredResources() );
}
onCloseAssertion.run(); onCloseAssertion.run();
} ); } );
//Test call close explicitly //Test call close explicitly
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
try (Stream<Long> stream = getLongStream(
try (Stream<Long> stream = getLongStream(prepare, session, onCloseCallbacks, flatMapBefore, flatMapAfter)) { prepare,
session,
onCloseCallbacks,
flatMapBefore,
flatMapAfter
)) {
ResourceRegistry resourceRegistry = resourceRegistry( session ); ResourceRegistry resourceRegistry = resourceRegistry( session );
assertTrue( resourceRegistry.hasRegisteredResources() ); try {
Object[] result = stream.sorted().skip( 5 ).limit( 5 ).toArray();
assertTrue( resourceRegistry.hasRegisteredResources() );
Object[] result = stream.sorted().skip( 5 ).limit( 5 ).toArray(); assertEquals( 5, result.length );
assertEquals( 5, result.length ); assertEquals( 6, result[0] );
assertEquals( 6, result[0] ); assertEquals( 10, result[4] );
assertEquals( 10, result[4] );
assertFalse( resourceRegistry.hasRegisteredResources() ); }
finally {
stream.close();
assertFalse( resourceRegistry.hasRegisteredResources() );
}
onCloseAssertion.run(); onCloseAssertion.run();
} }
} ); } );
//Test Java 9 Stream methods //Test Java 9 Stream methods
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Method takeWhileMethod = ReflectHelper.getMethod( Stream.class, "takeWhile", Predicate.class ); Method takeWhileMethod = ReflectHelper.getMethod( Stream.class, "takeWhile", Predicate.class );
if ( takeWhileMethod != null ) { if ( takeWhileMethod != null ) {
try (Stream<Long> stream = getLongStream(prepare, session, onCloseCallbacks, flatMapBefore, flatMapAfter)) { try (Stream<Long> stream = getLongStream(
prepare,
session,
onCloseCallbacks,
flatMapBefore,
flatMapAfter
)) {
ResourceRegistry resourceRegistry = resourceRegistry( session ); ResourceRegistry resourceRegistry = resourceRegistry( session );
assertTrue( resourceRegistry.hasRegisteredResources() ); try {
Predicate<Integer> predicate = id -> id <= 5; Predicate<Integer> predicate = id -> id <= 5;
Stream<Integer> takeWhileStream = (Stream<Integer>) takeWhileMethod.invoke( stream, predicate ); Stream<Integer> takeWhileStream = (Stream<Integer>) takeWhileMethod.invoke( stream, predicate );
List<Integer> result = takeWhileStream.collect( Collectors.toList() ); List<Integer> result = takeWhileStream.collect( Collectors.toList() );
assertTrue( resourceRegistry.hasRegisteredResources() );
assertEquals( 5, result.size() ); assertEquals( 5, result.size() );
assertTrue( result.contains( 1 ) ); assertTrue( result.contains( 1 ) );
assertTrue( result.contains( 3 ) ); assertTrue( result.contains( 3 ) );
assertTrue( result.contains( 5 ) ); assertTrue( result.contains( 5 ) );
}
assertFalse( resourceRegistry.hasRegisteredResources() ); finally {
stream.close();
assertFalse( resourceRegistry.hasRegisteredResources() );
}
onCloseAssertion.run(); onCloseAssertion.run();
} }
@ -392,27 +497,36 @@ public class JpaStreamTest extends BaseNonConfigCoreFunctionalTestCase {
} }
} ); } );
doInHibernate( this::sessionFactory, session -> { scope.inTransaction( session -> {
Method dropWhileMethod = ReflectHelper.getMethod( Stream.class, "dropWhile", Predicate.class ); Method dropWhileMethod = ReflectHelper.getMethod( Stream.class, "dropWhile", Predicate.class );
if ( dropWhileMethod != null ) { if ( dropWhileMethod != null ) {
try (Stream<Long> stream = getLongStream(prepare, session, onCloseCallbacks, flatMapBefore, flatMapAfter)) { try (Stream<Long> stream = getLongStream(
prepare,
session,
onCloseCallbacks,
flatMapBefore,
flatMapAfter
)) {
ResourceRegistry resourceRegistry = resourceRegistry( session ); ResourceRegistry resourceRegistry = resourceRegistry( session );
assertTrue( resourceRegistry.hasRegisteredResources() );
Predicate<Integer> predicate = id -> id <= 5; Predicate<Integer> predicate = id -> id <= 5;
Stream<Integer> dropWhileStream = (Stream<Integer>) dropWhileMethod.invoke( stream, predicate ); Stream<Integer> dropWhileStream = (Stream<Integer>) dropWhileMethod.invoke( stream, predicate );
try {
List<Integer> result = dropWhileStream.collect( Collectors.toList() );
assertTrue( resourceRegistry.hasRegisteredResources() );
List<Integer> result = dropWhileStream.collect( Collectors.toList() ); assertEquals( 5, result.size() );
assertTrue( result.contains( 6 ) );
assertEquals( 5, result.size() ); assertTrue( result.contains( 8 ) );
assertTrue( result.contains( 6 ) ); assertTrue( result.contains( 10 ) );
assertTrue( result.contains( 8 ) ); }
assertTrue( result.contains( 10 ) ); finally {
stream.close();
assertFalse( resourceRegistry.hasRegisteredResources() ); assertFalse( resourceRegistry.hasRegisteredResources() );
}
onCloseAssertion.run(); onCloseAssertion.run();
} }
@ -486,7 +600,7 @@ public class JpaStreamTest extends BaseNonConfigCoreFunctionalTestCase {
} }
@Entity(name = "MyEntity") @Entity(name = "MyEntity")
@Table(name="MyEntity") @Table(name = "MyEntity")
public static class MyEntity { public static class MyEntity {
@Id @Id

View File

@ -1,183 +0,0 @@
/*
* 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.stream.basic;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Tuple;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import org.hibernate.Session;
import org.hibernate.boot.MetadataSources;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
/**
* @author Steve Ebersole
*/
public class BasicStreamTest extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( MyEntity.class );
}
@Test
public void basicStreamTest() {
Session session = openSession();
session.getTransaction().begin();
// mainly we want to make sure that closing the Stream releases the ScrollableResults too
assertThat( ( (SessionImplementor) session ).getJdbcCoordinator().getLogicalConnection().getResourceRegistry().hasRegisteredResources(), is( false ) );
final Stream<MyEntity> stream = session.createQuery( "from MyEntity", MyEntity.class ).stream();
assertThat( ( (SessionImplementor) session ).getJdbcCoordinator().getLogicalConnection().getResourceRegistry().hasRegisteredResources(), is( true ) );
stream.forEach( System.out::println );
assertThat( ( (SessionImplementor) session ).getJdbcCoordinator().getLogicalConnection().getResourceRegistry().hasRegisteredResources(), is( false ) );
stream.close();
assertThat( ( (SessionImplementor) session ).getJdbcCoordinator().getLogicalConnection().getResourceRegistry().hasRegisteredResources(), is( false ) );
session.getTransaction().commit();
session.close();
}
@Test
@TestForIssue(jiraKey = "HHH-10824")
public void testQueryStream() {
Session session = openSession();
try {
session.getTransaction().begin();
MyEntity e= new MyEntity();
e.id = 1;
e.name = "Test";
session.persist( e );
session.getTransaction().commit();
session.clear();
// Test stream query without type.
Object result = session.createQuery( "From MyEntity" ).stream().findFirst().orElse( null );
assertTyping( MyEntity.class, result );
// Test stream query with type.
result = session.createQuery( "From MyEntity", MyEntity.class ).stream().findFirst().orElse( null );
assertTyping( MyEntity.class, result );
// Test stream query using forEach
session.createQuery( "From MyEntity", MyEntity.class ).stream().forEach( i -> {
assertTyping( MyEntity.class, i );
} );
Stream<Object[]> data = session.createQuery( "SELECT me.id, me.name FROM MyEntity me" ).stream();
data.forEach( i -> {
assertTyping( Integer.class, i[0] );
assertTyping( String.class, i[1] );
});
}
finally {
session.close();
}
}
@Test
@TestForIssue(jiraKey = "HHH-11743")
public void testTupleStream() {
doInHibernate( this::sessionFactory, session -> {
MyEntity entity = new MyEntity();
entity.id = 2;
entity.name = "an entity";
session.persist( entity );
} );
//test tuple stream using criteria
doInHibernate( this::sessionFactory, session -> {
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Tuple> criteria = cb.createTupleQuery();
Root<MyEntity> me = criteria.from( MyEntity.class );
criteria.multiselect( me.get( "id" ), me.get( "name" ) );
Stream<Tuple> data = session.createQuery( criteria ).stream();
data.forEach( tuple -> assertTyping( Tuple.class, tuple ) );
} );
//test tuple stream using JPQL
doInHibernate( this::sessionFactory, session -> {
Stream<Tuple> data = session.createQuery( "SELECT me.id, me.name FROM MyEntity me", Tuple.class ).stream();
data.forEach( tuple -> assertTyping( Tuple.class, tuple ) );
} );
}
@Test
public void basicStreamTestWithExplicitOnClose() {
Session session = openSession();
session.getTransaction().begin();
AtomicInteger onCloseCount = new AtomicInteger();
// mainly we want to make sure that closing the Stream releases the ScrollableResults too
assertThat( ( (SessionImplementor) session ).getJdbcCoordinator()
.getLogicalConnection()
.getResourceRegistry()
.hasRegisteredResources(), is( false ) );
assertThat( onCloseCount.get(), equalTo( 0 ) );
final Stream<MyEntity> stream = session.createQuery( "from MyEntity", MyEntity.class ).stream().onClose(
onCloseCount::incrementAndGet );
assertThat( ( (SessionImplementor) session ).getJdbcCoordinator()
.getLogicalConnection()
.getResourceRegistry()
.hasRegisteredResources(), is( true ) );
assertThat( onCloseCount.get(), equalTo( 0 ) );
stream.forEach( System.out::println );
assertThat( ( (SessionImplementor) session ).getJdbcCoordinator()
.getLogicalConnection()
.getResourceRegistry()
.hasRegisteredResources(), is( false ) );
assertThat( onCloseCount.get(), equalTo( 1 ) );
stream.close();
assertThat( ( (SessionImplementor) session ).getJdbcCoordinator()
.getLogicalConnection()
.getResourceRegistry()
.hasRegisteredResources(), is( false ) );
assertThat( onCloseCount.get(), equalTo( 1 ) );
session.getTransaction().commit();
session.close();
}
@Entity(name = "MyEntity")
@Table(name="MyEntity")
public static class MyEntity {
@Id
public Integer id;
public String name;
}
}

View File

@ -62,9 +62,9 @@ Passing null or not is now triggered by whether setting the parameter was called
=== HQL results === HQL results
Hql queries that joins an Entity `Person` with an Entity `Address` without specifying a select clause `from Address a join a.address` does not return anymore a `List<Object[]>`but a list of `Person`. HQL queries that use joins without specifying a select clause e.g. `from Person p join p.address` do not return a `List<Object[]>` with an entry per join anymore, but a list of `Person`.
The hql query `select a,ad from Address a join a.address ad` returns instead a `List<Object[]>`. The HQL query `select p,a from Person p join p.address a` returns instead a `List<Object[]>`.
e.g. e.g.
``` ```
@ -85,14 +85,19 @@ class Address {
} }
List<A> result = session.createQuery("from Address a join a.address").list() List<Person> result = session.createQuery("from Person p join p.address").list();
List<Object[]> results = session.createQuery("select p, a from Person p join p.address a").list();
``` ```
==== Stream
`jakarta.persistence.Query#getResultStream()` and `org.hibernate.query.Query#stream()` do not return a `Stream` decorator anymore, so in order to close the underlying IO resources is now necessary to explicitly call the `Stream#close()` method. The JDK `Stream` documentation is quite explicit about the need for an explicit call to `close` by the user to avoid resource leakages, so we build upon this requirement.
==== Iterate ==== Iterate
The `Query#iterate()` method has been removed. The `Query#iterate()` method has been removed. The alternative is to use `Query#stream()` or `Query#getResultStream()`.
=== Native Query === Native Query