diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java index 6225934dde..cbac8b6ae5 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java @@ -27,10 +27,12 @@ import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.ConfigurableConversionService; import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Slice; import org.springframework.data.domain.SliceImpl; import org.springframework.data.jpa.provider.PersistenceProvider; +import org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor; import org.springframework.data.repository.query.ParameterAccessor; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.ParametersParameterAccessor; @@ -323,6 +325,8 @@ protected Object doExecute(AbstractJpaQuery jpaQuery, Object[] values) { */ static class StreamExecution extends JpaQueryExecution { + private static final String NO_SURROUNDING_TRANSACTION = "You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses @Transactional or any other way of declaring a (read-only) transaction."; + /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[]) @@ -330,6 +334,10 @@ static class StreamExecution extends JpaQueryExecution { @Override protected Object doExecute(final AbstractJpaQuery query, Object[] values) { + if (!SurroundingTransactionDetectorMethodInterceptor.INSTANCE.isSurroundingTransactionActive()) { + throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION); + } + Query jpaQuery = query.createQuery(values); PersistenceProvider persistenceProvider = PersistenceProvider.fromEntityManager(query.getEntityManager()); CloseableIterator iter = persistenceProvider.executeQueryWithResultStream(jpaQuery); diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java index 5d34458f82..1333c0e295 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2015 the original author or authors. + * Copyright 2008-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,10 +22,12 @@ import java.util.Arrays; import java.util.List; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Slice; @@ -37,6 +39,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** @@ -68,6 +71,13 @@ public void setUp() { oliver = userRepository.save(new User("Oliver August", "Matthews", "oliver@dmband.com")); } + @After + public void clearUp() { + + userRepository.deleteAll(); + roleRepository.deleteAll(); + } + /** * Tests creation of a simple query. */ @@ -234,4 +244,13 @@ public void translatesNotContainsToNotMemberOf() { public void executesQueryWithProjectionContainingReferenceToPluralAttribute() { assertThat(userRepository.findRolesAndFirstnameBy(), is(notNullValue())); } + + /** + * @see DATAJPA-1023, DATACMNS-959 + */ + @Test(expected = InvalidDataAccessApiUsageException.class) + @Transactional(propagation = Propagation.NOT_SUPPORTED) + public void rejectsStreamExecutionIfNoSurroundingTransactionActive() { + userRepository.findAllByCustomQueryAndStream(); + } }