DATAJPA-1023 - StreamExceution now rejects non-transactional execution.
StreamExecution now makes use of the newly introduced SurroundingTransactionDetectingMethodInterceptor to find out about whether the transaction is used in code that already has a transaction running. This is necessary to make sure the Stream returned by the method can actually be consumed as the surrounding transaction keeps the transaction open. Related tickets: DATACMNS-959.
This commit is contained in:
@@ -27,10 +27,12 @@ import javax.persistence.StoredProcedureQuery;
|
||||
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 @@ public abstract class JpaQueryExecution {
|
||||
*/
|
||||
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 @@ public abstract class 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<Object> iter = persistenceProvider.executeQueryWithResultStream(jpaQuery);
|
||||
|
||||
@@ -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 static org.springframework.data.domain.Sort.Direction.*;
|
||||
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.jpa.repository.sample.UserRepository;
|
||||
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 class UserRepositoryFinderTests {
|
||||
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 class UserRepositoryFinderTests {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user