Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c97664d

Browse files
committed
GH-2020 Added SqlTypeResolver abstraction
Signed-off-by: mipo256 <mikhailpolivakha@gmail.com>
1 parent 38f2af0 commit c97664d

File tree

20 files changed

+647
-91
lines changed

20 files changed

+647
-91
lines changed

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DefaultJdbcTypeFactory.java

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,25 +44,6 @@ public DefaultJdbcTypeFactory(JdbcOperations operations) {
4444
this(operations, org.springframework.data.jdbc.core.dialect.JdbcArrayColumns.DefaultSupport.INSTANCE);
4545
}
4646

47-
/**
48-
* Creates a new {@link DefaultJdbcTypeFactory}.
49-
*
50-
* @param operations must not be {@literal null}.
51-
* @since 2.3
52-
* @deprecated use
53-
* {@link #DefaultJdbcTypeFactory(JdbcOperations, org.springframework.data.jdbc.core.dialect.JdbcArrayColumns)}
54-
* instead.
55-
*/
56-
@Deprecated(forRemoval = true, since = "3.5")
57-
public DefaultJdbcTypeFactory(JdbcOperations operations, JdbcArrayColumns arrayColumns) {
58-
59-
Assert.notNull(operations, "JdbcOperations must not be null");
60-
Assert.notNull(arrayColumns, "JdbcArrayColumns must not be null");
61-
62-
this.operations = operations;
63-
this.arrayColumns = arrayColumns;
64-
}
65-
6647
/**
6748
* Creates a new {@link DefaultJdbcTypeFactory}.
6849
*

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/DelegatingDataAccessStrategy.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ public class DelegatingDataAccessStrategy implements DataAccessStrategy {
4545

4646
private DataAccessStrategy delegate;
4747

48+
/**
49+
* @deprecated please, use {@link DelegatingDataAccessStrategy#DelegatingDataAccessStrategy(DataAccessStrategy)} instead
50+
*/
51+
@Deprecated(forRemoval = true, since = "4.0")
4852
public DelegatingDataAccessStrategy() {}
4953

5054
public DelegatingDataAccessStrategy(DataAccessStrategy delegate) {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.data.jdbc.core.dialect;
18+
19+
import java.sql.SQLType;
20+
21+
import org.springframework.data.relational.repository.query.RelationalParameters;
22+
import org.springframework.lang.Nullable;
23+
import org.springframework.util.Assert;
24+
25+
/**
26+
* Default implementation of {@link SqlTypeResolver}. Capable to resolve the {@link SqlType} annotation
27+
* on the {@link java.lang.annotation.ElementType#PARAMETER method parameters}, like this:
28+
* <p>
29+
* <pre class="code">
30+
* List<User> findByAge(&#64;SqlType(name = "TINYINT", vendorTypeNumber = Types.TINYINT) byte age);
31+
* </pre>
32+
*
33+
* Qualification of the actual {@link SQLType} (the sql type of the component), then the following needs to be done:
34+
* <pre class="code">
35+
* List<User> findByAgeIn(&#64;SqlType(name = "TINYINT", vendorTypeNumber = Types.TINYINT) Integer[] age);
36+
* </pre>
37+
*
38+
* @author Mikhail Polivakha
39+
*/
40+
public class DefaultSqlTypeResolver implements SqlTypeResolver {
41+
42+
public static DefaultSqlTypeResolver INSTANCE = new DefaultSqlTypeResolver();
43+
44+
@Override
45+
@Nullable
46+
public SQLType resolveSqlType(RelationalParameters.RelationalParameter relationalParameter) {
47+
return resolveInternally(relationalParameter);
48+
}
49+
50+
@Override
51+
@Nullable
52+
public SQLType resolveActualSqlType(RelationalParameters.RelationalParameter relationalParameter) {
53+
return resolveInternally(relationalParameter);
54+
}
55+
56+
private static AnnotationBasedSqlType resolveInternally(
57+
RelationalParameters.RelationalParameter relationalParameter) {
58+
SqlType parameterAnnotation = relationalParameter.getMethodParameter().getParameterAnnotation(SqlType.class);
59+
60+
if (parameterAnnotation != null) {
61+
return new AnnotationBasedSqlType(parameterAnnotation);
62+
} else {
63+
return null;
64+
}
65+
}
66+
67+
/**
68+
* {@link SQLType} determined from the {@link SqlType} annotation.
69+
*
70+
* @author Mikhail Polivakha
71+
*/
72+
protected static class AnnotationBasedSqlType implements SQLType {
73+
74+
private final SqlType sqlType;
75+
76+
public AnnotationBasedSqlType(SqlType sqlType) {
77+
Assert.notNull(sqlType, "sqlType must not be null");
78+
79+
this.sqlType = sqlType;
80+
}
81+
82+
@Override
83+
public String getName() {
84+
return sqlType.name();
85+
}
86+
87+
@Override
88+
public String getVendor() {
89+
return "Spring Data JDBC";
90+
}
91+
92+
@Override
93+
public Integer getVendorTypeNumber() {
94+
return sqlType.vendorTypeNumber();
95+
}
96+
}
97+
}

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/dialect/JdbcDialect.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
package org.springframework.data.jdbc.core.dialect;
1717

1818
import org.springframework.data.relational.core.dialect.Dialect;
19+
import org.springframework.data.jdbc.core.dialect.SqlTypeResolver;
20+
import org.springframework.data.jdbc.core.dialect.DefaultSqlTypeResolver;
1921

2022
/**
2123
* {@link org.springframework.data.relational.core.dialect.ArrayColumns} that offer JDBC specific functionality.
@@ -37,4 +39,12 @@ default JdbcArrayColumns getArraySupport() {
3739
return JdbcArrayColumns.Unsupported.INSTANCE;
3840
}
3941

42+
/**
43+
* Returns a {@link SqlTypeResolver} of this dialect.
44+
*
45+
* @since 4.0
46+
*/
47+
default SqlTypeResolver getSqlTypeResolver() {
48+
return DefaultSqlTypeResolver.INSTANCE;
49+
}
4050
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.data.jdbc.core.dialect;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
import java.sql.SQLType;
25+
26+
/**
27+
* Serves as a hint to the {@link DefaultSqlTypeResolver}, that signals the {@link java.sql.SQLType} to be used.
28+
* The arguments of this annotation are identical to the methods on {@link java.sql.SQLType} interface, expect for
29+
* the {@link SQLType#getVendor()}, which is absent, because it typically does not matter as such for the underlying
30+
* JDBC drivers. The examples of usage, can be found in javadoc of {@link DefaultSqlTypeResolver}.
31+
*
32+
* @see DefaultSqlTypeResolver
33+
* @author Mikhail Polivakha
34+
*/
35+
@Documented
36+
@Target({ElementType.PARAMETER})
37+
@Retention(RetentionPolicy.RUNTIME)
38+
public @interface SqlType {
39+
40+
/**
41+
* Returns the {@code SQLType} name that represents a SQL data type.
42+
*
43+
* @return The name of this {@code SQLType}.
44+
*/
45+
String name();
46+
47+
/**
48+
* Returns the vendor specific type number for the data type.
49+
*
50+
* @return An Integer representing the vendor specific data type
51+
*/
52+
int vendorTypeNumber();
53+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.data.jdbc.core.dialect;
18+
19+
import java.sql.SQLType;
20+
21+
import org.springframework.data.relational.repository.query.RelationalParameters.RelationalParameter;
22+
import org.springframework.data.util.TypeInformation;
23+
import org.springframework.lang.Nullable;
24+
25+
/**
26+
* Common interface for all objects capable to resolve the {@link SQLType} to be used for a give method parameter.
27+
*
28+
* @author Mikhail Polivakha
29+
*/
30+
public interface SqlTypeResolver {
31+
32+
/**
33+
* Resolving the {@link SQLType} from the given {@link RelationalParameter}.
34+
*
35+
* @param relationalParameter the parameter of the query method
36+
* @return {@code null} in case the given {@link SqlTypeResolver} cannot or do not want to determine the
37+
* {@link SQLType} of the given parameter
38+
*/
39+
@Nullable
40+
SQLType resolveSqlType(RelationalParameter relationalParameter);
41+
42+
/**
43+
* Resolving the {@link SQLType} from the given {@link RelationalParameter}. The definition of "actual"
44+
* type can be looked up in the {@link TypeInformation#getActualType()}.
45+
*
46+
* @param relationalParameter the parameter of the query method
47+
* @return {@code null} in case the given {@link SqlTypeResolver} cannot or do not want to determine the
48+
* actual {@link SQLType} of the given parameter
49+
*/
50+
@Nullable
51+
SQLType resolveActualSqlType(RelationalParameter relationalParameter);
52+
}

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/JdbcParameters.java

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,47 @@
2121
import org.springframework.core.MethodParameter;
2222
import org.springframework.data.jdbc.core.convert.JdbcColumnTypes;
2323
import org.springframework.data.jdbc.support.JdbcUtil;
24+
import org.springframework.data.jdbc.core.dialect.DefaultSqlTypeResolver;
25+
import org.springframework.data.jdbc.core.dialect.SqlTypeResolver;
2426
import org.springframework.data.relational.repository.query.RelationalParameters;
2527
import org.springframework.data.repository.query.Parameter;
2628
import org.springframework.data.repository.query.ParametersSource;
2729
import org.springframework.data.util.Lazy;
2830
import org.springframework.data.util.TypeInformation;
31+
import org.springframework.util.Assert;
2932

3033
/**
3134
* Custom extension of {@link RelationalParameters}.
3235
*
3336
* @author Mark Paluch
37+
* @author Mikhail Polivakha
3438
* @since 3.2.6
3539
*/
3640
public class JdbcParameters extends RelationalParameters {
3741

3842
/**
39-
* Creates a new {@link JdbcParameters} instance from the given {@link ParametersSource}.
43+
* Creates a new {@link JdbcParameters} instance from the given {@link ParametersSource}. Uses the {@link DefaultSqlTypeResolver}.
4044
*
4145
* @param parametersSource must not be {@literal null}.
4246
*/
4347
public JdbcParameters(ParametersSource parametersSource) {
4448
super(parametersSource,
45-
methodParameter -> new JdbcParameter(methodParameter, parametersSource.getDomainTypeInformation()));
49+
methodParameter -> new JdbcParameter(methodParameter, parametersSource.getDomainTypeInformation(),
50+
DefaultSqlTypeResolver.INSTANCE));
51+
}
52+
53+
/**
54+
* Creates a new {@link JdbcParameters} instance from the given {@link ParametersSource} and given {@link SqlTypeResolver}.
55+
*
56+
* @param parametersSource must not be {@literal null}.
57+
* @param sqlTypeResolver must not be {@literal null}.
58+
*/
59+
public JdbcParameters(ParametersSource parametersSource, SqlTypeResolver sqlTypeResolver) {
60+
super(parametersSource,
61+
methodParameter -> new JdbcParameter(methodParameter, parametersSource.getDomainTypeInformation(), sqlTypeResolver));
62+
63+
Assert.notNull(sqlTypeResolver, "SqlTypeResolver must not be null");
64+
Assert.notNull(parametersSource, "ParametersSource must not be null");
4665
}
4766

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

72-
private final SQLType sqlType;
91+
private final Lazy<SQLType> sqlType;
7392
private final Lazy<SQLType> actualSqlType;
7493

7594
/**
7695
* Creates a new {@link RelationalParameter}.
7796
*
7897
* @param parameter must not be {@literal null}.
7998
*/
80-
JdbcParameter(MethodParameter parameter, TypeInformation<?> domainType) {
99+
JdbcParameter(MethodParameter parameter, TypeInformation<?> domainType, SqlTypeResolversqlTypeResolver) {
81100
super(parameter, domainType);
82101

83102
TypeInformation<?> typeInformation = getTypeInformation();
84103

85-
sqlType = JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getType()));
104+
sqlType = Lazy.of(() -> {
105+
SQLType resolvedSqlType = sqlTypeResolver.resolveSqlType(this);
106+
107+
if (resolvedSqlType == null) {
108+
return JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getType()));
109+
} else {
110+
return resolvedSqlType;
111+
}
112+
});
113+
114+
actualSqlType = Lazy.of(() -> {
115+
SQLType resolvedActualSqlType = sqlTypeResolver.resolveActualSqlType(this);
86116

87-
actualSqlType = Lazy.of(() -> JdbcUtil
88-
.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getActualType().getType())));
117+
if (resolvedActualSqlType == null) {
118+
return JdbcUtil.targetSqlTypeFor(JdbcColumnTypes.INSTANCE.resolvePrimitiveType(typeInformation.getActualType().getType()));
119+
} else {
120+
return resolvedActualSqlType;
121+
}
122+
});
89123
}
90124

91125
public SQLType getSqlType() {
92-
return sqlType;
126+
return sqlType.get();
93127
}
94128

95129
public SQLType getActualSqlType() {

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /