From c587ead8a6250a994c10df4fbe22a9f39dc2dddc Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Fri, 9 Feb 2018 21:15:47 +0900 Subject: [PATCH 1/2] DATAJDBC-175 - Supports simple types as return type for annotated methods --- .../mapping/model/JdbcMappingContext.java | 8 +++ .../support/JdbcQueryLookupStrategy.java | 30 ++++++++-- .../QueryAnnotationHsqlIntegrationTests.java | 56 +++++++++++++++++++ 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcMappingContext.java b/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcMappingContext.java index 570d4a8f92..8871cd9f3e 100644 --- a/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcMappingContext.java +++ b/src/main/java/org/springframework/data/jdbc/mapping/model/JdbcMappingContext.java @@ -44,6 +44,7 @@ * * @author Jens Schauder * @author Greg Turnquist + * @author Kazuki Shimizu * @since 2.0 */ public class JdbcMappingContext extends AbstractMappingContext, JdbcPersistentProperty> { @@ -56,6 +57,7 @@ public class JdbcMappingContext extends AbstractMappingContext {}); } + @Override + public void setSimpleTypeHolder(SimpleTypeHolder simpleTypes) { + super.setSimpleTypeHolder(simpleTypes); + this.simpleTypeHolder = simpleTypes; + } + public List referencedEntities(Class rootType, PropertyPath path) { List paths = new ArrayList<>(); diff --git a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java index ec097d6a9f..2598fdbff1 100644 --- a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java +++ b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java @@ -17,6 +17,7 @@ import java.lang.reflect.Method; +import org.springframework.core.convert.ConversionService; import org.springframework.data.jdbc.core.DataAccessStrategy; import org.springframework.data.jdbc.core.EntityRowMapper; import org.springframework.data.jdbc.mapping.model.JdbcMappingContext; @@ -27,22 +28,26 @@ import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.SingleColumnRowMapper; /** * {@link QueryLookupStrategy} for JDBC repositories. Currently only supports annotated queries. * * @author Jens Schauder + * @author Kazuki Shimizu */ class JdbcQueryLookupStrategy implements QueryLookupStrategy { private final JdbcMappingContext context; private final DataAccessStrategy accessStrategy; + private final ConversionService conversionService; JdbcQueryLookupStrategy(EvaluationContextProvider evaluationContextProvider, JdbcMappingContext context, DataAccessStrategy accessStrategy) { this.context = context; this.accessStrategy = accessStrategy; + this.conversionService = context.getConversions(); } @Override @@ -50,10 +55,27 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repository ProjectionFactory projectionFactory, NamedQueries namedQueries) { JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory); - Class domainType = queryMethod.getReturnedObjectType(); - RowMapper rowMapper = new EntityRowMapper<>(context.getRequiredPersistentEntity(domainType), - context.getConversions(), context, accessStrategy); - + Class returnedObjectType = queryMethod.getReturnedObjectType(); + RowMapper rowMapper = context.getSimpleTypeHolder().isSimpleType(returnedObjectType) + ? new CustomSingleColumnRowMapper<>(returnedObjectType) + : new EntityRowMapper<>(context.getRequiredPersistentEntity(returnedObjectType), conversionService, + context, accessStrategy); return new JdbcRepositoryQuery(queryMethod, context, rowMapper); } + + private class CustomSingleColumnRowMapper extends SingleColumnRowMapper { + + private CustomSingleColumnRowMapper(Class requiredType) { + super(requiredType); + } + + @Override + protected Object convertValueToRequiredType(Object value, Class requiredType) { + return conversionService.canConvert(value.getClass(), requiredType) + ? conversionService.convert(value, requiredType) + : super.convertValueToRequiredType(value, requiredType); + } + + } + } diff --git a/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java b/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java index 243e7c9119..ff216f6dd0 100644 --- a/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java +++ b/src/test/java/org/springframework/data/jdbc/repository/query/QueryAnnotationHsqlIntegrationTests.java @@ -33,6 +33,8 @@ import org.springframework.test.context.junit4.rules.SpringMethodRule; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; +import java.util.Date; import java.util.List; import java.util.Optional; import java.util.stream.Stream; @@ -165,6 +167,48 @@ public void executeCustomQueryWithReturnTypeIsStream() { .containsExactlyInAnyOrder("a", "b"); } + + @Test // DATAJDBC-175 + public void executeCustomQueryWithReturnTypeIsNubmer() { + + repository.save(dummyEntity("aaa")); + repository.save(dummyEntity("bbb")); + repository.save(dummyEntity("cac")); + + int count = repository.countByNameContaining("a"); + + assertThat(count).isEqualTo(2); + + } + + @Test // DATAJDBC-175 + public void executeCustomQueryWithReturnTypeIsBoolean() { + + repository.save(dummyEntity("aaa")); + repository.save(dummyEntity("bbb")); + repository.save(dummyEntity("cac")); + + assertThat(repository.existsByNameContaining("a")).isTrue(); + assertThat(repository.existsByNameContaining("d")).isFalse(); + + } + + @Test // DATAJDBC-175 + public void executeCustomQueryWithReturnTypeIsDate() { + + Date now = new Date(); + assertThat(repository.nowWithDate()).isAfterOrEqualsTo(now); + + } + + @Test // DATAJDBC-175 + public void executeCustomQueryWithReturnTypeIsLocalDateTimeList() { + + LocalDateTime now = LocalDateTime.now(); + repository.nowWithLocalDateTimeList() // + .forEach(d -> assertThat(d).isAfterOrEqualTo(now)); + + } private DummyEntity dummyEntity(String name) { @@ -209,5 +253,17 @@ private interface DummyEntityRepository extends CrudRepository findAllWithReturnTypeIsStream(); + @Query("SELECT count(*) FROM DUMMYENTITY WHERE name like '%' || :name || '%'") + int countByNameContaining(@Param("name") String name); + + @Query("SELECT count(*) FROM DUMMYENTITY WHERE name like '%' || :name || '%'") + boolean existsByNameContaining(@Param("name") String name); + + @Query("VALUES (current_timestamp)") + Date nowWithDate(); + + @Query("VALUES (current_timestamp),(current_timestamp)") + List nowWithLocalDateTimeList(); + } } From b91da68c77a78c4ac44347a5e5fd1c54a6a80c9d Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Tue, 27 Feb 2018 21:51:01 +0900 Subject: [PATCH 2/2] DATAJDBC-175 - Apply the change of Spring Framework 5.0.4(SPR-16483) --- .../support/JdbcQueryLookupStrategy.java | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java index 2598fdbff1..be2d0667e4 100644 --- a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java +++ b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java @@ -57,25 +57,10 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repository JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory); Class returnedObjectType = queryMethod.getReturnedObjectType(); RowMapper rowMapper = context.getSimpleTypeHolder().isSimpleType(returnedObjectType) - ? new CustomSingleColumnRowMapper<>(returnedObjectType) + ? SingleColumnRowMapper.newInstance(returnedObjectType, conversionService) : new EntityRowMapper<>(context.getRequiredPersistentEntity(returnedObjectType), conversionService, context, accessStrategy); return new JdbcRepositoryQuery(queryMethod, context, rowMapper); } - - private class CustomSingleColumnRowMapper extends SingleColumnRowMapper { - - private CustomSingleColumnRowMapper(Class requiredType) { - super(requiredType); - } - - @Override - protected Object convertValueToRequiredType(Object value, Class requiredType) { - return conversionService.canConvert(value.getClass(), requiredType) - ? conversionService.convert(value, requiredType) - : super.convertValueToRequiredType(value, requiredType); - } - - } }