Skip to content

Commit 1d6efb7

Browse files
committed
Add support for streamed query results
Use queryForStream for streamed query results. ResultSetExtractor is ignored because it cannot be used together with streams. Closes #356
1 parent d0a1e19 commit 1d6efb7

File tree

3 files changed

+48
-2
lines changed

3 files changed

+48
-2
lines changed

Diff for: spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.sql.ResultSet;
1919
import java.sql.SQLException;
2020
import java.util.List;
21+
import java.util.stream.Stream;
2122

2223
import org.springframework.core.convert.converter.Converter;
2324
import org.springframework.dao.EmptyResultDataAccessException;
@@ -40,6 +41,7 @@
4041
* @author Oliver Gierke
4142
* @author Maciej Walkowiak
4243
* @author Mark Paluch
44+
* @author Dennis Effing
4345
* @since 2.0
4446
*/
4547
public abstract class AbstractJdbcQuery implements RepositoryQuery {
@@ -88,10 +90,14 @@ protected JdbcQueryExecution<?> getQueryExecution(JdbcQueryMethod queryMethod,
8890
return createModifyingQueryExecutor();
8991
}
9092

91-
if (queryMethod.isCollectionQuery() || queryMethod.isStreamQuery()) {
93+
if (queryMethod.isCollectionQuery()) {
9294
return extractor != null ? getQueryExecution(extractor) : collectionQuery(rowMapper);
9395
}
9496

97+
if (queryMethod.isStreamQuery()) {
98+
return streamQuery(rowMapper);
99+
}
100+
95101
return extractor != null ? getQueryExecution(extractor) : singleObjectQuery(rowMapper);
96102
}
97103

@@ -140,6 +146,10 @@ protected Class<?> resolveTypeToRead(ResultProcessor resultProcessor) {
140146
: returnedType.getReturnedType();
141147
}
142148

149+
private <T> JdbcQueryExecution<Stream<T>> streamQuery(RowMapper<T> rowMapper) {
150+
return (query, parameters) -> operations.queryForStream(query, parameters, rowMapper);
151+
}
152+
143153
private <T> JdbcQueryExecution<T> getQueryExecution(ResultSetExtractor<T> resultSetExtractor) {
144154
return (query, parameters) -> operations.query(query, parameters, resultSetExtractor);
145155
}

Diff for: spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java

+20
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
* @author Jens Schauder
5252
* @author Kazuki Shimizu
5353
* @author Mark Paluch
54+
* @author Dennis Effing
5455
*/
5556
@Transactional
5657
@ActiveProfiles("hsql")
@@ -173,6 +174,21 @@ public void executeCustomQueryWithReturnTypeIsStream() {
173174
.containsExactlyInAnyOrder("a", "b");
174175
}
175176

177+
@Test // DATAJDBC-356
178+
public void executeCustomQueryWithNamedParameterAndReturnTypeIsStream() {
179+
180+
repository.save(dummyEntity("a"));
181+
repository.save(dummyEntity("b"));
182+
repository.save(dummyEntity("c"));
183+
184+
Stream<DummyEntity> entities = repository.findByNamedRangeWithNamedParameterAndReturnTypeIsStream("a", "c");
185+
186+
assertThat(entities) //
187+
.extracting(e -> e.name) //
188+
.containsExactlyInAnyOrder("b");
189+
190+
}
191+
176192
@Test // DATAJDBC-175
177193
public void executeCustomQueryWithReturnTypeIsNumber() {
178194

@@ -292,6 +308,10 @@ private interface DummyEntityRepository extends CrudRepository<DummyEntity, Long
292308
@Query("SELECT * FROM DUMMY_ENTITY")
293309
Stream<DummyEntity> findAllWithReturnTypeIsStream();
294310

311+
@Query("SELECT * FROM DUMMY_ENTITY WHERE name < :upper and name > :lower")
312+
Stream<DummyEntity> findByNamedRangeWithNamedParameterAndReturnTypeIsStream(@Param("lower") String lower,
313+
@Param("upper") String upper);
314+
295315
// DATAJDBC-175
296316
@Query("SELECT count(*) FROM DUMMY_ENTITY WHERE name like concat('%', :name, '%')")
297317
int countByNameContaining(@Param("name") String name);

Diff for: spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQueryUnitTests.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@
2222
import java.sql.ResultSet;
2323
import java.util.List;
2424
import java.util.Properties;
25+
import java.util.stream.Stream;
2526

2627
import org.assertj.core.api.Assertions;
2728
import org.junit.jupiter.api.BeforeEach;
2829
import org.junit.jupiter.api.Test;
29-
3030
import org.springframework.dao.DataAccessException;
3131
import org.springframework.data.domain.Page;
3232
import org.springframework.data.domain.Pageable;
@@ -39,9 +39,11 @@
3939
import org.springframework.data.repository.Repository;
4040
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
4141
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
42+
import org.springframework.data.repository.query.DefaultParameters;
4243
import org.springframework.jdbc.core.ResultSetExtractor;
4344
import org.springframework.jdbc.core.RowMapper;
4445
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
46+
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
4547
import org.springframework.util.ReflectionUtils;
4648

4749
/**
@@ -52,6 +54,7 @@
5254
* @author Maciej Walkowiak
5355
* @author Evgeni Dimitrov
5456
* @author Mark Paluch
57+
* @author Dennis Effing
5558
*/
5659
public class StringBasedJdbcQueryUnitTests {
5760

@@ -127,6 +130,16 @@ public void customResultSetExtractorAndRowMapperGetCombined() {
127130
"RowMapper is not expected to be custom");
128131
}
129132

133+
@Test // DATAJDBC-356
134+
public void streamQueryCallsQueryForStreamOnOperations() {
135+
JdbcQueryMethod queryMethod = createMethod("findAllWithStreamReturnType");
136+
StringBasedJdbcQuery query = createQuery(queryMethod);
137+
138+
query.execute(new Object[] {});
139+
140+
verify(operations).queryForStream(eq("some sql statement"), any(SqlParameterSource.class), any(RowMapper.class));
141+
}
142+
130143
@Test // GH-774
131144
public void sliceQueryNotSupported() {
132145

@@ -173,6 +186,9 @@ interface MyRepository extends Repository<Object, Long> {
173186
resultSetExtractorClass = CustomResultSetExtractor.class)
174187
List<Object> findAllWithCustomRowMapperAndResultSetExtractor();
175188

189+
@Query(value = "some sql statement")
190+
Stream<Object> findAllWithStreamReturnType();
191+
176192
List<Object> noAnnotation();
177193

178194
@Query(value = "some sql statement")

0 commit comments

Comments
 (0)