diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java index 05ea371bc..ecf80945b 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/AbstractJdbcQuery.java @@ -42,6 +42,7 @@ import org.springframework.util.Assert; * @author Maciej Walkowiak * @author Mark Paluch * @author Dennis Effing + * @author Mikhail Polivakha * @since 2.0 */ public abstract class AbstractJdbcQuery implements RepositoryQuery { @@ -71,26 +72,42 @@ public abstract class AbstractJdbcQuery implements RepositoryQuery { } /** - * Creates a {@link JdbcQueryExecution} given {@link JdbcQueryMethod}, {@link ResultSetExtractor} an + * Creates a {@link JdbcQueryExecution} given a {@link JdbcQueryMethod}, and ac{@link ResultSetExtractor} or a * {@link RowMapper}. Prefers the given {@link ResultSetExtractor} over {@link RowMapper}. * * @param queryMethod must not be {@literal null}. * @param extractor must not be {@literal null}. * @param rowMapper must not be {@literal null}. * @return a JdbcQueryExecution appropriate for {@literal queryMethod}. Guaranteed to be not {@literal null}. + * @deprecated use {@link #createReadingQueryExecution(ResultSetExtractor, RowMapper)} instead. */ + @Deprecated(since = "3.1", forRemoval = true) + // a better name would be createReadingQueryExecution protected JdbcQueryExecution getQueryExecution(JdbcQueryMethod queryMethod, @Nullable ResultSetExtractor extractor, RowMapper rowMapper) { + return createReadingQueryExecution(extractor, rowMapper); + } + + /** + * Creates a {@link JdbcQueryExecution} given a {@link ResultSetExtractor} or a {@link RowMapper}. Prefers the given + * {@link ResultSetExtractor} over {@link RowMapper}. + * + * @param extractor must not be {@literal null}. + * @param rowMapper must not be {@literal null}. + * @return a JdbcQueryExecution appropriate for {@literal queryMethod}. Guaranteed to be not {@literal null}. + */ + protected JdbcQueryExecution createReadingQueryExecution(@Nullable ResultSetExtractor extractor, + RowMapper rowMapper) { - if (queryMethod.isCollectionQuery()) { - return extractor != null ? getQueryExecution(extractor) : collectionQuery(rowMapper); + if (getQueryMethod().isCollectionQuery()) { + return extractor != null ? createSingleReadingQueryExecution(extractor) : collectionQuery(rowMapper); } - if (queryMethod.isStreamQuery()) { - return extractor != null ? getQueryExecution(extractor) : streamQuery(rowMapper); + if (getQueryMethod().isStreamQuery()) { + return extractor != null ? createSingleReadingQueryExecution(extractor) : streamQuery(rowMapper); } - return extractor != null ? getQueryExecution(extractor) : singleObjectQuery(rowMapper); + return extractor != null ? createSingleReadingQueryExecution(extractor) : singleObjectQuery(rowMapper); } protected JdbcQueryExecution createModifyingQueryExecutor() { @@ -100,7 +117,8 @@ public abstract class AbstractJdbcQuery implements RepositoryQuery { int updatedCount = operations.update(query, parameters); Class returnedObjectType = queryMethod.getReturnedObjectType(); - return (returnedObjectType == boolean.class || returnedObjectType == Boolean.class) ? updatedCount != 0 + return (returnedObjectType == boolean.class || returnedObjectType == Boolean.class) // + ? updatedCount != 0 // : updatedCount; }; } @@ -117,14 +135,15 @@ public abstract class AbstractJdbcQuery implements RepositoryQuery { } JdbcQueryExecution> collectionQuery(RowMapper rowMapper) { - return getQueryExecution(new RowMapperResultSetExtractor<>(rowMapper)); + return createSingleReadingQueryExecution(new RowMapperResultSetExtractor<>(rowMapper)); } /** * Obtain the result type to read from {@link ResultProcessor}. * - * @param resultProcessor the {@link ResultProcessor} used to determine the result type. Must not be {@literal null}. - * @return the type that should get loaded from the database before it gets converted into the actual return type of a method. Guaranteed to be not {@literal null}. + * @param resultProcessor the {@link ResultProcessor} used to determine the result type. Must not be {@literal null}. + * @return the type that should get loaded from the database before it gets converted into the actual return type of a + * method. Guaranteed to be not {@literal null}. */ protected Class resolveTypeToRead(ResultProcessor resultProcessor) { @@ -142,7 +161,7 @@ public abstract class AbstractJdbcQuery implements RepositoryQuery { return (query, parameters) -> operations.queryForStream(query, parameters, rowMapper); } - private JdbcQueryExecution getQueryExecution(ResultSetExtractor resultSetExtractor) { + private JdbcQueryExecution createSingleReadingQueryExecution(ResultSetExtractor resultSetExtractor) { return (query, parameters) -> operations.query(query, parameters, resultSetExtractor); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java index 1b674d4cf..01876f0d6 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQuery.java @@ -52,6 +52,7 @@ import org.springframework.util.Assert; * @author Mark Paluch * @author Jens Schauder * @author Diego Krupitza + * @author Mikhail Polivakha * @since 2.0 */ public class PartTreeJdbcQuery extends AbstractJdbcQuery { @@ -172,27 +173,29 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery { return queryExecution; } + protected ParametrizedQuery createQuery(RelationalParametersParameterAccessor accessor, ReturnedType returnedType) { + + RelationalEntityMetadata entityMetadata = getQueryMethod().getEntityInformation(); + + JdbcQueryCreator queryCreator = new JdbcQueryCreator(context, tree, converter, dialect, entityMetadata, accessor, + getQueryMethod().isSliceQuery(), returnedType, this.getQueryMethod().lookupLockAnnotation()); + return queryCreator.createQuery(getDynamicSort(accessor)); + } + private JdbcQueryExecution getJdbcQueryExecution(@Nullable ResultSetExtractor extractor, RowMapper rowMapper) { + if (getQueryMethod().isPageQuery() || getQueryMethod().isSliceQuery()) { return collectionQuery(rowMapper); } else { + if (getQueryMethod().isModifyingQuery()) { return createModifyingQueryExecutor(); } else { - return getQueryExecution(getQueryMethod(), extractor, rowMapper); + return createReadingQueryExecution(extractor, rowMapper); } } } - protected ParametrizedQuery createQuery(RelationalParametersParameterAccessor accessor, ReturnedType returnedType) { - - RelationalEntityMetadata entityMetadata = getQueryMethod().getEntityInformation(); - - JdbcQueryCreator queryCreator = new JdbcQueryCreator(context, tree, converter, dialect, entityMetadata, accessor, - getQueryMethod().isSliceQuery(), returnedType, this.getQueryMethod().lookupLockAnnotation()); - return queryCreator.createQuery(getDynamicSort(accessor)); - } - /** * {@link JdbcQueryExecution} returning a {@link org.springframework.data.domain.Slice}. * diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/Query.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/Query.java index a772e5746..229a78717 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/Query.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/Query.java @@ -33,10 +33,13 @@ import org.springframework.jdbc.core.RowMapper; * You can also specify the way to extract data from {@link java.sql.ResultSet}. There are 4 attribute of this * annotation you can set to do that: *

- * 1. {@link #resultSetExtractorRef()} - * 2. {@link #resultSetExtractorClass()} - * 3. {@link #rowMapperRef()} - * 4. {@link #rowMapperClass()} + *

    + *
  1. {@link #resultSetExtractorRef()} + *
  2. {@link #resultSetExtractorClass()} + *
  3. {@link #rowMapperRef()} + *
  4. {@link #rowMapperClass()} + *
  5. + *
* * The annotation attributes above are listed in their preference order, that is - the {@link #resultSetExtractorRef()}, * has the highest privilege and, will suppress any other 3 attribute from above, and consequently {@link #rowMapperClass()} @@ -45,6 +48,7 @@ import org.springframework.jdbc.core.RowMapper; * @author Jens Schauder * @author Moises Cisneros * @author Hebert Coelho + * @author Mikhail Polivakha */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java index 690cad065..77c13873d 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/StringBasedJdbcQuery.java @@ -61,13 +61,12 @@ import org.springframework.util.ObjectUtils; * @author Hebert Coelho * @author Chirag Tailor * @author Christopher Klein + * @author Mikhail Polivakha * @since 2.0 */ public class StringBasedJdbcQuery extends AbstractJdbcQuery { private static final String PARAMETER_NEEDS_TO_BE_NAMED = "For queries with named parameters you need to provide names for method parameters; Use @Param for query method parameters, or when on Java 8+ use the javac flag -parameters"; - - private final JdbcQueryMethod queryMethod; private final JdbcConverter converter; private final RowMapperFactory rowMapperFactory; private BeanFactory beanFactory; @@ -104,7 +103,6 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery { Assert.notNull(rowMapperFactory, "RowMapperFactory must not be null"); - this.queryMethod = queryMethod; this.converter = converter; this.rowMapperFactory = rowMapperFactory; this.evaluationContextProvider = evaluationContextProvider; @@ -135,25 +133,24 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery { String query = determineQuery(); if (ObjectUtils.isEmpty(query)) { - throw new IllegalStateException(String.format("No query specified on %s", queryMethod.getName())); + throw new IllegalStateException(String.format("No query specified on %s", getQueryMethod().getName())); } return queryExecution.execute(processSpelExpressions(objects, parameterMap, query), parameterMap); } - private JdbcQueryExecution createJdbcQueryExecution(RelationalParameterAccessor accessor, ResultProcessor processor, ResultProcessingConverter converter) { - JdbcQueryExecution queryExecution; + private JdbcQueryExecution createJdbcQueryExecution(RelationalParameterAccessor accessor, + ResultProcessor processor, ResultProcessingConverter converter) { - if (queryMethod.isModifyingQuery()) { - queryExecution = createModifyingQueryExecutor(); + if (getQueryMethod().isModifyingQuery()) { + return createModifyingQueryExecutor(); } else { RowMapper rowMapper = determineRowMapper(rowMapperFactory.create(resolveTypeToRead(processor)), converter, accessor.findDynamicProjection() != null); - queryExecution = getQueryExecution(queryMethod, determineResultSetExtractor(rowMapper), rowMapper); + return createReadingQueryExecution(determineResultSetExtractor(rowMapper), rowMapper); } - return queryExecution; } private String processSpelExpressions(Object[] objects, MapSqlParameterSource parameterMap, String query) { @@ -162,18 +159,13 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery { .of((counter, expression) -> String.format("__$synthetic$__%d", counter + 1), String::concat) .withEvaluationContextProvider(evaluationContextProvider); - SpelEvaluator spelEvaluator = queryContext.parse(query, queryMethod.getParameters()); + SpelEvaluator spelEvaluator = queryContext.parse(query, getQueryMethod().getParameters()); spelEvaluator.evaluate(objects).forEach(parameterMap::addValue); return spelEvaluator.getQueryString(); } - @Override - public JdbcQueryMethod getQueryMethod() { - return queryMethod; - } - private MapSqlParameterSource bindParameters(RelationalParameterAccessor accessor) { MapSqlParameterSource parameters = new MapSqlParameterSource(); @@ -191,7 +183,7 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery { String parameterName = p.getName().orElseThrow(() -> new IllegalStateException(PARAMETER_NEEDS_TO_BE_NAMED)); - RelationalParameters.RelationalParameter parameter = queryMethod.getParameters().getParameter(p.getIndex()); + RelationalParameters.RelationalParameter parameter = getQueryMethod().getParameters().getParameter(p.getIndex()); ResolvableType resolvableType = parameter.getResolvableType(); Class type = resolvableType.resolve(); Assert.notNull(type, "@Query parameter type could not be resolved"); @@ -233,10 +225,10 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery { private String determineQuery() { - String query = queryMethod.getDeclaredQuery(); + String query = getQueryMethod().getDeclaredQuery(); if (ObjectUtils.isEmpty(query)) { - throw new IllegalStateException(String.format("No query specified on %s", queryMethod.getName())); + throw new IllegalStateException(String.format("No query specified on %s", getQueryMethod().getName())); } return query; @@ -246,7 +238,7 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery { @SuppressWarnings({ "rawtypes", "unchecked" }) ResultSetExtractor determineResultSetExtractor(@Nullable RowMapper rowMapper) { - String resultSetExtractorRef = queryMethod.getResultSetExtractorRef(); + String resultSetExtractorRef = getQueryMethod().getResultSetExtractorRef(); if (!ObjectUtils.isEmpty(resultSetExtractorRef)) { @@ -255,7 +247,7 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery { return (ResultSetExtractor) beanFactory.getBean(resultSetExtractorRef); } - Class resultSetExtractorClass = queryMethod.getResultSetExtractorClass(); + Class resultSetExtractorClass = getQueryMethod().getResultSetExtractorClass(); if (isUnconfigured(resultSetExtractorClass, ResultSetExtractor.class)) { return null; @@ -288,7 +280,7 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery { @Nullable RowMapper determineRowMapper(@Nullable RowMapper defaultMapper) { - String rowMapperRef = queryMethod.getRowMapperRef(); + String rowMapperRef = getQueryMethod().getRowMapperRef(); if (!ObjectUtils.isEmpty(rowMapperRef)) { @@ -297,7 +289,7 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery { return (RowMapper) beanFactory.getBean(rowMapperRef); } - Class rowMapperClass = queryMethod.getRowMapperClass(); + Class rowMapperClass = getQueryMethod().getRowMapperClass(); if (isUnconfigured(rowMapperClass, RowMapper.class)) { return (RowMapper) defaultMapper;