DATAJDBC-165 - Allows configuration of custom RowMapper on @Query annotation.

This commit is contained in:
Jens Schauder
2018-01-15 14:19:36 +01:00
committed by Greg Turnquist
parent 5f6a44d00a
commit 043bd4de35
5 changed files with 251 additions and 18 deletions

View File

@@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.data.annotation.QueryAnnotation;
import org.springframework.jdbc.core.RowMapper;
/**
* Annotation to provide SQL statements that will get used for executing the method.
@@ -36,5 +37,14 @@ import org.springframework.data.annotation.QueryAnnotation;
@QueryAnnotation
@Documented
public @interface Query {
/**
* The SQL statement to execute when the annotated method gets invoked.
*/
String value();
/**
* Optional {@link RowMapper} to use to convert the result of the query to domain class instances.
*/
Class<? extends RowMapper> rowMapperClass() default RowMapper.class;
}

View File

@@ -28,9 +28,7 @@ import org.springframework.lang.Nullable;
/**
* {@link QueryMethod} implementation that implements a method by executing the query from a {@link Query} annotation on
* that method.
*
* Binds method arguments to named parameters in the SQL statement.
* that method. Binds method arguments to named parameters in the SQL statement.
*
* @author Jens Schauder
* @author Kazuki Shimizu
@@ -40,6 +38,7 @@ public class JdbcQueryMethod extends QueryMethod {
private final Method method;
public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory) {
super(method, metadata, factory);
this.method = method;
@@ -52,12 +51,18 @@ public class JdbcQueryMethod extends QueryMethod {
*/
@Nullable
public String getAnnotatedQuery() {
Query queryAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, Query.class);
return queryAnnotation == null ? null : queryAnnotation.value();
return getMergedAnnotationAttribute("value");
}
/**
* Returns the class to be used as {@link org.springframework.jdbc.core.RowMapper}
*
* @return May be {@code null}.
*/
public Class<?> getRowMapperClass() {
return getMergedAnnotationAttribute("rowMapperClass");
}
/**
* Returns whether the query method is a modifying one.
*
@@ -68,4 +73,10 @@ public class JdbcQueryMethod extends QueryMethod {
return AnnotationUtils.findAnnotation(method, Modifying.class) != null;
}
@SuppressWarnings("unchecked")
private <T> T getMergedAnnotationAttribute(String attribute) {
Query queryAnnotation = AnnotatedElementUtils.findMergedAnnotation(method, Query.class);
return (T) AnnotationUtils.getValue(queryAnnotation, attribute);
}
}

View File

@@ -15,11 +15,13 @@
*/
package org.springframework.data.jdbc.repository.support;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.jdbc.mapping.model.JdbcMappingContext;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.util.StringUtils;
/**
* A query to be executed based on a repository method, it's annotated SQL query and the arguments provided to the
@@ -36,29 +38,34 @@ class JdbcRepositoryQuery implements RepositoryQuery {
private final JdbcMappingContext context;
private final RowMapper<?> rowMapper;
JdbcRepositoryQuery(JdbcQueryMethod queryMethod, JdbcMappingContext context, RowMapper rowMapper) {
JdbcRepositoryQuery(JdbcQueryMethod queryMethod, JdbcMappingContext context, RowMapper defaultRowMapper) {
this.queryMethod = queryMethod;
this.context = context;
this.rowMapper = rowMapper;
this.rowMapper = createRowMapper(queryMethod, defaultRowMapper);
}
private static RowMapper<?> createRowMapper(JdbcQueryMethod queryMethod, RowMapper defaultRowMapper) {
Class<?> rowMapperClass = queryMethod.getRowMapperClass();
return rowMapperClass == null || rowMapperClass == RowMapper.class ? defaultRowMapper
: (RowMapper<?>) BeanUtils.instantiateClass(rowMapperClass);
}
@Override
public Object execute(Object[] objects) {
String query = queryMethod.getAnnotatedQuery();
String query = determineQuery();
MapSqlParameterSource parameters = new MapSqlParameterSource();
queryMethod.getParameters().getBindableParameters().forEach(p -> {
String parameterName = p.getName().orElseThrow(() -> new IllegalStateException(PARAMETER_NEEDS_TO_BE_NAMED));
parameters.addValue(parameterName, objects[p.getIndex()]);
});
MapSqlParameterSource parameters = bindParameters(objects);
if (queryMethod.isModifyingQuery()) {
int updatedCount = context.getTemplate().update(query, parameters);
Class<?> returnedObjectType = queryMethod.getReturnedObjectType();
return (returnedObjectType == boolean.class || returnedObjectType == Boolean.class) ? updatedCount != 0 : updatedCount;
return (returnedObjectType == boolean.class || returnedObjectType == Boolean.class) ? updatedCount != 0
: updatedCount;
}
if (queryMethod.isCollectionQuery() || queryMethod.isStreamQuery()) {
@@ -76,4 +83,25 @@ class JdbcRepositoryQuery implements RepositoryQuery {
public JdbcQueryMethod getQueryMethod() {
return queryMethod;
}
private String determineQuery() {
String query = queryMethod.getAnnotatedQuery();
if (StringUtils.isEmpty(query)) {
throw new IllegalStateException(String.format("No query specified on %s", queryMethod.getName()));
}
return query;
}
private MapSqlParameterSource bindParameters(Object[] objects) {
MapSqlParameterSource parameters = new MapSqlParameterSource();
queryMethod.getParameters().getBindableParameters().forEach(p -> {
String parameterName = p.getName().orElseThrow(() -> new IllegalStateException(PARAMETER_NEEDS_TO_BE_NAMED));
parameters.addValue(parameterName, objects[p.getIndex()]);
});
return parameters;
}
}