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;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import java.util.stream.Stream;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.EmptyResultDataAccessException;
@ -40,6 +41,7 @@ import org.springframework.util.Assert;
* @author Oliver Gierke * @author Oliver Gierke
* @author Maciej Walkowiak * @author Maciej Walkowiak
* @author Mark Paluch * @author Mark Paluch
* @author Dennis Effing
* @since 2.0 * @since 2.0
*/ */
public abstract class AbstractJdbcQuery implements RepositoryQuery { public abstract class AbstractJdbcQuery implements RepositoryQuery {
@ -88,10 +90,14 @@ public abstract class AbstractJdbcQuery implements RepositoryQuery {
return createModifyingQueryExecutor(); return createModifyingQueryExecutor();
} }
if (queryMethod.isCollectionQuery() || queryMethod.isStreamQuery()) { if (queryMethod.isCollectionQuery()) {
return extractor != null ? getQueryExecution(extractor) : collectionQuery(rowMapper); return extractor != null ? getQueryExecution(extractor) : collectionQuery(rowMapper);
} }
if (queryMethod.isStreamQuery()) {
return extractor != null ? getQueryExecution(extractor) : streamQuery(rowMapper);
}
return extractor != null ? getQueryExecution(extractor) : singleObjectQuery(rowMapper); return extractor != null ? getQueryExecution(extractor) : singleObjectQuery(rowMapper);
} }
@ -140,6 +146,10 @@ public abstract class AbstractJdbcQuery implements RepositoryQuery {
: returnedType.getReturnedType(); : 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) { private <T> JdbcQueryExecution<T> getQueryExecution(ResultSetExtractor<T> resultSetExtractor) {
return (query, parameters) -> operations.query(query, parameters, 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;
* @author Jens Schauder * @author Jens Schauder
* @author Kazuki Shimizu * @author Kazuki Shimizu
* @author Mark Paluch * @author Mark Paluch
* @author Dennis Effing
*/ */
@Transactional @Transactional
@ActiveProfiles("hsql") @ActiveProfiles("hsql")
@ -173,6 +174,21 @@ public class QueryAnnotationHsqlIntegrationTests {
.containsExactlyInAnyOrder("a", "b"); .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 @Test // DATAJDBC-175
public void executeCustomQueryWithReturnTypeIsNumber() { public void executeCustomQueryWithReturnTypeIsNumber() {
@ -292,6 +308,10 @@ public class QueryAnnotationHsqlIntegrationTests {
@Query("SELECT * FROM DUMMY_ENTITY") @Query("SELECT * FROM DUMMY_ENTITY")
Stream<DummyEntity> findAllWithReturnTypeIsStream(); 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 // DATAJDBC-175
@Query("SELECT count(*) FROM DUMMY_ENTITY WHERE name like concat('%', :name, '%')") @Query("SELECT count(*) FROM DUMMY_ENTITY WHERE name like concat('%', :name, '%')")
int countByNameContaining(@Param("name") String 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;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.stream.Stream;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.dao.DataAccessException; import org.springframework.dao.DataAccessException;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
@ -39,9 +40,11 @@ import org.springframework.data.relational.core.mapping.RelationalMappingContext
import org.springframework.data.repository.Repository; import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata; import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries; 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.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper; import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
/** /**
@ -52,6 +55,7 @@ import org.springframework.util.ReflectionUtils;
* @author Maciej Walkowiak * @author Maciej Walkowiak
* @author Evgeni Dimitrov * @author Evgeni Dimitrov
* @author Mark Paluch * @author Mark Paluch
* @author Dennis Effing
*/ */
public class StringBasedJdbcQueryUnitTests { public class StringBasedJdbcQueryUnitTests {
@ -127,6 +131,28 @@ public class StringBasedJdbcQueryUnitTests {
"RowMapper is not expected to be custom"); "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 @Test // GH-774
public void sliceQueryNotSupported() { public void sliceQueryNotSupported() {
@ -173,6 +199,12 @@ public class StringBasedJdbcQueryUnitTests {
resultSetExtractorClass = CustomResultSetExtractor.class) resultSetExtractorClass = CustomResultSetExtractor.class)
List<Object> findAllWithCustomRowMapperAndResultSetExtractor(); 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(); List<Object> noAnnotation();
@Query(value = "some sql statement") @Query(value = "some sql statement")

Loading…
Cancel
Save