Skip to content

GH-2020 Added SqlTypeResolver abstraction #2024

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,47 @@
import org.springframework.core.MethodParameter;
import org.springframework.data.jdbc.core.convert.JdbcColumnTypes;
import org.springframework.data.jdbc.support.JdbcUtil;
import org.springframework.data.relational.core.dialect.DefaultSqlTypeResolver;
import org.springframework.data.relational.core.dialect.SqlTypeResolver;
import org.springframework.data.relational.repository.query.RelationalParameters;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.ParametersSource;
import org.springframework.data.util.Lazy;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;

/**
* Custom extension of {@link RelationalParameters}.
*
* @author Mark Paluch
* @author Mikhail Polivakha
* @since 3.2.6
*/
public class JdbcParameters extends RelationalParameters {

/**
* Creates a new {@link JdbcParameters} instance from the given {@link ParametersSource}.
* Creates a new {@link JdbcParameters} instance from the given {@link ParametersSource}. Uses the {@link DefaultSqlTypeResolver}.
*
* @param parametersSource must not be {@literal null}.
*/
public JdbcParameters(ParametersSource parametersSource) {
super(parametersSource,
methodParameter -> new JdbcParameter(methodParameter, parametersSource.getDomainTypeInformation()));
methodParameter -> new JdbcParameter(methodParameter, parametersSource.getDomainTypeInformation(),
Lazy.of(DefaultSqlTypeResolver.INSTANCE)));
}

/**
* Creates a new {@link JdbcParameters} instance from the given {@link ParametersSource} and given {@link SqlTypeResolver}.
*
* @param parametersSource must not be {@literal null}.
* @param sqlTypeResolver must not be {@literal null}.
*/
public JdbcParameters(ParametersSource parametersSource, Lazy<SqlTypeResolver> sqlTypeResolver) {
super(parametersSource,
methodParameter -> new JdbcParameter(methodParameter, parametersSource.getDomainTypeInformation(), sqlTypeResolver));

Assert.notNull(sqlTypeResolver, "SqlTypeResolver must not be null");
Assert.notNull(parametersSource, "ParametersSource must not be null");
}

@SuppressWarnings({ "rawtypes", "unchecked" })
Expand All @@ -69,27 +88,42 @@ protected JdbcParameters createFrom(List<RelationalParameter> parameters) {
*/
public static class JdbcParameter extends RelationalParameter {

private final SQLType sqlType;
private final Lazy<SQLType> sqlType;
private final Lazy<SQLType> actualSqlType;

/**
* Creates a new {@link RelationalParameter}.
*
* @param parameter must not be {@literal null}.
*/
JdbcParameter(MethodParameter parameter, TypeInformation<?> domainType) {
JdbcParameter(MethodParameter parameter, TypeInformation<?> domainType, Lazy<SqlTypeResolver> sqlTypeResolver) {
super(parameter, domainType);

TypeInformation<?> typeInformation = getTypeInformation();

sqlType = JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getType()));
sqlType = Lazy.of(() -> {
SQLType resolvedSqlType = sqlTypeResolver.get().resolveSqlType(this);

if (resolvedSqlType == null) {
return JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getType()));
} else {
return resolvedSqlType;
}
});

actualSqlType = Lazy.of(() -> {
SQLType resolvedActualSqlType = sqlTypeResolver.get().resolveActualSqlType(this);

actualSqlType = Lazy.of(() -> JdbcUtil
.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getActualType().getType())));
if (resolvedActualSqlType == null) {
return JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getActualType().getType()));
} else {
return resolvedActualSqlType;
}
});
}

public SQLType getSqlType() {
return sqlType;
return sqlType.get();
}

public SQLType getActualSqlType() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,28 @@

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.relational.core.dialect.DefaultSqlTypeResolver;
import org.springframework.data.relational.core.dialect.SqlTypeResolver;
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
import org.springframework.data.relational.repository.Lock;
import org.springframework.data.relational.repository.query.RelationalEntityMetadata;
import org.springframework.data.relational.repository.query.SimpleRelationalEntityMetadata;
import org.springframework.data.repository.core.NamedQueries;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ParametersSource;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.util.Lazy;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.lang.Nullable;
Expand All @@ -52,6 +57,7 @@
* @author Hebert Coelho
* @author Diego Krupitza
* @author Mark Paluch
* @author Mikhail Polivakha
*/
public class JdbcQueryMethod extends QueryMethod {

Expand All @@ -62,22 +68,33 @@ public class JdbcQueryMethod extends QueryMethod {
private @Nullable RelationalEntityMetadata<?> metadata;
private final boolean modifyingQuery;

private final SqlTypeResolver sqlTypeResolver;

// TODO: Remove NamedQueries and put it into JdbcQueryLookupStrategy
public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
NamedQueries namedQueries,
MappingContext<? extends RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> mappingContext) {
this(method, metadata, factory, namedQueries, mappingContext, DefaultSqlTypeResolver.INSTANCE);
}

public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
NamedQueries namedQueries,
MappingContext<? extends RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> mappingContext,
SqlTypeResolver sqlTypeResolver) {

super(method, metadata, factory);
this.namedQueries = namedQueries;
this.method = method;
this.mappingContext = mappingContext;
this.annotationCache = new ConcurrentReferenceHashMap<>();
this.modifyingQuery = AnnotationUtils.findAnnotation(method, Modifying.class) != null;
this.sqlTypeResolver = sqlTypeResolver;
}

// SqlTypeResolver has to be wrapped, becuase the createParameters() is invoked in the parents constructor before child initialization
@Override
protected Parameters<?, ?> createParameters(ParametersSource parametersSource) {
return new JdbcParameters(parametersSource);
return new JdbcParameters(parametersSource, Lazy.of(() -> this.sqlTypeResolver));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package org.springframework.data.jdbc.repository.query;

import static org.springframework.data.jdbc.repository.query.JdbcQueryExecution.*;
import static org.springframework.data.jdbc.repository.query.JdbcQueryExecution.ResultProcessingConverter;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
Expand All @@ -32,21 +32,17 @@
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.data.expression.ValueEvaluationContext;
import org.springframework.data.expression.ValueExpressionParser;
import org.springframework.data.jdbc.core.convert.JdbcColumnTypes;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.mapping.JdbcValue;
import org.springframework.data.jdbc.support.JdbcUtil;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
import org.springframework.data.repository.query.CachingValueExpressionDelegate;
import org.springframework.data.repository.query.Parameter;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider;
import org.springframework.data.repository.query.QueryMethodValueEvaluationContextAccessor;
import org.springframework.data.repository.query.ResultProcessor;
import org.springframework.data.repository.query.ValueExpressionDelegate;
import org.springframework.data.repository.query.ValueExpressionQueryRewriter;
Expand Down Expand Up @@ -95,8 +91,8 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
* and {@link RowMapper}.
*
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param defaultRowMapper can be {@literal null} (only in case of a modifying query).
* @deprecated since 3.4, use the constructors accepting {@link ValueExpressionDelegate} instead.
*/
Expand All @@ -112,10 +108,10 @@ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOpera
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
* and {@link RowMapperFactory}.
*
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param rowMapperFactory must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param rowMapperFactory must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
* @since 2.3
* @deprecated use alternative constructor
Expand All @@ -132,11 +128,11 @@ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOpera
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
* and {@link RowMapperFactory}.
*
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param rowMapperFactory must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param delegate must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param delegate must not be {@literal null}.
* @since 3.4
*/
public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
Expand All @@ -148,12 +144,12 @@ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOpera
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
* and {@link RowMapperFactory}.
*
* @param query must not be {@literal null} or empty.
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param query must not be {@literal null} or empty.
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param rowMapperFactory must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param delegate must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param delegate must not be {@literal null}.
* @since 3.4
*/
public StringBasedJdbcQuery(String query, JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
Expand Down Expand Up @@ -201,11 +197,11 @@ public StringBasedJdbcQuery(String query, JdbcQueryMethod queryMethod, NamedPara
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
* and {@link RowMapperFactory}.
*
* @param query must not be {@literal null} or empty.
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param rowMapperFactory must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param query must not be {@literal null} or empty.
* @param queryMethod must not be {@literal null}.
* @param operations must not be {@literal null}.
* @param rowMapperFactory must not be {@literal null}.
* @param converter must not be {@literal null}.
* @param evaluationContextProvider must not be {@literal null}.
* @since 3.4
* @deprecated since 3.4, use the constructors accepting {@link ValueExpressionDelegate} instead.
Expand All @@ -215,9 +211,9 @@ public StringBasedJdbcQuery(String query, JdbcQueryMethod queryMethod, NamedPara
RowMapperFactory rowMapperFactory, JdbcConverter converter,
QueryMethodEvaluationContextProvider evaluationContextProvider) {
this(query, queryMethod, operations, rowMapperFactory, converter, new CachingValueExpressionDelegate(
new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), rootObject -> evaluationContextProvider
.getEvaluationContext(queryMethod.getParameters(), new Object[] { rootObject })),
ValueExpressionParser.create()));
new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(),
rootObject -> evaluationContextProvider.getEvaluationContext(queryMethod.getParameters(),
new Object[] { rootObject })), ValueExpressionParser.create()));
}

@Override
Expand Down Expand Up @@ -413,7 +409,8 @@ private static boolean isUnconfigured(@Nullable Class<?> configuredClass, Class<
}

@Deprecated(since = "3.4")
public void setBeanFactory(BeanFactory beanFactory) {}
public void setBeanFactory(BeanFactory beanFactory) {
}

class CachedRowMapperFactory {

Expand Down Expand Up @@ -472,19 +469,20 @@ public CachedResultSetExtractorFactory(Supplier<RowMapper<?>> resultSetExtractor
String resultSetExtractorRef = getQueryMethod().getResultSetExtractorRef();
Class<? extends ResultSetExtractor> resultSetExtractorClass = getQueryMethod().getResultSetExtractorClass();

if (!ObjectUtils.isEmpty(resultSetExtractorRef)
&& !isUnconfigured(resultSetExtractorClass, ResultSetExtractor.class)) {
if (!ObjectUtils.isEmpty(resultSetExtractorRef) && !isUnconfigured(resultSetExtractorClass,
ResultSetExtractor.class)) {
throw new IllegalArgumentException(
"Invalid ResultSetExtractor configuration. Configure either one but not both via @Query(resultSetExtractorRef = …, resultSetExtractorClass = …) for query method "
+ getQueryMethod());
}

this.configuredResultSetExtractor = !ObjectUtils.isEmpty(resultSetExtractorRef)
|| !isUnconfigured(resultSetExtractorClass, ResultSetExtractor.class);
this.configuredResultSetExtractor =
!ObjectUtils.isEmpty(resultSetExtractorRef) || !isUnconfigured(resultSetExtractorClass,
ResultSetExtractor.class);

this.rowMapperConstructor = resultSetExtractorClass != null
? ClassUtils.getConstructorIfAvailable(resultSetExtractorClass, RowMapper.class)
: null;
this.rowMapperConstructor = resultSetExtractorClass != null ?
ClassUtils.getConstructorIfAvailable(resultSetExtractorClass, RowMapper.class) :
null;
this.constructor = resultSetExtractorClass != null ? findPrimaryConstructor(resultSetExtractorClass) : null;
this.resultSetExtractorFactory = rowMapper -> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repository
*/
JdbcQueryMethod getJdbcQueryMethod(Method method, RepositoryMetadata repositoryMetadata,
ProjectionFactory projectionFactory, NamedQueries namedQueries) {
return new JdbcQueryMethod(method, repositoryMetadata, projectionFactory, namedQueries, getMappingContext());
return new JdbcQueryMethod(method, repositoryMetadata, projectionFactory, namedQueries, getMappingContext(), getDialect().getSqlTypeResolver());
}

/**
Expand Down
Loading
Loading