Skip to content

Commit

Permalink
StringBasedJdbcQuery no longer requires BeanFactory.
Browse files Browse the repository at this point in the history
The lookup is now performed by the `RowMapperFactory`.

Closes #1872
Original pull request: #1874
  • Loading branch information
schauder authored and mp911de committed Sep 16, 2024
1 parent 9e91a0e commit d2bb64f
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.jdbc.core.convert.JdbcArrayColumns;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.repository.query.ReturnedType;
Expand Down Expand Up @@ -155,7 +156,34 @@ private <T> JdbcQueryExecution<T> createSingleReadingQueryExecution(ResultSetExt
* @since 2.3
*/
public interface RowMapperFactory {

/**
* Create a {@link RowMapper} based on the expected return type passed in as an argument.
*
* @param result must not be {@code null}.
* @return a {@code RowMapper} producing instances of {@code result}.
*/
RowMapper<Object> create(Class<?> result);

/**
* Obtain a {@code RowMapper} from some other source, typically a {@link org.springframework.beans.factory.BeanFactory}.
*
* @param reference must not be {@code null}.
* @since 3.4
*/
default RowMapper<Object> rowMapperByReference(String reference) {
throw new UnsupportedOperationException("rowMapperByReference is not supported");
}

/**
* Obtain a {@code ResultSetExtractor} from some other source, typically a {@link org.springframework.beans.factory.BeanFactory}.
*
* @param reference must not be {@code null}.
* @since 3.4
*/
default ResultSetExtractor<Object> resultSetExtractorByReference(String reference) {
throw new UnsupportedOperationException("resultSetExtractorByReference is not supported");
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
private final SpelEvaluator spelEvaluator;
private final boolean containsSpelExpressions;
private final String query;
private BeanFactory beanFactory;

private final CachedRowMapperFactory cachedRowMapperFactory;
private final CachedResultSetExtractorFactory cachedResultSetExtractorFactory;
Expand Down Expand Up @@ -353,10 +352,6 @@ private static boolean isUnconfigured(@Nullable Class<?> configuredClass, Class<
return configuredClass == null || configuredClass == defaultClass;
}

public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}

class CachedRowMapperFactory {

private final Lazy<RowMapper<Object>> cachedRowMapper;
Expand All @@ -380,10 +375,7 @@ public CachedRowMapperFactory(Supplier<RowMapper<Object>> defaultMapper) {
this.cachedRowMapper = Lazy.of(() -> {

if (!ObjectUtils.isEmpty(rowMapperRef)) {

Assert.notNull(beanFactory, "When a RowMapperRef is specified the BeanFactory must not be null");

return (RowMapper<Object>) beanFactory.getBean(rowMapperRef);
return rowMapperFactory.rowMapperByReference(rowMapperRef);
}

if (isUnconfigured(rowMapperClass, RowMapper.class)) {
Expand Down Expand Up @@ -434,10 +426,7 @@ public CachedResultSetExtractorFactory(Supplier<RowMapper<?>> resultSetExtractor
this.resultSetExtractorFactory = rowMapper -> {

if (!ObjectUtils.isEmpty(resultSetExtractorRef)) {

Assert.notNull(beanFactory, "When a ResultSetExtractorRef is specified the BeanFactory must not be null");

return (ResultSetExtractor<Object>) beanFactory.getBean(resultSetExtractorRef);
return rowMapperFactory.resultSetExtractorByReference(resultSetExtractorRef);
}

if (isUnconfigured(resultSetExtractorClass, ResultSetExtractor.class)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.springframework.data.jdbc.core.convert.EntityRowMapper;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.repository.QueryMappingConfiguration;
import org.springframework.data.jdbc.repository.query.AbstractJdbcQuery;
import org.springframework.data.jdbc.repository.query.JdbcQueryMethod;
import org.springframework.data.jdbc.repository.query.PartTreeJdbcQuery;
import org.springframework.data.jdbc.repository.query.StringBasedJdbcQuery;
Expand All @@ -42,6 +43,7 @@
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.RepositoryQuery;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations;
Expand Down Expand Up @@ -161,15 +163,36 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repository

String queryString = evaluateTableExpressions(repositoryMetadata, queryMethod.getRequiredQuery());

StringBasedJdbcQuery query = new StringBasedJdbcQuery(queryString, queryMethod, getOperations(),
this::createMapper, getConverter(), evaluationContextProvider);
query.setBeanFactory(getBeanFactory());
return query;
return new StringBasedJdbcQuery(queryString, queryMethod, getOperations(),
new BeanFactoryRowMapperFactory(getBeanFactory()), getConverter(), evaluationContextProvider);
}

throw new IllegalStateException(
String.format("Did neither find a NamedQuery nor an annotated query for method %s", method));
}

private class BeanFactoryRowMapperFactory implements AbstractJdbcQuery.RowMapperFactory {

private final BeanFactory beanFactory;

BeanFactoryRowMapperFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public RowMapper<Object> create(Class<?> result) {
return createMapper(result);
}

@Override
public RowMapper<Object> rowMapperByReference(String reference) {
return beanFactory.getBean(reference, RowMapper.class);
}

@Override
public ResultSetExtractor<Object> resultSetExtractorByReference(String reference) {
return beanFactory.getBean(reference, ResultSetExtractor.class);
}
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.convert.converter.Converter;
import org.springframework.dao.DataAccessException;
import org.springframework.data.convert.ReadingConverter;
Expand Down Expand Up @@ -163,14 +162,9 @@ void cachesCustomMapperAndExtractorInstances() {
@Test // GH-1721
void obtainsCustomRowMapperRef() {

BeanFactory beanFactory = mock(BeanFactory.class);
JdbcQueryMethod queryMethod = createMethod("findAllCustomRowMapperRef");
StringBasedJdbcQuery query = createQuery(queryMethod);
query.setBeanFactory(beanFactory);

CustomRowMapper customRowMapper = new CustomRowMapper();

when(beanFactory.getBean("CustomRowMapper")).thenReturn(customRowMapper);
JdbcQueryMethod queryMethod = createMethod("findAllCustomRowMapperRef");
StringBasedJdbcQuery query = createQuery(queryMethod, "CustomRowMapper", customRowMapper);

RowMapper<?> rowMapper = query.determineRowMapper(queryMethod.getResultProcessor(), false);
ResultSetExtractor<Object> resultSetExtractor = query.determineResultSetExtractor(() -> {
Expand All @@ -184,14 +178,9 @@ void obtainsCustomRowMapperRef() {
@Test // GH-1721
void obtainsCustomResultSetExtractorRef() {

BeanFactory beanFactory = mock(BeanFactory.class);
JdbcQueryMethod queryMethod = createMethod("findAllCustomResultSetExtractorRef");
StringBasedJdbcQuery query = createQuery(queryMethod);
query.setBeanFactory(beanFactory);

CustomResultSetExtractor cre = new CustomResultSetExtractor();

when(beanFactory.getBean("CustomResultSetExtractor")).thenReturn(cre);
JdbcQueryMethod queryMethod = createMethod("findAllCustomResultSetExtractorRef");
StringBasedJdbcQuery query = createQuery(queryMethod, "CustomResultSetExtractor", cre);

RowMapper<?> rowMapper = query.determineRowMapper(queryMethod.getResultProcessor(), false);
ResultSetExtractor<Object> resultSetExtractor = query.determineResultSetExtractor(() -> {
Expand Down Expand Up @@ -332,10 +321,10 @@ void queryByListOfTuples() {
String[][] tuples = { new String[] { "Albert", "Einstein" }, new String[] { "Richard", "Feynman" } };

SqlParameterSource parameterSource = forMethod("findByListOfTuples", List.class) //
.withArguments(Arrays.asList(tuples)) //
.withArguments(Arrays.asList(tuples))//
.extractParameterSource();

assertThat(parameterSource.getValue("tuples")).asInstanceOf(LIST) //
assertThat(parameterSource.getValue("tuples")).asInstanceOf(LIST)//
.containsExactly(tuples);

assertThat(parameterSource.getSqlType("tuples")).isEqualTo(JdbcUtil.TYPE_UNKNOWN.getVendorTypeNumber());
Expand Down Expand Up @@ -441,7 +430,11 @@ private JdbcQueryMethod createMethod(String methodName, Class<?>... paramTypes)
}

private StringBasedJdbcQuery createQuery(JdbcQueryMethod queryMethod) {
return new StringBasedJdbcQuery(queryMethod, operations, defaultRowMapper, converter, evaluationContextProvider);
return createQuery(queryMethod, null, null);
}

private StringBasedJdbcQuery createQuery(JdbcQueryMethod queryMethod, String preparedReference, Object value) {
return new StringBasedJdbcQuery(queryMethod, operations, new StubRowMapperFactory(preparedReference, value), converter, evaluationContextProvider);
}

interface MyRepository extends Repository<Object, Long> {
Expand Down Expand Up @@ -636,4 +629,37 @@ public Object getRootObject() {
}
}

private class StubRowMapperFactory implements AbstractJdbcQuery.RowMapperFactory {

private final String preparedReference;
private final Object value;

StubRowMapperFactory(String preparedReference, Object value) {
this.preparedReference = preparedReference;
this.value = value;
}

@Override
public RowMapper<Object> create(Class<?> result) {
return defaultRowMapper;
}

@Override
public RowMapper<Object> rowMapperByReference(String reference) {

if (preparedReference.equals(reference)) {
return (RowMapper<Object>) value;
}
return AbstractJdbcQuery.RowMapperFactory.super.rowMapperByReference(reference);
}

@Override
public ResultSetExtractor<Object> resultSetExtractorByReference(String reference) {

if (preparedReference.equals(reference)) {
return (ResultSetExtractor<Object>) value;
}
return AbstractJdbcQuery.RowMapperFactory.super.resultSetExtractorByReference(reference);
}
}
}

0 comments on commit d2bb64f

Please sign in to comment.