diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java
index 040367b06a..2b722be9a9 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java
@@ -44,25 +44,6 @@ public DefaultJdbcTypeFactory(JdbcOperations operations) {
this(operations, org.springframework.data.jdbc.core.dialect.JdbcArrayColumns.DefaultSupport.INSTANCE);
}
- /**
- * Creates a new {@link DefaultJdbcTypeFactory}.
- *
- * @param operations must not be {@literal null}.
- * @since 2.3
- * @deprecated use
- * {@link #DefaultJdbcTypeFactory(JdbcOperations, org.springframework.data.jdbc.core.dialect.JdbcArrayColumns)}
- * instead.
- */
- @Deprecated(forRemoval = true, since = "3.5")
- public DefaultJdbcTypeFactory(JdbcOperations operations, JdbcArrayColumns arrayColumns) {
-
- Assert.notNull(operations, "JdbcOperations must not be null");
- Assert.notNull(arrayColumns, "JdbcArrayColumns must not be null");
-
- this.operations = operations;
- this.arrayColumns = arrayColumns;
- }
-
/**
* Creates a new {@link DefaultJdbcTypeFactory}.
*
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java
index 1bec8222f0..11653e7716 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java
@@ -45,6 +45,10 @@ public class DelegatingDataAccessStrategy implements DataAccessStrategy {
private DataAccessStrategy delegate;
+ /**
+ * @deprecated please, use {@link DelegatingDataAccessStrategy#DelegatingDataAccessStrategy(DataAccessStrategy)} instead
+ */
+ @Deprecated(forRemoval = true, since = "4.0")
public DelegatingDataAccessStrategy() {}
public DelegatingDataAccessStrategy(DataAccessStrategy delegate) {
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DefaultSqlTypeResolver.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DefaultSqlTypeResolver.java
new file mode 100644
index 0000000000..f36240f51f
--- /dev/null
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/DefaultSqlTypeResolver.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 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.core.dialect;
+
+import java.sql.SQLType;
+
+import org.springframework.data.relational.repository.query.RelationalParameters;
+import org.springframework.lang.Nullable;
+import org.springframework.util.Assert;
+
+/**
+ * Default implementation of {@link SqlTypeResolver}. Capable to resolve the {@link SqlType} annotation
+ * on the {@link java.lang.annotation.ElementType#PARAMETER method parameters}, like this:
+ *
+ *
+ * @author Mikhail Polivakha
+ */
+public class DefaultSqlTypeResolver implements SqlTypeResolver {
+
+ public static DefaultSqlTypeResolver INSTANCE = new DefaultSqlTypeResolver();
+
+ @Override
+ @Nullable
+ public SQLType resolveSqlType(RelationalParameters.RelationalParameter relationalParameter) {
+ return resolveInternally(relationalParameter);
+ }
+
+ @Override
+ @Nullable
+ public SQLType resolveActualSqlType(RelationalParameters.RelationalParameter relationalParameter) {
+ return resolveInternally(relationalParameter);
+ }
+
+ private static AnnotationBasedSqlType resolveInternally(
+ RelationalParameters.RelationalParameter relationalParameter) {
+ SqlType parameterAnnotation = relationalParameter.getMethodParameter().getParameterAnnotation(SqlType.class);
+
+ if (parameterAnnotation != null) {
+ return new AnnotationBasedSqlType(parameterAnnotation);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@link SQLType} determined from the {@link SqlType} annotation.
+ *
+ * @author Mikhail Polivakha
+ */
+ protected static class AnnotationBasedSqlType implements SQLType {
+
+ private final SqlType sqlType;
+
+ public AnnotationBasedSqlType(SqlType sqlType) {
+ Assert.notNull(sqlType, "sqlType must not be null");
+
+ this.sqlType = sqlType;
+ }
+
+ @Override
+ public String getName() {
+ return sqlType.name();
+ }
+
+ @Override
+ public String getVendor() {
+ return "Spring Data JDBC";
+ }
+
+ @Override
+ public Integer getVendorTypeNumber() {
+ return sqlType.vendorTypeNumber();
+ }
+ }
+}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDialect.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDialect.java
index 5728ce4f56..b494d224f9 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDialect.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDialect.java
@@ -16,6 +16,8 @@
package org.springframework.data.jdbc.core.dialect;
import org.springframework.data.relational.core.dialect.Dialect;
+import org.springframework.data.jdbc.core.dialect.SqlTypeResolver;
+import org.springframework.data.jdbc.core.dialect.DefaultSqlTypeResolver;
/**
* {@link org.springframework.data.relational.core.dialect.ArrayColumns} that offer JDBC specific functionality.
@@ -37,4 +39,12 @@ default JdbcArrayColumns getArraySupport() {
return JdbcArrayColumns.Unsupported.INSTANCE;
}
+ /**
+ * Returns a {@link SqlTypeResolver} of this dialect.
+ *
+ * @since 4.0
+ */
+ default SqlTypeResolver getSqlTypeResolver() {
+ return DefaultSqlTypeResolver.INSTANCE;
+ }
}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/SqlType.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/SqlType.java
new file mode 100644
index 0000000000..25d267c49a
--- /dev/null
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/SqlType.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 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.core.dialect;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.sql.SQLType;
+
+/**
+ * Serves as a hint to the {@link DefaultSqlTypeResolver}, that signals the {@link java.sql.SQLType} to be used.
+ * The arguments of this annotation are identical to the methods on {@link java.sql.SQLType} interface, expect for
+ * the {@link SQLType#getVendor()}, which is absent, because it typically does not matter as such for the underlying
+ * JDBC drivers. The examples of usage, can be found in javadoc of {@link DefaultSqlTypeResolver}.
+ *
+ * @see DefaultSqlTypeResolver
+ * @author Mikhail Polivakha
+ */
+@Documented
+@Target({ElementType.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SqlType {
+
+ /**
+ * Returns the {@code SQLType} name that represents a SQL data type.
+ *
+ * @return The name of this {@code SQLType}.
+ */
+ String name();
+
+ /**
+ * Returns the vendor specific type number for the data type.
+ *
+ * @return An Integer representing the vendor specific data type
+ */
+ int vendorTypeNumber();
+}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/SqlTypeResolver.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/SqlTypeResolver.java
new file mode 100644
index 0000000000..bd845b1564
--- /dev/null
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/SqlTypeResolver.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 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.core.dialect;
+
+import java.sql.SQLType;
+
+import org.springframework.data.relational.repository.query.RelationalParameters.RelationalParameter;
+import org.springframework.data.util.TypeInformation;
+import org.springframework.lang.Nullable;
+
+/**
+ * Common interface for all objects capable to resolve the {@link SQLType} to be used for a give method parameter.
+ *
+ * @author Mikhail Polivakha
+ */
+public interface SqlTypeResolver {
+
+ /**
+ * Resolving the {@link SQLType} from the given {@link RelationalParameter}.
+ *
+ * @param relationalParameter the parameter of the query method
+ * @return {@code null} in case the given {@link SqlTypeResolver} cannot or do not want to determine the
+ * {@link SQLType} of the given parameter
+ */
+ @Nullable
+ SQLType resolveSqlType(RelationalParameter relationalParameter);
+
+ /**
+ * Resolving the {@link SQLType} from the given {@link RelationalParameter}. The definition of "actual"
+ * type can be looked up in the {@link TypeInformation#getActualType()}.
+ *
+ * @param relationalParameter the parameter of the query method
+ * @return {@code null} in case the given {@link SqlTypeResolver} cannot or do not want to determine the
+ * actual {@link SQLType} of the given parameter
+ */
+ @Nullable
+ SQLType resolveActualSqlType(RelationalParameter relationalParameter);
+}
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcParameters.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcParameters.java
index ddfb1e7431..1e9c5752b0 100755
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcParameters.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcParameters.java
@@ -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.jdbc.core.dialect.DefaultSqlTypeResolver;
+import org.springframework.data.jdbc.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(),
+ 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, 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" })
@@ -69,7 +88,7 @@ protected JdbcParameters createFrom(List parameters) {
*/
public static class JdbcParameter extends RelationalParameter {
- private final SQLType sqlType;
+ private final Lazy sqlType;
private final Lazy actualSqlType;
/**
@@ -77,19 +96,34 @@ public static class JdbcParameter extends RelationalParameter {
*
* @param parameter must not be {@literal null}.
*/
- JdbcParameter(MethodParameter parameter, TypeInformation> domainType) {
+ JdbcParameter(MethodParameter parameter, TypeInformation> domainType, SqlTypeResolver sqlTypeResolver) {
super(parameter, domainType);
TypeInformation> typeInformation = getTypeInformation();
- sqlType = JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getType()));
+ sqlType = Lazy.of(() -> {
+ SQLType resolvedSqlType = sqlTypeResolver.resolveSqlType(this);
+
+ if (resolvedSqlType == null) {
+ return JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getType()));
+ } else {
+ return resolvedSqlType;
+ }
+ });
+
+ actualSqlType = Lazy.of(() -> {
+ SQLType resolvedActualSqlType = sqlTypeResolver.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() {
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryMethod.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryMethod.java
index 7fc7c114c6..3a8dcf5001 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryMethod.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcQueryMethod.java
@@ -24,6 +24,8 @@
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.projection.ProjectionFactory;
+import org.springframework.data.jdbc.core.dialect.DefaultSqlTypeResolver;
+import org.springframework.data.jdbc.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;
@@ -34,6 +36,7 @@
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;
@@ -53,6 +56,7 @@
* @author Diego Krupitza
* @author Mark Paluch
* @author Daeho Kwon
+ * @author Mikhail Polivakha
*/
public class JdbcQueryMethod extends QueryMethod {
@@ -67,8 +71,15 @@ public class JdbcQueryMethod extends QueryMethod {
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, JdbcParameters::new);
+ super(method, metadata, factory, parametersSource -> new JdbcParameters(parametersSource, sqlTypeResolver));
this.namedQueries = namedQueries;
this.method = method;
this.mappingContext = mappingContext;
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 814daac7d0..cdcb09fe0b 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
@@ -86,13 +86,13 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
/**
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
- * and {@link org.springframework.data.jdbc.repository.query.RowMapperFactory}.
+ * 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,
@@ -105,12 +105,12 @@ public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOpera
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
* 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}.
- * @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,
@@ -378,7 +378,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 {
@@ -437,19 +438,20 @@ public CachedResultSetExtractorFactory(Supplier> 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 -> {
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java
index 0d9099bd2f..5ebc122a38 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java
@@ -23,6 +23,7 @@
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration;
+import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.jdbc.repository.query.DefaultRowMapperFactory;
import org.springframework.data.jdbc.repository.query.JdbcQueryMethod;
import org.springframework.data.jdbc.repository.query.PartTreeJdbcQuery;
@@ -69,7 +70,7 @@ abstract class JdbcQueryLookupStrategy extends RelationalQueryLookupStrategy {
protected final ValueExpressionDelegate delegate;
JdbcQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks,
- RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
+ RelationalMappingContext context, JdbcConverter converter, JdbcDialect dialect,
QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
ValueExpressionDelegate delegate) {
@@ -105,7 +106,7 @@ static class CreateQueryLookupStrategy extends JdbcQueryLookupStrategy {
private final RowMapperFactory rowMapperFactory;
CreateQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks,
- RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
+ RelationalMappingContext context, JdbcConverter converter, JdbcDialect dialect,
QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
ValueExpressionDelegate delegate) {
@@ -138,7 +139,7 @@ static class DeclaredQueryLookupStrategy extends JdbcQueryLookupStrategy {
private final RowMapperFactory rowMapperFactory;
DeclaredQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks,
- RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
+ RelationalMappingContext context, JdbcConverter converter, JdbcDialect dialect,
QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
@Nullable BeanFactory beanfactory, ValueExpressionDelegate delegate) {
super(publisher, callbacks, context, converter, dialect, queryMappingConfiguration, operations, delegate);
@@ -191,7 +192,7 @@ static class CreateIfNotFoundQueryLookupStrategy extends JdbcQueryLookupStrategy
* @param lookupStrategy must not be {@literal null}.
*/
CreateIfNotFoundQueryLookupStrategy(ApplicationEventPublisher publisher, @Nullable EntityCallbacks callbacks,
- RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
+ RelationalMappingContext context, JdbcConverter converter, JdbcDialect dialect,
QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
CreateQueryLookupStrategy createStrategy, DeclaredQueryLookupStrategy lookupStrategy,
ValueExpressionDelegate delegate) {
@@ -222,7 +223,12 @@ 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());
+ }
+
+ @Override
+ public JdbcDialect getDialect() {
+ return (JdbcDialect) super.getDialect();
}
/**
@@ -238,7 +244,9 @@ JdbcQueryMethod getJdbcQueryMethod(Method method, RepositoryMetadata repositoryM
* @param queryMappingConfiguration must not be {@literal null}
* @param operations must not be {@literal null}
* @param beanFactory may be {@literal null}
+ * @deprecated please, use {@link #create(Key, ApplicationEventPublisher, EntityCallbacks, RelationalMappingContext, JdbcConverter, JdbcDialect, QueryMappingConfiguration, NamedParameterJdbcOperations, BeanFactory, ValueExpressionDelegate)}
*/
+ @Deprecated(forRemoval = true, since = "4.0")
public static QueryLookupStrategy create(@Nullable Key key, ApplicationEventPublisher publisher,
@Nullable EntityCallbacks callbacks, RelationalMappingContext context, JdbcConverter converter, Dialect dialect,
QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
@@ -250,6 +258,52 @@ public static QueryLookupStrategy create(@Nullable Key key, ApplicationEventPubl
Assert.notNull(queryMappingConfiguration, "QueryMappingConfiguration must not be null");
Assert.notNull(operations, "NamedParameterJdbcOperations must not be null");
Assert.notNull(delegate, "ValueExpressionDelegate must not be null");
+ Assert.state(dialect instanceof JdbcDialect, "Dialect must be an instance of the JdbcDialect");
+
+ CreateQueryLookupStrategy createQueryLookupStrategy = new CreateQueryLookupStrategy(publisher, callbacks, context,
+ converter, (JdbcDialect) dialect, queryMappingConfiguration, operations, delegate);
+
+ DeclaredQueryLookupStrategy declaredQueryLookupStrategy = new DeclaredQueryLookupStrategy(publisher, callbacks,
+ context, converter, (JdbcDialect) dialect, queryMappingConfiguration, operations, beanFactory, delegate);
+
+ Key keyToUse = key != null ? key : Key.CREATE_IF_NOT_FOUND;
+
+ LOG.debug(String.format("Using the queryLookupStrategy %s", keyToUse));
+
+ return switch (keyToUse) {
+ case CREATE -> createQueryLookupStrategy;
+ case USE_DECLARED_QUERY -> declaredQueryLookupStrategy;
+ case CREATE_IF_NOT_FOUND ->
+ new CreateIfNotFoundQueryLookupStrategy(publisher, callbacks, context, converter, (JdbcDialect) dialect,
+ queryMappingConfiguration, operations, createQueryLookupStrategy, declaredQueryLookupStrategy, delegate);
+ };
+ }
+
+ /**
+ * Creates a {@link QueryLookupStrategy} based on the provided
+ * {@link org.springframework.data.repository.query.QueryLookupStrategy.Key}.
+ *
+ * @param key the key that decides what {@link QueryLookupStrategy} shozld be used.
+ * @param publisher must not be {@literal null}
+ * @param callbacks may be {@literal null}
+ * @param context must not be {@literal null}
+ * @param converter must not be {@literal null}
+ * @param dialect must not be {@literal null}
+ * @param queryMappingConfiguration must not be {@literal null}
+ * @param operations must not be {@literal null}
+ * @param beanFactory may be {@literal null}
+ */
+ public static QueryLookupStrategy create(@Nullable Key key, ApplicationEventPublisher publisher,
+ @Nullable EntityCallbacks callbacks, RelationalMappingContext context, JdbcConverter converter, JdbcDialect dialect,
+ QueryMappingConfiguration queryMappingConfiguration, NamedParameterJdbcOperations operations,
+ @Nullable BeanFactory beanFactory, ValueExpressionDelegate delegate) {
+ Assert.notNull(publisher, "ApplicationEventPublisher must not be null");
+ Assert.notNull(context, "RelationalMappingContextPublisher must not be null");
+ Assert.notNull(converter, "JdbcConverter must not be null");
+ Assert.notNull(dialect, "Dialect must not be null");
+ Assert.notNull(queryMappingConfiguration, "QueryMappingConfiguration must not be null");
+ Assert.notNull(operations, "NamedParameterJdbcOperations must not be null");
+ Assert.notNull(delegate, "ValueExpressionDelegate must not be null");
CreateQueryLookupStrategy createQueryLookupStrategy = new CreateQueryLookupStrategy(publisher, callbacks, context,
converter, dialect, queryMappingConfiguration, operations, delegate);
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java
index c9b54ac8b7..8eeda490da 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactory.java
@@ -23,6 +23,7 @@
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
import org.springframework.data.jdbc.core.convert.JdbcConverter;
import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration;
+import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
@@ -58,7 +59,7 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
private final ApplicationEventPublisher publisher;
private final DataAccessStrategy accessStrategy;
private final NamedParameterJdbcOperations operations;
- private final Dialect dialect;
+ private final JdbcDialect dialect;
private @Nullable BeanFactory beanFactory;
private QueryMappingConfiguration queryMappingConfiguration = QueryMappingConfiguration.EMPTY;
@@ -75,10 +76,41 @@ public class JdbcRepositoryFactory extends RepositoryFactorySupport {
* @param publisher must not be {@literal null}.
* @param operations must not be {@literal null}.
*/
+ @Deprecated(forRemoval = true, since = "4.0")
public JdbcRepositoryFactory(DataAccessStrategy dataAccessStrategy, RelationalMappingContext context,
JdbcConverter converter, Dialect dialect, ApplicationEventPublisher publisher,
NamedParameterJdbcOperations operations) {
+ Assert.notNull(dataAccessStrategy, "DataAccessStrategy must not be null");
+ Assert.notNull(context, "RelationalMappingContext must not be null");
+ Assert.notNull(converter, "RelationalConverter must not be null");
+ Assert.notNull(dialect, "Dialect must not be null");
+ Assert.notNull(publisher, "ApplicationEventPublisher must not be null");
+ Assert.state(dialect instanceof JdbcDialect, "Dialect must be an instance of the JdbcDialect");
+
+ this.publisher = publisher;
+ this.context = context;
+ this.converter = converter;
+ this.dialect = (JdbcDialect) dialect;
+ this.accessStrategy = dataAccessStrategy;
+ this.operations = operations;
+ }
+
+ /**
+ * Creates a new {@link JdbcRepositoryFactory} for the given {@link DataAccessStrategy},
+ * {@link RelationalMappingContext} and {@link ApplicationEventPublisher}.
+ *
+ * @param dataAccessStrategy must not be {@literal null}.
+ * @param context must not be {@literal null}.
+ * @param converter must not be {@literal null}.
+ * @param dialect must not be {@literal null}.
+ * @param publisher must not be {@literal null}.
+ * @param operations must not be {@literal null}.
+ */
+ public JdbcRepositoryFactory(DataAccessStrategy dataAccessStrategy, RelationalMappingContext context,
+ JdbcConverter converter, JdbcDialect dialect, ApplicationEventPublisher publisher,
+ NamedParameterJdbcOperations operations) {
+
Assert.notNull(dataAccessStrategy, "DataAccessStrategy must not be null");
Assert.notNull(context, "RelationalMappingContext must not be null");
Assert.notNull(converter, "RelationalConverter must not be null");
diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java
index 0d56c6b159..4600e567fb 100644
--- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java
+++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcRepositoryFactoryBean.java
@@ -20,6 +20,8 @@
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.FactoryBean;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.data.jdbc.core.convert.DataAccessStrategy;
@@ -29,6 +31,7 @@
import org.springframework.data.jdbc.core.convert.QueryMappingConfiguration;
import org.springframework.data.jdbc.core.convert.SqlGeneratorSource;
import org.springframework.data.jdbc.core.convert.SqlParametersFactory;
+import org.springframework.data.jdbc.core.dialect.JdbcDialect;
import org.springframework.data.mapping.callback.EntityCallbacks;
import org.springframework.data.relational.core.dialect.Dialect;
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
@@ -39,7 +42,7 @@
import org.springframework.util.Assert;
/**
- * Special adapter for Springs {@link org.springframework.beans.factory.FactoryBean} interface to allow easy setup of
+ * Special adapter for Springs {@link FactoryBean} interface to allow easy setup of
* repository factories via Spring configuration.
*