Skip to content

Commit 182faef

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. Original pull request #176 Closes #356
1 parent d0a1e19 commit 182faef

File tree

3 files changed

+50
-1
lines changed

3 files changed

+50
-1
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

+19
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.springframework.jdbc.core.ResultSetExtractor;
4343
import org.springframework.jdbc.core.RowMapper;
4444
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
45+
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
4546
import org.springframework.util.ReflectionUtils;
4647

4748
/**
@@ -52,6 +53,7 @@
5253
* @author Maciej Walkowiak
5354
* @author Evgeni Dimitrov
5455
* @author Mark Paluch
56+
* @author Dennis Effing
5557
*/
5658
public class StringBasedJdbcQueryUnitTests {
5759

@@ -127,6 +129,17 @@ public void customResultSetExtractorAndRowMapperGetCombined() {
127129
"RowMapper is not expected to be custom");
128130
}
129131

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

@@ -183,6 +196,12 @@ interface MyRepository extends Repository<Object, Long> {
183196

184197
}
185198

199+
/**
200+
* The whole purpose of this method is to easily generate a {@link DefaultParameters} instance during test setup.
201+
*/
202+
@SuppressWarnings("unused")
203+
private void dummyMethod() {}
204+
186205
private static class CustomRowMapper implements RowMapper<Object> {
187206

188207
@Override

0 commit comments

Comments
 (0)