From 73fe1ca93bb7c5f9e31292343c0b06f981e51284 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 annotated queries. Added @Modify for marking queries that perform DML or DDL. Modifying queries with return type boolean or Boolean return wether the number of updated rows is greater 0. This shouldn't be used for DML statements since it will always return false. Original pull request: ##48. --- .../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 00000000..54c3fa00 --- /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 b7979f58..54842aea 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 @@ class JdbcQueryLookupStrategy implements QueryLookupStrategy { 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 f1552190..dd2b3a26 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 @@ package org.springframework.data.jdbc.repository.support; 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 @@ import org.springframework.data.repository.query.QueryMethod; * 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 class JdbcQueryMethod extends QueryMethod { 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 362176f3..be48004c 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 @@ class JdbcRepositoryQuery implements RepositoryQuery { 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 ff216f6d..6f09adca 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 class QueryAnnotationHsqlIntegrationTests { } + @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 @@ public class QueryAnnotationHsqlIntegrationTests { @Query("VALUES (current_timestamp),(current_timestamp)") List 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); + } }