Skip to content

Commit eb9acd6

Browse files
committed
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.
1 parent 343f5c7 commit eb9acd6

File tree

2 files changed

+26
-1
lines changed

2 files changed

+26
-1
lines changed

Diff for: src/main/java/org/springframework/data/jpa/repository/query/JpaQueryExecution.java

+8
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,12 @@
2727
import org.springframework.core.convert.ConversionService;
2828
import org.springframework.core.convert.support.ConfigurableConversionService;
2929
import org.springframework.core.convert.support.DefaultConversionService;
30+
import org.springframework.dao.InvalidDataAccessApiUsageException;
3031
import org.springframework.data.domain.Pageable;
3132
import org.springframework.data.domain.Slice;
3233
import org.springframework.data.domain.SliceImpl;
3334
import org.springframework.data.jpa.provider.PersistenceProvider;
35+
import org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor;
3436
import org.springframework.data.repository.query.ParameterAccessor;
3537
import org.springframework.data.repository.query.Parameters;
3638
import org.springframework.data.repository.query.ParametersParameterAccessor;
@@ -323,13 +325,19 @@ protected Object doExecute(AbstractJpaQuery jpaQuery, Object[] values) {
323325
*/
324326
static class StreamExecution extends JpaQueryExecution {
325327

328+
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.";
329+
326330
/*
327331
* (non-Javadoc)
328332
* @see org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute(org.springframework.data.jpa.repository.query.AbstractJpaQuery, java.lang.Object[])
329333
*/
330334
@Override
331335
protected Object doExecute(final AbstractJpaQuery query, Object[] values) {
332336

337+
if (!SurroundingTransactionDetectorMethodInterceptor.INSTANCE.isSurroundingTransactionActive()) {
338+
throw new InvalidDataAccessApiUsageException(NO_SURROUNDING_TRANSACTION);
339+
}
340+
333341
Query jpaQuery = query.createQuery(values);
334342
PersistenceProvider persistenceProvider = PersistenceProvider.fromEntityManager(query.getEntityManager());
335343
CloseableIterator<Object> iter = persistenceProvider.executeQueryWithResultStream(jpaQuery);

Diff for: src/test/java/org/springframework/data/jpa/repository/UserRepositoryFinderTests.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2008-2015 the original author or authors.
2+
* Copyright 2008-2016 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -26,6 +26,7 @@
2626
import org.junit.Test;
2727
import org.junit.runner.RunWith;
2828
import org.springframework.beans.factory.annotation.Autowired;
29+
import org.springframework.dao.InvalidDataAccessApiUsageException;
2930
import org.springframework.data.domain.Page;
3031
import org.springframework.data.domain.PageRequest;
3132
import org.springframework.data.domain.Slice;
@@ -37,6 +38,7 @@
3738
import org.springframework.data.repository.query.QueryLookupStrategy;
3839
import org.springframework.test.context.ContextConfiguration;
3940
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
41+
import org.springframework.transaction.annotation.Propagation;
4042
import org.springframework.transaction.annotation.Transactional;
4143

4244
/**
@@ -234,4 +236,19 @@ public void translatesNotContainsToNotMemberOf() {
234236
public void executesQueryWithProjectionContainingReferenceToPluralAttribute() {
235237
assertThat(userRepository.findRolesAndFirstnameBy(), is(notNullValue()));
236238
}
239+
240+
/**
241+
* @see DATAJPA-1023, DATACMNS-959
242+
*/
243+
@Test(expected = InvalidDataAccessApiUsageException.class)
244+
@Transactional(propagation = Propagation.NOT_SUPPORTED)
245+
public void rejectsStreamExecutionIfNoSurroundingTransactionActive() {
246+
247+
try {
248+
userRepository.findAllByCustomQueryAndStream();
249+
} finally {
250+
roleRepository.deleteAll();
251+
userRepository.deleteAll();
252+
}
253+
}
237254
}

0 commit comments

Comments
 (0)