Browse Source

Add support for streamed query results.

Use queryForStream for streamed query results.
Since ResultSetExtractor cannot be reasonably be used together with streams it falls back to the existing collection behaviour.

Closes #578
Original pull request #903
pull/1002/head
Dennis Effing 5 years ago committed by Jens Schauder
parent
commit
c416d3d0ea
No known key found for this signature in database
GPG Key ID: 45CC872F17423DBF
  1. 12
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java
  2. 20
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java
  3. 34
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQueryUnitTests.java

12
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java

@ -18,6 +18,7 @@ package org.springframework.data.jdbc.repository.query; @@ -18,6 +18,7 @@ package org.springframework.data.jdbc.repository.query;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.stream.Stream;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.EmptyResultDataAccessException;
@ -40,6 +41,7 @@ import org.springframework.util.Assert; @@ -40,6 +41,7 @@ import org.springframework.util.Assert;
* @author Oliver Gierke
* @author Maciej Walkowiak
* @author Mark Paluch
* @author Dennis Effing
* @since 2.0
*/
public abstract class AbstractJdbcQuery implements RepositoryQuery {
@ -88,10 +90,14 @@ public abstract class AbstractJdbcQuery implements RepositoryQuery { @@ -88,10 +90,14 @@ public abstract class AbstractJdbcQuery implements RepositoryQuery {
return createModifyingQueryExecutor();
}
if (queryMethod.isCollectionQuery() || queryMethod.isStreamQuery()) {
if (queryMethod.isCollectionQuery()) {
return extractor != null ? getQueryExecution(extractor) : collectionQuery(rowMapper);
}
if (queryMethod.isStreamQuery()) {
return extractor != null ? getQueryExecution(extractor) : streamQuery(rowMapper);
}
return extractor != null ? getQueryExecution(extractor) : singleObjectQuery(rowMapper);
}
@ -140,6 +146,10 @@ public abstract class AbstractJdbcQuery implements RepositoryQuery { @@ -140,6 +146,10 @@ public abstract class AbstractJdbcQuery implements RepositoryQuery {
: returnedType.getReturnedType();
}
private <T> JdbcQueryExecution<Stream<T>> streamQuery(RowMapper<T> rowMapper) {
return (query, parameters) -> operations.queryForStream(query, parameters, rowMapper);
}
private <T> JdbcQueryExecution<T> getQueryExecution(ResultSetExtractor<T> resultSetExtractor) {
return (query, parameters) -> operations.query(query, parameters, resultSetExtractor);
}

20
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java

@ -51,6 +51,7 @@ import org.springframework.transaction.annotation.Transactional; @@ -51,6 +51,7 @@ import org.springframework.transaction.annotation.Transactional;
* @author Jens Schauder
* @author Kazuki Shimizu
* @author Mark Paluch
* @author Dennis Effing
*/
@Transactional
@ActiveProfiles("hsql")
@ -173,6 +174,21 @@ public class QueryAnnotationHsqlIntegrationTests { @@ -173,6 +174,21 @@ public class QueryAnnotationHsqlIntegrationTests {
.containsExactlyInAnyOrder("a", "b");
}
@Test // DATAJDBC-356
public void executeCustomQueryWithNamedParameterAndReturnTypeIsStream() {
repository.save(dummyEntity("a"));
repository.save(dummyEntity("b"));
repository.save(dummyEntity("c"));
Stream<DummyEntity> entities = repository.findByNamedRangeWithNamedParameterAndReturnTypeIsStream("a", "c");
assertThat(entities) //
.extracting(e -> e.name) //
.containsExactlyInAnyOrder("b");
}
@Test // DATAJDBC-175
public void executeCustomQueryWithReturnTypeIsNumber() {
@ -292,6 +308,10 @@ public class QueryAnnotationHsqlIntegrationTests { @@ -292,6 +308,10 @@ public class QueryAnnotationHsqlIntegrationTests {
@Query("SELECT * FROM DUMMY_ENTITY")
Stream<DummyEntity> findAllWithReturnTypeIsStream();
@Query("SELECT * FROM DUMMY_ENTITY WHERE name < :upper and name > :lower")
Stream<DummyEntity> findByNamedRangeWithNamedParameterAndReturnTypeIsStream(@Param("lower") String lower,
@Param("upper") String upper);
// DATAJDBC-175
@Query("SELECT count(*) FROM DUMMY_ENTITY WHERE name like concat('%', :name, '%')")
int countByNameContaining(@Param("name") String name);

34
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQueryUnitTests.java

@ -22,11 +22,12 @@ import java.lang.reflect.Method; @@ -22,11 +22,12 @@ import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.util.List;
import java.util.Properties;
import java.util.stream.Stream;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
@ -39,9 +40,11 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext @@ -39,9 +40,11 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
import org.springframework.data.repository.query.DefaultParameters;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.util.ReflectionUtils;
/**
@ -52,6 +55,7 @@ import org.springframework.util.ReflectionUtils; @@ -52,6 +55,7 @@ import org.springframework.util.ReflectionUtils;
* @author Maciej Walkowiak
* @author Evgeni Dimitrov
* @author Mark Paluch
* @author Dennis Effing
*/
public class StringBasedJdbcQueryUnitTests {
@ -127,6 +131,28 @@ public class StringBasedJdbcQueryUnitTests { @@ -127,6 +131,28 @@ public class StringBasedJdbcQueryUnitTests {
"RowMapper is not expected to be custom");
}
@Test // DATAJDBC-356
public void streamQueryCallsQueryForStreamOnOperations() {
JdbcQueryMethod queryMethod = createMethod("findAllWithStreamReturnType");
StringBasedJdbcQuery query = createQuery(queryMethod);
query.execute(new Object[] {});
verify(operations).queryForStream(eq("some sql statement"), any(SqlParameterSource.class), any(RowMapper.class));
}
@Test // DATAJDBC-356
void streamQueryFallsBackToCollectionQueryWhenCustomResultSetExtractorIsSpecified() {
JdbcQueryMethod queryMethod = createMethod("findAllWithStreamReturnTypeAndResultSetExtractor");
StringBasedJdbcQuery query = createQuery(queryMethod);
query.execute(new Object[] {});
ArgumentCaptor<ResultSetExtractor> captor = ArgumentCaptor.forClass(ResultSetExtractor.class);
verify(operations).query(eq("some sql statement"), any(SqlParameterSource.class), captor.capture());
assertThat(captor.getValue()).isInstanceOf(CustomResultSetExtractor.class);
}
@Test // GH-774
public void sliceQueryNotSupported() {
@ -173,6 +199,12 @@ public class StringBasedJdbcQueryUnitTests { @@ -173,6 +199,12 @@ public class StringBasedJdbcQueryUnitTests {
resultSetExtractorClass = CustomResultSetExtractor.class)
List<Object> findAllWithCustomRowMapperAndResultSetExtractor();
@Query(value = "some sql statement")
Stream<Object> findAllWithStreamReturnType();
@Query(value = "some sql statement", resultSetExtractorClass = CustomResultSetExtractor.class)
Stream<Object> findAllWithStreamReturnTypeAndResultSetExtractor();
List<Object> noAnnotation();
@Query(value = "some sql statement")

Loading…
Cancel
Save