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 d92a729

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

File tree

12 files changed

+488
-76
lines changed

12 files changed

+488
-76
lines changed

‎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.relational.core.dialect.DefaultSqlTypeResolver;
25+
import org.springframework.data.relational.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+
Lazy.of(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, Lazy<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, Lazy<SqlTypeResolver> sqlTypeResolver) {
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.get().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.get().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() {

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

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,28 @@
1717

1818
import java.lang.annotation.Annotation;
1919
import java.lang.reflect.Method;
20+
import java.util.List;
2021
import java.util.Map;
2122
import java.util.Optional;
2223

2324
import org.springframework.core.annotation.AnnotatedElementUtils;
2425
import org.springframework.core.annotation.AnnotationUtils;
2526
import org.springframework.data.mapping.context.MappingContext;
2627
import org.springframework.data.projection.ProjectionFactory;
28+
import org.springframework.data.relational.core.dialect.DefaultSqlTypeResolver;
29+
import org.springframework.data.relational.core.dialect.SqlTypeResolver;
2730
import org.springframework.data.relational.core.mapping.RelationalPersistentEntity;
2831
import org.springframework.data.relational.core.mapping.RelationalPersistentProperty;
2932
import org.springframework.data.relational.repository.Lock;
3033
import org.springframework.data.relational.repository.query.RelationalEntityMetadata;
3134
import org.springframework.data.relational.repository.query.SimpleRelationalEntityMetadata;
3235
import org.springframework.data.repository.core.NamedQueries;
3336
import org.springframework.data.repository.core.RepositoryMetadata;
37+
import org.springframework.data.repository.query.Parameter;
3438
import org.springframework.data.repository.query.Parameters;
3539
import org.springframework.data.repository.query.ParametersSource;
3640
import org.springframework.data.repository.query.QueryMethod;
41+
import org.springframework.data.util.Lazy;
3742
import org.springframework.jdbc.core.ResultSetExtractor;
3843
import org.springframework.jdbc.core.RowMapper;
3944
import org.springframework.lang.Nullable;
@@ -52,6 +57,7 @@
5257
* @author Hebert Coelho
5358
* @author Diego Krupitza
5459
* @author Mark Paluch
60+
* @author Mikhail Polivakha
5561
*/
5662
public class JdbcQueryMethod extends QueryMethod {
5763

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

71+
private final SqlTypeResolver sqlTypeResolver;
72+
6573
// TODO: Remove NamedQueries and put it into JdbcQueryLookupStrategy
6674
public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
6775
NamedQueries namedQueries,
6876
MappingContext<? extends RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> mappingContext) {
77+
this(method, metadata, factory, namedQueries, mappingContext, DefaultSqlTypeResolver.INSTANCE);
78+
}
79+
80+
public JdbcQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory,
81+
NamedQueries namedQueries,
82+
MappingContext<? extends RelationalPersistentEntity<?>, ? extends RelationalPersistentProperty> mappingContext,
83+
SqlTypeResolver sqlTypeResolver) {
6984

7085
super(method, metadata, factory);
7186
this.namedQueries = namedQueries;
7287
this.method = method;
7388
this.mappingContext = mappingContext;
7489
this.annotationCache = new ConcurrentReferenceHashMap<>();
7590
this.modifyingQuery = AnnotationUtils.findAnnotation(method, Modifying.class) != null;
91+
this.sqlTypeResolver = sqlTypeResolver;
7692
}
7793

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

83100
@Override

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

Lines changed: 1 addition & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.springframework.data.jdbc.core.convert.JdbcConverter;
4040
import org.springframework.data.jdbc.core.mapping.JdbcValue;
4141
import org.springframework.data.jdbc.support.JdbcUtil;
42+
import org.springframework.data.relational.core.dialect.SqlTypeResolver;
4243
import org.springframework.data.relational.core.mapping.RelationalMappingContext;
4344
import org.springframework.data.relational.repository.query.RelationalParameterAccessor;
4445
import org.springframework.data.relational.repository.query.RelationalParametersParameterAccessor;
@@ -91,43 +92,6 @@ public class StringBasedJdbcQuery extends AbstractJdbcQuery {
9192
private final CachedResultSetExtractorFactory cachedResultSetExtractorFactory;
9293
private final ValueExpressionDelegate delegate;
9394

94-
/**
95-
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
96-
* and {@link RowMapper}.
97-
*
98-
* @param queryMethod must not be {@literal null}.
99-
* @param operations must not be {@literal null}.
100-
* @param defaultRowMapper can be {@literal null} (only in case of a modifying query).
101-
* @deprecated since 3.4, use the constructors accepting {@link ValueExpressionDelegate} instead.
102-
*/
103-
@Deprecated(since = "3.4")
104-
public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
105-
@Nullable RowMapper<?> defaultRowMapper, JdbcConverter converter,
106-
QueryMethodEvaluationContextProvider evaluationContextProvider) {
107-
this(queryMethod.getRequiredQuery(), queryMethod, operations, result -> (RowMapper<Object>) defaultRowMapper,
108-
converter, evaluationContextProvider);
109-
}
110-
111-
/**
112-
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
113-
* and {@link RowMapperFactory}.
114-
*
115-
* @param queryMethod must not be {@literal null}.
116-
* @param operations must not be {@literal null}.
117-
* @param rowMapperFactory must not be {@literal null}.
118-
* @param converter must not be {@literal null}.
119-
* @param evaluationContextProvider must not be {@literal null}.
120-
* @since 2.3
121-
* @deprecated use alternative constructor
122-
*/
123-
@Deprecated(since = "3.4")
124-
public StringBasedJdbcQuery(JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
125-
RowMapperFactory rowMapperFactory, JdbcConverter converter,
126-
QueryMethodEvaluationContextProvider evaluationContextProvider) {
127-
this(queryMethod.getRequiredQuery(), queryMethod, operations, rowMapperFactory, converter,
128-
evaluationContextProvider);
129-
}
130-
13195
/**
13296
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
13397
* and {@link RowMapperFactory}.
@@ -197,28 +161,6 @@ public StringBasedJdbcQuery(String query, JdbcQueryMethod queryMethod, NamedPara
197161
this.delegate = delegate;
198162
}
199163

200-
/**
201-
* Creates a new {@link StringBasedJdbcQuery} for the given {@link JdbcQueryMethod}, {@link RelationalMappingContext}
202-
* and {@link RowMapperFactory}.
203-
*
204-
* @param query must not be {@literal null} or empty.
205-
* @param queryMethod must not be {@literal null}.
206-
* @param operations must not be {@literal null}.
207-
* @param rowMapperFactory must not be {@literal null}.
208-
* @param converter must not be {@literal null}.
209-
* @param evaluationContextProvider must not be {@literal null}.
210-
* @since 3.4
211-
* @deprecated since 3.4, use the constructors accepting {@link ValueExpressionDelegate} instead.
212-
*/
213-
@Deprecated(since = "3.4")
214-
public StringBasedJdbcQuery(String query, JdbcQueryMethod queryMethod, NamedParameterJdbcOperations operations,
215-
RowMapperFactory rowMapperFactory, JdbcConverter converter,
216-
QueryMethodEvaluationContextProvider evaluationContextProvider) {
217-
this(query, queryMethod, operations, rowMapperFactory, converter, new CachingValueExpressionDelegate(
218-
new QueryMethodValueEvaluationContextAccessor(new StandardEnvironment(), rootObject -> evaluationContextProvider
219-
.getEvaluationContext(queryMethod.getParameters(), new Object[] { rootObject })),
220-
ValueExpressionParser.create()));
221-
}
222164

223165
@Override
224166
public Object execute(Object[] objects) {

‎spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/support/JdbcQueryLookupStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ public RepositoryQuery resolveQuery(Method method, RepositoryMetadata repository
264264
*/
265265
JdbcQueryMethod getJdbcQueryMethod(Method method, RepositoryMetadata repositoryMetadata,
266266
ProjectionFactory projectionFactory, NamedQueries namedQueries) {
267-
return new JdbcQueryMethod(method, repositoryMetadata, projectionFactory, namedQueries, getMappingContext());
267+
return new JdbcQueryMethod(method, repositoryMetadata, projectionFactory, namedQueries, getMappingContext(), getDialect().getSqlTypeResolver());
268268
}
269269

270270
/**

‎spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/JdbcQueryMethodUnitTests.java

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,23 @@
2020
import static org.mockito.Mockito.*;
2121

2222
import java.lang.reflect.Method;
23+
import java.sql.JDBCType;
2324
import java.sql.ResultSet;
25+
import java.sql.SQLType;
26+
import java.sql.Types;
27+
import java.util.List;
2428
import java.util.Properties;
2529

30+
import org.assertj.core.api.Assertions;
2631
import org.junit.jupiter.api.BeforeEach;
2732
import org.junit.jupiter.api.Test;
2833
import org.springframework.data.jdbc.core.mapping.JdbcMappingContext;
2934
import org.springframework.data.projection.ProjectionFactory;
35+
import org.springframework.data.relational.core.dialect.DefaultSqlTypeResolver;
36+
import org.springframework.data.relational.core.dialect.SqlTypeResolver;
3037
import org.springframework.data.relational.core.sql.LockMode;
3138
import org.springframework.data.relational.repository.Lock;
39+
import org.springframework.data.relational.repository.query.SqlType;
3240
import org.springframework.data.repository.core.NamedQueries;
3341
import org.springframework.data.repository.core.RepositoryMetadata;
3442
import org.springframework.data.repository.core.support.PropertiesBasedNamedQueries;
@@ -43,6 +51,7 @@
4351
* @author Moises Cisneros
4452
* @author Mark Paluch
4553
* @author Diego Krupitza
54+
* @author Mikhail Polivakha
4655
*/
4756
public class JdbcQueryMethodUnitTests {
4857

@@ -66,6 +75,8 @@ public void before() {
6675
namedQueries = new PropertiesBasedNamedQueries(properties);
6776

6877
metadata = mock(RepositoryMetadata.class);
78+
when(metadata.getDomainTypeInformation()).then(invocationOnMock -> TypeInformation.of(Object.class));
79+
6980
doReturn(String.class).when(metadata).getReturnedDomainClass(any(Method.class));
7081
doReturn(TypeInformation.of(String.class)).when(metadata).getReturnType(any(Method.class));
7182
}
@@ -78,6 +89,31 @@ public void returnsSqlStatement() throws NoSuchMethodException {
7889
assertThat(queryMethod.getDeclaredQuery()).isEqualTo(QUERY);
7990
}
8091

92+
@Test // DATAJDBC-165
93+
public void testSqlTypeResolver() throws NoSuchMethodException {
94+
95+
JdbcQueryMethod queryMethod = createJdbcQueryMethod(
96+
"findUserTestMethod",
97+
new DefaultSqlTypeResolver(),
98+
Integer.class, String.class, List.class
99+
);
100+
101+
JdbcParameters parameters = queryMethod.getParameters();
102+
103+
SQLType first = parameters.getParameter(0).getSqlType();
104+
SQLType second = parameters.getParameter(1).getSqlType();
105+
SQLType thirdActual = parameters.getParameter(2).getActualSqlType();
106+
107+
Assertions.assertThat(first.getName()).isEqualTo(JDBCType.TINYINT.getName());
108+
Assertions.assertThat(first.getVendorTypeNumber()).isEqualTo(Types.TINYINT);
109+
110+
Assertions.assertThat(second.getName()).isEqualTo(JDBCType.VARCHAR.getName());
111+
Assertions.assertThat(second.getVendorTypeNumber()).isEqualTo(Types.VARCHAR);
112+
113+
Assertions.assertThat(thirdActual.getName()).isEqualTo(JDBCType.SMALLINT.getName());
114+
Assertions.assertThat(thirdActual.getVendorTypeNumber()).isEqualTo(Types.SMALLINT);
115+
}
116+
81117
@Test // DATAJDBC-165
82118
public void returnsSpecifiedRowMapperClass() throws NoSuchMethodException {
83119

@@ -102,12 +138,6 @@ public void returnsSpecifiedSqlStatementIfNameAndValueAreGiven() throws NoSuchMe
102138

103139
}
104140

105-
private JdbcQueryMethod createJdbcQueryMethod(String methodName) throws NoSuchMethodException {
106-
107-
Method method = JdbcQueryMethodUnitTests.class.getDeclaredMethod(methodName);
108-
return new JdbcQueryMethod(method, metadata, mock(ProjectionFactory.class), namedQueries, mappingContext);
109-
}
110-
111141
@Test // DATAJDBC-234
112142
public void returnsImplicitlyNamedQuery() throws NoSuchMethodException {
113143

@@ -148,10 +178,27 @@ void returnsQueryMethodWithCorrectLockTypeNoLock() throws NoSuchMethodException
148178
assertThat(queryMethodWithWriteLock.lookupLockAnnotation()).isEmpty();
149179
}
150180

181+
private JdbcQueryMethod createJdbcQueryMethod(String methodName) throws NoSuchMethodException {
182+
return createJdbcQueryMethod(methodName, new DefaultSqlTypeResolver());
183+
}
184+
185+
private JdbcQueryMethod createJdbcQueryMethod(String methodName, SqlTypeResolver sqlTypeResolver, Class<?>... args) throws NoSuchMethodException {
186+
187+
Method method = JdbcQueryMethodUnitTests.class.getDeclaredMethod(methodName, args);
188+
return new JdbcQueryMethod(method, metadata, mock(ProjectionFactory.class), namedQueries, mappingContext, sqlTypeResolver);
189+
}
190+
151191
@Lock(LockMode.PESSIMISTIC_WRITE)
152192
@Query
153193
private void queryMethodWithWriteLock() {}
154194

195+
@Query
196+
private void findUserTestMethod(
197+
@SqlType(name = "TINYINT", vendorTypeNumber = Types.TINYINT) Integer age,
198+
String name,
199+
List<@SqlType(name = "SMALLINT", vendorTypeNumber = Types.SMALLINT) Integer> statuses
200+
) {}
201+
155202
@Lock(LockMode.PESSIMISTIC_READ)
156203
@Query
157204
private void queryMethodWithReadLock() {}

0 commit comments

Comments
(0)

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