Extract RowMapper into top level classes.
Signed-off-by: mipo256 <mikhailpolivakha@gmail.com> Commit message changed by Jens Schauder Original pull request #2000 Closes #1998
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2020-2025 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
|
||||
*
|
||||
* https://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.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Abstract {@link RowMapper} that delegates the actual mapping logic to a {@link AbstractDelegatingRowMapper#delegate delegate}
|
||||
*
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
public abstract class AbstractDelegatingRowMapper<T> implements RowMapper<T> {
|
||||
|
||||
private final RowMapper<T> delegate;
|
||||
|
||||
protected AbstractDelegatingRowMapper(RowMapper<T> delegate) {
|
||||
Assert.notNull(delegate, "Delegating RowMapper cannot be null");
|
||||
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
T intermediate = delegate.mapRow(rs, rowNum);
|
||||
return postProcessMapping(intermediate);
|
||||
}
|
||||
|
||||
/**
|
||||
* The post-processing callback for implementations.
|
||||
*
|
||||
* @return the mapped entity after applying post-processing logic
|
||||
*/
|
||||
protected T postProcessMapping(@Nullable T object) {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
@@ -153,60 +153,25 @@ public abstract class AbstractJdbcQuery implements RepositoryQuery {
|
||||
* Factory to create a {@link RowMapper} for a given class.
|
||||
*
|
||||
* @since 2.3
|
||||
* @deprecated Use {@link org.springframework.data.jdbc.repository.query.RowMapperFactory} instead
|
||||
*/
|
||||
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> getRowMapper(String reference) {
|
||||
throw new UnsupportedOperationException("getRowMapper 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> getResultSetExtractor(String reference) {
|
||||
throw new UnsupportedOperationException("getResultSetExtractor is not supported");
|
||||
}
|
||||
}
|
||||
@Deprecated(forRemoval = true, since = "3.4.4")
|
||||
public interface RowMapperFactory extends org.springframework.data.jdbc.repository.query.RowMapperFactory { }
|
||||
|
||||
/**
|
||||
* Delegating {@link RowMapper} that reads a row into {@code T} and converts it afterwards into {@code Object}.
|
||||
*
|
||||
* @param <T>
|
||||
* @since 2.3
|
||||
* @deprecated use {@link org.springframework.data.jdbc.repository.query.ConvertingRowMapper} instead
|
||||
*/
|
||||
protected static class ConvertingRowMapper<T> implements RowMapper<Object> {
|
||||
|
||||
private final RowMapper<T> delegate;
|
||||
private final Converter<Object, Object> converter;
|
||||
@Deprecated(forRemoval = true, since = "3.4.4")
|
||||
protected static class ConvertingRowMapper<T> extends
|
||||
org.springframework.data.jdbc.repository.query.ConvertingRowMapper {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public ConvertingRowMapper(RowMapper<T> delegate, Converter<Object, Object> converter) {
|
||||
this.delegate = delegate;
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
|
||||
T object = delegate.mapRow(rs, rowNum);
|
||||
|
||||
return object == null ? null : converter.convert(object);
|
||||
super((RowMapper<Object>) delegate, converter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2020-2025 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
|
||||
*
|
||||
* https://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.sql.ResultSet;
|
||||
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.mapping.callback.EntityCallbacks;
|
||||
import org.springframework.data.relational.core.mapping.event.AfterConvertCallback;
|
||||
import org.springframework.data.relational.core.mapping.event.AfterConvertEvent;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Delegating {@link RowMapper} implementation that applies post-processing logic
|
||||
* after the {@link RowMapper#mapRow(ResultSet, int)}. In particular, it emits the
|
||||
* {@link AfterConvertEvent} event and invokes the {@link AfterConvertCallback} callbacks.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
public class CallbackCapableRowMapper<T> extends AbstractDelegatingRowMapper<T> {
|
||||
|
||||
private final ApplicationEventPublisher publisher;
|
||||
private final @Nullable EntityCallbacks callbacks;
|
||||
|
||||
public CallbackCapableRowMapper(RowMapper<T> delegate, ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks) {
|
||||
super(delegate);
|
||||
this.publisher = publisher;
|
||||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T postProcessMapping(@Nullable T object) {
|
||||
if (object != null) {
|
||||
|
||||
publisher.publishEvent(new AfterConvertEvent<>(object));
|
||||
|
||||
if (callbacks != null) {
|
||||
return callbacks.callback(AfterConvertCallback.class, object);
|
||||
}
|
||||
|
||||
}
|
||||
return object;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2020-2025 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
|
||||
*
|
||||
* https://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 org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Delegating {@link RowMapper} that reads a row into {@code T} and converts it afterwards into {@code Object}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Mikhail Polivakha
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public class ConvertingRowMapper extends AbstractDelegatingRowMapper<Object> {
|
||||
|
||||
private final Converter<Object, Object> converter;
|
||||
|
||||
public ConvertingRowMapper(RowMapper<Object> delegate, Converter<Object, Object> converter) {
|
||||
super(delegate);
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessMapping(@Nullable Object object) {
|
||||
return object != null ? converter.convert(object) : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2020-2025 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
|
||||
*
|
||||
* https://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 org.springframework.context.ApplicationEventPublisher;
|
||||
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.mapping.callback.EntityCallbacks;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.SingleColumnRowMapper;
|
||||
|
||||
/**
|
||||
* Default implementation of {@link RowMapperFactory}. Honors the custom mappings defined
|
||||
* in {@link QueryMappingConfiguration}.
|
||||
* <p>
|
||||
* This implementation is not capable of loading the {@link RowMapper} or {@link ResultSetExtractor}
|
||||
* by reference via corresponding methods from {@link RowMapperFactory}.
|
||||
*
|
||||
* @implNote Public APIs of this class are thread-safe.
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
public class DefaultRowMapperFactory implements RowMapperFactory {
|
||||
|
||||
private final RelationalMappingContext context;
|
||||
private final JdbcConverter converter;
|
||||
private final QueryMappingConfiguration queryMappingConfiguration;
|
||||
private final EntityCallbacks entityCallbacks;
|
||||
private final ApplicationEventPublisher publisher;
|
||||
|
||||
public DefaultRowMapperFactory(
|
||||
RelationalMappingContext context,
|
||||
JdbcConverter converter,
|
||||
QueryMappingConfiguration queryMappingConfiguration,
|
||||
EntityCallbacks entityCallbacks,
|
||||
ApplicationEventPublisher publisher
|
||||
) {
|
||||
this.context = context;
|
||||
this.converter = converter;
|
||||
this.queryMappingConfiguration = queryMappingConfiguration;
|
||||
this.entityCallbacks = entityCallbacks;
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public RowMapper<Object> create(Class<?> returnedObjectType) {
|
||||
|
||||
RelationalPersistentEntity<?> persistentEntity = context.getPersistentEntity(returnedObjectType);
|
||||
|
||||
if (persistentEntity == null) {
|
||||
return (RowMapper<Object>) SingleColumnRowMapper.newInstance(returnedObjectType,
|
||||
converter.getConversionService());
|
||||
}
|
||||
|
||||
return (RowMapper<Object>) determineDefaultMapper(returnedObjectType);
|
||||
}
|
||||
|
||||
private RowMapper<?> determineDefaultMapper(Class<?> returnedObjectType) {
|
||||
|
||||
RowMapper<?> configuredQueryMapper = queryMappingConfiguration.getRowMapper(returnedObjectType);
|
||||
|
||||
if (configuredQueryMapper != null) {
|
||||
return configuredQueryMapper;
|
||||
}
|
||||
|
||||
EntityRowMapper<?> defaultEntityRowMapper = new EntityRowMapper<>( //
|
||||
context.getRequiredPersistentEntity(returnedObjectType), //
|
||||
converter //
|
||||
);
|
||||
|
||||
return new CallbackCapableRowMapper<>(defaultEntityRowMapper, publisher, entityCallbacks);
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,6 @@ import java.util.List;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.LongSupplier;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
@@ -98,7 +97,7 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery {
|
||||
* @since 2.3
|
||||
*/
|
||||
public PartTreeJdbcQuery(RelationalMappingContext context, JdbcQueryMethod queryMethod, Dialect dialect,
|
||||
JdbcConverter converter, NamedParameterJdbcOperations operations, RowMapperFactory rowMapperFactory) {
|
||||
JdbcConverter converter, NamedParameterJdbcOperations operations, org.springframework.data.jdbc.repository.query.RowMapperFactory rowMapperFactory) {
|
||||
|
||||
super(queryMethod, operations);
|
||||
|
||||
@@ -292,7 +291,7 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery {
|
||||
private final Lazy<RowMapper<?>> rowMapper;
|
||||
private final Function<ResultProcessor, RowMapper<?>> rowMapperFunction;
|
||||
|
||||
public CachedRowMapperFactory(PartTree tree, RowMapperFactory rowMapperFactory, RelationalConverter converter,
|
||||
public CachedRowMapperFactory(PartTree tree, org.springframework.data.jdbc.repository.query.RowMapperFactory rowMapperFactory, RelationalConverter converter,
|
||||
ResultProcessor defaultResultProcessor) {
|
||||
|
||||
this.rowMapperFunction = processor -> {
|
||||
@@ -302,7 +301,7 @@ public class PartTreeJdbcQuery extends AbstractJdbcQuery {
|
||||
}
|
||||
Converter<Object, Object> resultProcessingConverter = new ResultProcessingConverter(processor,
|
||||
converter.getMappingContext(), converter.getEntityInstantiators());
|
||||
return new ConvertingRowMapper<>(rowMapperFactory.create(processor.getReturnedType().getDomainType()),
|
||||
return new org.springframework.data.jdbc.repository.query.ConvertingRowMapper(rowMapperFactory.create(processor.getReturnedType().getDomainType()),
|
||||
resultProcessingConverter);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2020-2025 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
|
||||
*
|
||||
* https://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 org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
|
||||
/**
|
||||
* Factory to create a {@link RowMapper} for a given class.
|
||||
*
|
||||
* @author Jens Schauder
|
||||
* @author Mikhail Polivakha
|
||||
*
|
||||
* @since 2.3
|
||||
*/
|
||||
public interface RowMapperFactory {
|
||||
|
||||
/**
|
||||
* Obtain 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 {@link 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> getRowMapper(String reference) {
|
||||
throw new UnsupportedOperationException("getRowMapper by reference 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> getResultSetExtractor(String reference) {
|
||||
throw new UnsupportedOperationException("getResultSetExtractor by reference is not supported");
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ 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 use the javac flag -parameters";
|
||||
private static final String LOCKING_IS_NOT_SUPPORTED = "Currently, @Lock is supported only on derived queries. In other words, for queries created with @Query, the locking condition specified with @Lock does nothing. Offending method: ";
|
||||
private final JdbcConverter converter;
|
||||
private final RowMapperFactory rowMapperFactory;
|
||||
private final org.springframework.data.jdbc.repository.query.RowMapperFactory rowMapperFactory;
|
||||
private final ValueExpressionQueryRewriter.ParsedQuery parsedQuery;
|
||||
private final String query;
|
||||
|
||||
@@ -85,7 +85,7 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
|
||||
|
||||
/**
|
||||
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
|
||||
* and {@link RowMapperFactory}.
|
||||
* and {@link org.springframework.data.jdbc.repository.query.RowMapperFactory}.
|
||||
*
|
||||
* @param queryMethod must not be {@literal null}.
|
||||
* @param operations must not be {@literal null}.
|
||||
@@ -95,13 +95,13 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
|
||||
* @since 3.4
|
||||
*/
|
||||
public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
|
||||
RowMapperFactory rowMapperFactory, JdbcConverter converter, ValueExpressionDelegate delegate) {
|
||||
org.springframework.data.jdbc.repository.query.RowMapperFactory rowMapperFactory, JdbcConverter converter, ValueExpressionDelegate delegate) {
|
||||
this(queryMethod.getRequiredQuery(), queryMethod, operations, rowMapperFactory, converter, delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
|
||||
* and {@link RowMapperFactory}.
|
||||
* and {@link org.springframework.data.jdbc.repository.query.RowMapperFactory}.
|
||||
*
|
||||
* @param query must not be {@literal null} or empty.
|
||||
* @param queryMethod must not be {@literal null}.
|
||||
@@ -112,7 +112,7 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
|
||||
* @since 3.4
|
||||
*/
|
||||
public StringBasedJdbcQuery(String query, JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
|
||||
RowMapperFactory rowMapperFactory, JdbcConverter converter, ValueExpressionDelegate delegate) {
|
||||
org.springframework.data.jdbc.repository.query.RowMapperFactory rowMapperFactory, JdbcConverter converter, ValueExpressionDelegate delegate) {
|
||||
super(queryMethod, operations);
|
||||
Assert.hasText(query, "Query must not be null or empty");
|
||||
Assert.notNull(rowMapperFactory, "RowMapperFactory must not be null");
|
||||
@@ -318,7 +318,7 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
|
||||
|
||||
ResultProcessingConverter converter = new ResultProcessingConverter(resultProcessor,
|
||||
this.converter.getMappingContext(), this.converter.getEntityInstantiators());
|
||||
return new ConvertingRowMapper<>(rowMapperToUse, converter);
|
||||
return new org.springframework.data.jdbc.repository.query.ConvertingRowMapper(rowMapperToUse, converter);
|
||||
}
|
||||
|
||||
return cachedRowMapperFactory.getRowMapper();
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 2020-2025 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
|
||||
*
|
||||
* https://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.support;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.data.jdbc.core.convert.JdbcConverter;
|
||||
import org.springframework.data.jdbc.repository.QueryMappingConfiguration;
|
||||
import org.springframework.data.jdbc.repository.query.DefaultRowMapperFactory;
|
||||
import org.springframework.data.jdbc.repository.query.RowMapperFactory;
|
||||
import org.springframework.data.mapping.callback.EntityCallbacks;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
import org.springframework.jdbc.core.ResultSetExtractor;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* This {@link RowMapperFactory} implementation extends the {@link DefaultRowMapperFactory}
|
||||
* by adding the capabilities to load {@link RowMapper} or {@link ResultSetExtractor} beans by
|
||||
* their names in {@link BeanFactory}.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Jens Schauder
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class BeanFactoryAwareRowMapperFactory extends DefaultRowMapperFactory {
|
||||
|
||||
private final @Nullable BeanFactory beanFactory;
|
||||
|
||||
public BeanFactoryAwareRowMapperFactory(
|
||||
RelationalMappingContext context,
|
||||
JdbcConverter converter,
|
||||
QueryMappingConfiguration queryMappingConfiguration,
|
||||
EntityCallbacks entityCallbacks,
|
||||
ApplicationEventPublisher publisher,
|
||||
@Nullable BeanFactory beanFactory
|
||||
) {
|
||||
super(context, converter, queryMappingConfiguration, entityCallbacks, publisher);
|
||||
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowMapper<Object> getRowMapper(String reference) {
|
||||
if (beanFactory == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot resolve RowMapper bean reference '" + reference + "'; BeanFactory is not configured.");
|
||||
}
|
||||
|
||||
return beanFactory.getBean(reference, RowMapper.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSetExtractor<Object> getResultSetExtractor(String reference) {
|
||||
if (beanFactory == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot resolve ResultSetExtractor bean reference '" + reference + "'; BeanFactory is not configured.");
|
||||
}
|
||||
|
||||
return beanFactory.getBean(reference, ResultSetExtractor.class);
|
||||
}
|
||||
}
|
||||
@@ -16,36 +16,28 @@
|
||||
package org.springframework.data.jdbc.repository.support;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
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.DefaultRowMapperFactory;
|
||||
import org.springframework.data.jdbc.repository.query.JdbcQueryMethod;
|
||||
import org.springframework.data.jdbc.repository.query.PartTreeJdbcQuery;
|
||||
import org.springframework.data.jdbc.repository.query.RowMapperFactory;
|
||||
import org.springframework.data.jdbc.repository.query.StringBasedJdbcQuery;
|
||||
import org.springframework.data.mapping.callback.EntityCallbacks;
|
||||
import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.relational.core.dialect.Dialect;
|
||||
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
|
||||
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
|
||||
import org.springframework.data.relational.core.mapping.event.AfterConvertCallback;
|
||||
import org.springframework.data.relational.core.mapping.event.AfterConvertEvent;
|
||||
import org.springframework.data.relational.repository.support.RelationalQueryLookupStrategy;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.data.repository.query.ValueExpressionDelegate;
|
||||
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;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -62,6 +54,7 @@ import org.springframework.util.Assert;
|
||||
* @author Hebert Coelho
|
||||
* @author Diego Krupitza
|
||||
* @author Christopher Klein
|
||||
* @author Mikhail Polivakha
|
||||
*/
|
||||
abstract class JdbcQueryLookupStrategy extends RelationalQueryLookupStrategy {
|
||||
|
||||
@@ -109,6 +102,8 @@ abstract class JdbcQueryLookupStrategy extends RelationalQueryLookupStrategy {
|
||||
*/
|
||||
static class CreateQueryLookupStrategy extends JdbcQueryLookupStrategy {
|
||||
|
||||
private final RowMapperFactory rowMapperFactory;
|
||||
|
||||
CreateQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks,
|
||||
RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
|
||||
QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
|
||||
@@ -116,6 +111,8 @@ abstract class JdbcQueryLookupStrategy extends RelationalQueryLookupStrategy {
|
||||
|
||||
super(publisher, callbacks, context, converter, dialect, queryMappingConfiguration, operations,
|
||||
delegate);
|
||||
|
||||
this.rowMapperFactory = new DefaultRowMapperFactory(getMappingContext(), getConverter(), getQueryMappingConfiguration(), getCallbacks(), getPublisher());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,8 +121,7 @@ abstract class JdbcQueryLookupStrategy extends RelationalQueryLookupStrategy {
|
||||
|
||||
JdbcQueryMethod queryMethod = getJdbcQueryMethod(method, repositoryMetadata, projectionFactory, namedQueries);
|
||||
|
||||
return new PartTreeJdbcQuery(getMappingContext(), queryMethod, getDialect(), getConverter(), getOperations(),
|
||||
this::createMapper);
|
||||
return new PartTreeJdbcQuery(getMappingContext(), queryMethod, getDialect(), getConverter(), getOperations(), rowMapperFactory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +134,7 @@ abstract class JdbcQueryLookupStrategy extends RelationalQueryLookupStrategy {
|
||||
*/
|
||||
static class DeclaredQueryLookupStrategy extends JdbcQueryLookupStrategy {
|
||||
|
||||
private final AbstractJdbcQuery.RowMapperFactory rowMapperFactory;
|
||||
private final RowMapperFactory rowMapperFactory;
|
||||
|
||||
DeclaredQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks,
|
||||
RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
|
||||
@@ -147,7 +143,7 @@ abstract class JdbcQueryLookupStrategy extends RelationalQueryLookupStrategy {
|
||||
super(publisher, callbacks, context, converter, dialect, queryMappingConfiguration, operations,
|
||||
delegate);
|
||||
|
||||
this.rowMapperFactory = new BeanFactoryRowMapperFactory(beanfactory);
|
||||
this.rowMapperFactory = new BeanFactoryAwareRowMapperFactory(context, converter, queryMappingConfiguration, callbacks, publisher, beanfactory);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,44 +168,6 @@ abstract class JdbcQueryLookupStrategy extends RelationalQueryLookupStrategy {
|
||||
throw new IllegalStateException(
|
||||
String.format("Did neither find a NamedQuery nor an annotated query for method %s", method));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private class BeanFactoryRowMapperFactory implements AbstractJdbcQuery.RowMapperFactory {
|
||||
|
||||
private final @Nullable BeanFactory beanFactory;
|
||||
|
||||
BeanFactoryRowMapperFactory(@Nullable BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowMapper<Object> create(Class<?> result) {
|
||||
return createMapper(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowMapper<Object> getRowMapper(String reference) {
|
||||
|
||||
if (beanFactory == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot resolve RowMapper bean reference '" + reference + "'; BeanFactory is not configured.");
|
||||
}
|
||||
|
||||
return beanFactory.getBean(reference, RowMapper.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSetExtractor<Object> getResultSetExtractor(String reference) {
|
||||
|
||||
if (beanFactory == null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot resolve ResultSetExtractor bean reference '" + reference + "'; BeanFactory is not configured.");
|
||||
}
|
||||
|
||||
return beanFactory.getBean(reference, ResultSetExtractor.class);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,57 +278,15 @@ abstract class JdbcQueryLookupStrategy extends RelationalQueryLookupStrategy {
|
||||
return operations;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
RowMapper<Object> createMapper(Class<?> returnedObjectType) {
|
||||
QueryMappingConfiguration getQueryMappingConfiguration() {
|
||||
return queryMappingConfiguration;
|
||||
}
|
||||
|
||||
RelationalPersistentEntity<?> persistentEntity = getMappingContext().getPersistentEntity(returnedObjectType);
|
||||
EntityCallbacks getCallbacks() {
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
if (persistentEntity == null) {
|
||||
return (RowMapper<Object>) SingleColumnRowMapper.newInstance(returnedObjectType,
|
||||
converter.getConversionService());
|
||||
}
|
||||
|
||||
return (RowMapper<Object>) determineDefaultMapper(returnedObjectType);
|
||||
}
|
||||
|
||||
private RowMapper<?> determineDefaultMapper(Class<?> returnedObjectType) {
|
||||
|
||||
RowMapper<?> configuredQueryMapper = queryMappingConfiguration.getRowMapper(returnedObjectType);
|
||||
|
||||
if (configuredQueryMapper != null)
|
||||
return configuredQueryMapper;
|
||||
|
||||
EntityRowMapper<?> defaultEntityRowMapper = new EntityRowMapper<>( //
|
||||
getMappingContext().getRequiredPersistentEntity(returnedObjectType), //
|
||||
converter //
|
||||
);
|
||||
|
||||
return new PostProcessingRowMapper<>(defaultEntityRowMapper);
|
||||
}
|
||||
|
||||
class PostProcessingRowMapper<T> implements RowMapper<T> {
|
||||
|
||||
private final RowMapper<T> delegate;
|
||||
|
||||
PostProcessingRowMapper(RowMapper<T> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
|
||||
|
||||
T entity = delegate.mapRow(rs, rowNum);
|
||||
|
||||
if (entity != null) {
|
||||
|
||||
publisher.publishEvent(new AfterConvertEvent<>(entity));
|
||||
|
||||
if (callbacks != null) {
|
||||
return callbacks.callback(AfterConvertCallback.class, entity);
|
||||
}
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
ApplicationEventPublisher getPublisher() {
|
||||
return publisher;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,7 +652,7 @@ class StringBasedJdbcQueryUnitTests {
|
||||
}
|
||||
}
|
||||
|
||||
private class StubRowMapperFactory implements AbstractJdbcQuery.RowMapperFactory {
|
||||
private class StubRowMapperFactory implements RowMapperFactory {
|
||||
|
||||
private final String preparedReference;
|
||||
private final Object value;
|
||||
@@ -662,7 +662,6 @@ class StringBasedJdbcQueryUnitTests {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowMapper<Object> create(Class<?> result) {
|
||||
return defaultRowMapper;
|
||||
}
|
||||
@@ -673,7 +672,7 @@ class StringBasedJdbcQueryUnitTests {
|
||||
if (preparedReference.equals(reference)) {
|
||||
return (RowMapper<Object>) value;
|
||||
}
|
||||
return AbstractJdbcQuery.RowMapperFactory.super.getRowMapper(reference);
|
||||
return RowMapperFactory.super.getRowMapper(reference);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -682,7 +681,7 @@ class StringBasedJdbcQueryUnitTests {
|
||||
if (preparedReference.equals(reference)) {
|
||||
return (ResultSetExtractor<Object>) value;
|
||||
}
|
||||
return AbstractJdbcQuery.RowMapperFactory.super.getResultSetExtractor(reference);
|
||||
return RowMapperFactory.super.getResultSetExtractor(reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user