From 002ef4e4c2adcf1906ce90ff3ba45ba45e64b4c1 Mon Sep 17 00:00:00 2001 From: Kazuki Shimizu Date: Fri, 9 Mar 2018 00:16:11 +0900 Subject: [PATCH] DATAJDBC-182 - Support modifying query on the query method --- .../data/jdbc/repository/query/Modifying.java | 33 ++++++++++++++ .../support/JdbcQueryLookupStrategy.java | 20 +++++---- .../repository/support/JdbcQueryMethod.java | 14 ++++++ .../support/JdbcRepositoryQuery.java | 17 ++++--- .../QueryAnnotationHsqlIntegrationTests.java | 45 +++++++++++++++++++ 5 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 src/main/java/org/springframework/data/jdbc/repository/query/Modifying.java diff --git a/src/main/java/org/springframework/data/jdbc/repository/query/Modifying.java b/src/main/java/org/springframework/data/jdbc/repository/query/Modifying.java new file mode 100644 index 0000000000..54c3fa0021 --- /dev/null +++ b/src/main/java/org/springframework/data/jdbc/repository/query/Modifying.java @@ -0,0 +1,33 @@ +/* + * Copyright 2018 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.jdbc.repository.query; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates a method should be regarded as modifying query. + * + * @author Kazuki Shimizu + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) +@Documented +public @interface Modifying { +} 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 b7979f5832..54842aeade 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 @@ -56,15 +56,17 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repository JdbcQueryMethod queryMethod = new JdbcQueryMethod(method, repositoryMetadata, projectionFactory); Class returnedObjectType = queryMethod.getReturnedObjectType(); - RowMapper rowMapper = context.getSimpleTypeHolder().isSimpleType(returnedObjectType) - ? SingleColumnRowMapper.newInstance(returnedObjectType, conversionService) - : new EntityRowMapper<>( // - context.getRequiredPersistentEntity(returnedObjectType), // - conversionService, // - context, // - accessStrategy // - ); - + RowMapper rowMapper = null; + if (!queryMethod.isModifyingQuery()) { + rowMapper = context.getSimpleTypeHolder().isSimpleType(returnedObjectType) + ? SingleColumnRowMapper.newInstance(returnedObjectType, conversionService) + : new EntityRowMapper<>( // + context.getRequiredPersistentEntity(returnedObjectType), // + conversionService, // + context, // + accessStrategy // + ); + } return new JdbcRepositoryQuery(queryMethod, context, rowMapper); } diff --git a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryMethod.java b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryMethod.java index f1552190b8..dd2b3a2672 100644 --- a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryMethod.java +++ b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryMethod.java @@ -18,6 +18,8 @@ import java.lang.reflect.Method; import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.data.jdbc.repository.query.Modifying; import org.springframework.data.jdbc.repository.query.Query; import org.springframework.data.projection.ProjectionFactory; import org.springframework.data.repository.core.RepositoryMetadata; @@ -30,6 +32,7 @@ * Binds method arguments to named parameters in the SQL statement. * * @author Jens Schauder + * @author Kazuki Shimizu */ public class JdbcQueryMethod extends QueryMethod { @@ -47,4 +50,15 @@ public String getAnnotatedQuery() { return queryAnnotation == null ? null : queryAnnotation.value(); } + + /** + * Returns whether the query method is a modifying one. + * + * @return if it's a modifying query, return {@code true}. + */ + @Override + public boolean isModifyingQuery() { + return AnnotationUtils.findAnnotation(method, Modifying.class) != null; + } + } diff --git a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryQuery.java b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryQuery.java index 362176f3de..be48004c26 100644 --- a/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryQuery.java +++ b/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryQuery.java @@ -55,15 +55,20 @@ public Object execute(Object[] objects) { parameters.addValue(parameterName, objects[p.getIndex()]); }); + if (queryMethod.isModifyingQuery()) { + int updatedCount = context.getTemplate().update(query, parameters); + Class returnedObjectType = queryMethod.getReturnedObjectType(); + return (returnedObjectType == boolean.class || returnedObjectType == Boolean.class) ? updatedCount != 0 : updatedCount; + } + if (queryMethod.isCollectionQuery() || queryMethod.isStreamQuery()) { return context.getTemplate().query(query, parameters, rowMapper); - } else { + } - try { - return context.getTemplate().queryForObject(query, parameters, rowMapper); - } catch (EmptyResultDataAccessException e) { - return null; - } + try { + return context.getTemplate().queryForObject(query, parameters, rowMapper); + } catch (EmptyResultDataAccessException e) { + return null; } } 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 ff216f6dd0..6f09adca67 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 @@ -210,6 +210,39 @@ public void executeCustomQueryWithReturnTypeIsLocalDateTimeList() { } + @Test // DATAJDBC-182 + public void executeCustomModifyingQueryWithReturnTypeIsNumber() { + + DummyEntity entity = dummyEntity("a"); + repository.save(entity); + + assertThat(repository.updateName(entity.id, "b")).isEqualTo(1); + assertThat(repository.updateName(9999L, "b")).isEqualTo(0); + assertThat(repository.findById(entity.id)).isPresent().map(e -> e.name).contains("b"); + + } + + @Test // DATAJDBC-182 + public void executeCustomModifyingQueryWithReturnTypeIsBoolean() { + + DummyEntity entity = dummyEntity("a"); + repository.save(entity); + + assertThat(repository.deleteByName("a")).isTrue(); + assertThat(repository.deleteByName("b")).isFalse(); + assertThat(repository.findById(entity.id)).isNotPresent(); + + } + + @Test // DATAJDBC-182 + public void executeCustomModifyingQueryWithReturnTypeIsVoid() { + + repository.insert("Spring Data JDBC"); + + assertThat(repository.findByNameAsEntity("Spring Data JDBC")).isNotNull(); + + } + private DummyEntity dummyEntity(String name) { DummyEntity entity = new DummyEntity(); @@ -265,5 +298,17 @@ private interface DummyEntityRepository extends CrudRepository nowWithLocalDateTimeList(); + @Modifying + @Query("UPDATE DUMMYENTITY SET name = :name WHERE id = :id") + int updateName(@Param("id") Long id, @Param("name") String name); + + @Modifying + @Query("DELETE FROM DUMMYENTITY WHERE name = :name") + boolean deleteByName(@Param("name") String name); + + @Modifying + @Query("INSERT INTO DUMMYENTITY (name) VALUES(:name)") + void insert(@Param("name") String name); + } }