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 01f10dd

Browse files
mipo256schauder
authored andcommitted
Propagate SqlType from Spel expression evaluation.
Signed-off-by: mipo256 <mikhailpolivakha@gmail.com> Commit message edited by Jens Schauder Original pull request #2079 Closes #2007
1 parent fa03d3e commit 01f10dd

File tree

11 files changed

+227
-6
lines changed

11 files changed

+227
-6
lines changed

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

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.beans.BeanUtils;
3232
import org.springframework.beans.factory.BeanFactory;
3333
import org.springframework.data.expression.ValueEvaluationContext;
34+
import org.springframework.data.expression.ValueExpression;
3435
import org.springframework.data.jdbc.core.convert.JdbcColumnTypes;
3536
import org.springframework.data.jdbc.core.convert.JdbcConverter;
3637
import org.springframework.data.jdbc.core.mapping.JdbcValue;
@@ -176,7 +177,7 @@ private String evaluateExpressions(Object[] objects, Parameters<?, ?> bindablePa
176177
.getEvaluationContext(objects);
177178

178179
parsedQuery.getParameterMap().forEach((paramName, valueExpression) -> {
179-
parameterMap.addValue(paramName, valueExpression.evaluate(evaluationContext));
180+
addEvaluatedParameterToParameterSource(parameterMap, paramName, valueExpression, evaluationContext);
180181
});
181182

182183
return parsedQuery.getQueryString();
@@ -185,6 +186,39 @@ private String evaluateExpressions(Object[] objects, Parameters<?, ?> bindablePa
185186
return this.query;
186187
}
187188

189+
private static void addEvaluatedParameterToParameterSource(
190+
MapSqlParameterSource parameterMap,
191+
String paramName,
192+
ValueExpression valueExpression,
193+
ValueEvaluationContext evaluationContext) {
194+
195+
Object evaluatedValue = valueExpression.evaluate(evaluationContext);
196+
Class<?> valueType = valueExpression.getValueType(evaluationContext);
197+
198+
SQLType sqlType;
199+
200+
if (valueType == null) {
201+
if (evaluatedValue != null) {
202+
sqlType = getSqlType(evaluatedValue.getClass());
203+
} else {
204+
sqlType = null;
205+
}
206+
} else {
207+
sqlType = getSqlType(valueType);
208+
}
209+
210+
if (sqlType != null) {
211+
parameterMap.addValue(paramName, evaluatedValue, sqlType.getVendorTypeNumber());
212+
} else {
213+
parameterMap.addValue(paramName, evaluatedValue);
214+
}
215+
}
216+
217+
private static SQLType getSqlType(Class<?> valueType) {
218+
Class<?> resolvedPrimitiveType = JdbcColumnTypes.INSTANCE.resolvePrimitiveType(valueType);
219+
return JdbcUtil.targetSqlTypeFor(resolvedPrimitiveType);
220+
}
221+
188222
private JdbcQueryExecution<?> createJdbcQueryExecution(RelationalParameterAccessor accessor,
189223
ResultProcessor processor) {
190224

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@
4141
import org.junit.jupiter.api.Test;
4242
import org.junit.jupiter.params.ParameterizedTest;
4343
import org.junit.jupiter.params.provider.Arguments;
44+
import org.junit.jupiter.params.provider.EnumSource;
4445
import org.junit.jupiter.params.provider.MethodSource;
46+
import org.junit.jupiter.params.provider.NullSource;
47+
import org.junit.jupiter.params.provider.ValueSource;
4548
import org.springframework.beans.factory.annotation.Autowired;
4649
import org.springframework.beans.factory.config.PropertiesFactoryBean;
4750
import org.springframework.context.ApplicationListener;
@@ -112,6 +115,7 @@ public class JdbcRepositoryIntegrationTests {
112115
@Autowired RootRepository rootRepository;
113116
@Autowired WithDelimitedColumnRepository withDelimitedColumnRepository;
114117
@Autowired EntityWithSequenceRepository entityWithSequenceRepository;
118+
@Autowired ExpressionSqlTypePropagationRepository expressionSqlTypePropagationRepository;
115119

116120
public static Stream<Arguments> findAllByExamplePageableSource() {
117121

@@ -346,6 +350,20 @@ public void update() {
346350
});
347351
}
348352

353+
@ParameterizedTest
354+
@NullSource
355+
@EnumSource(value = EnumClass.class)
356+
void shouldSaveWithCustomSpellExpressions(EnumClass value) {
357+
expressionSqlTypePropagationRepository.saveWithSpel(new ExpressionSqlTypePropagation(1L, value));
358+
359+
var found = expressionSqlTypePropagationRepository.findById(1L);
360+
361+
assertThat(found).isPresent().hasValueSatisfying(entity -> {
362+
assertThat(entity.getIdentifier()).isEqualTo(1L);
363+
assertThat(entity.getEnumClass()).isEqualTo(value);
364+
});
365+
}
366+
349367
@Test // DATAJDBC-98
350368
public void updateMany() {
351369

@@ -1573,6 +1591,18 @@ interface WithDelimitedColumnRepository extends CrudRepository<WithDelimitedColu
15731591

15741592
interface EntityWithSequenceRepository extends CrudRepository<EntityWithSequence, Long> {}
15751593

1594+
interface ExpressionSqlTypePropagationRepository extends CrudRepository<ExpressionSqlTypePropagation, Long> {
1595+
1596+
// language=sql
1597+
@Modifying
1598+
@Query(value = """
1599+
INSERT INTO EXPRESSION_SQL_TYPE_PROPAGATION(identifier, enum_class)
1600+
VALUES(:#{#expressionSqlTypePropagation.identifier}, :#{#expressionSqlTypePropagation.enumClass})
1601+
""")
1602+
void saveWithSpel(@Param("expressionSqlTypePropagation") ExpressionSqlTypePropagation expressionSqlTypePropagation);
1603+
}
1604+
1605+
15761606
interface DummyProjection {
15771607
String getName();
15781608
}
@@ -1608,6 +1638,11 @@ EntityWithSequenceRepository entityWithSequenceRepository() {
16081638
return factory.getRepository(EntityWithSequenceRepository.class);
16091639
}
16101640

1641+
@Bean
1642+
ExpressionSqlTypePropagationRepository simpleEnumClassRepository() {
1643+
return factory.getRepository(ExpressionSqlTypePropagationRepository.class);
1644+
}
1645+
16111646
@Bean
16121647
NamedQueries namedQueries() throws IOException {
16131648

@@ -1893,6 +1928,32 @@ public Long getId() {
18931928
}
18941929
}
18951930

1931+
static class ExpressionSqlTypePropagation {
1932+
1933+
@Id
1934+
Long identifier;
1935+
1936+
EnumClass enumClass;
1937+
1938+
public ExpressionSqlTypePropagation(Long identifier, EnumClass enumClass) {
1939+
this.identifier = identifier;
1940+
this.enumClass = enumClass;
1941+
}
1942+
1943+
public EnumClass getEnumClass() {
1944+
return enumClass;
1945+
}
1946+
1947+
public Long getIdentifier() {
1948+
return identifier;
1949+
}
1950+
}
1951+
1952+
enum EnumClass {
1953+
ACTIVE,
1954+
DELETE
1955+
}
1956+
18961957
static class EntityWithSequence {
18971958

18981959
@Id

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

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,36 @@
1515
*/
1616
package org.springframework.data.jdbc.repository.query;
1717

18-
import static org.assertj.core.api.Assertions.*;
19-
import static org.mockito.Mockito.*;
18+
import static org.assertj.core.api.Assertions.LIST;
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
21+
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
22+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
23+
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
24+
import static org.mockito.Mockito.any;
25+
import static org.mockito.Mockito.anyString;
26+
import static org.mockito.Mockito.eq;
27+
import static org.mockito.Mockito.mock;
28+
import static org.mockito.Mockito.verify;
2029

2130
import java.lang.reflect.Method;
2231
import java.sql.JDBCType;
2332
import java.sql.ResultSet;
33+
import java.sql.Types;
34+
import java.time.DayOfWeek;
35+
import java.time.Instant;
2436
import java.util.ArrayList;
2537
import java.util.Arrays;
2638
import java.util.Iterator;
2739
import java.util.List;
40+
import java.util.Map;
2841
import java.util.Properties;
2942
import java.util.Set;
3043
import java.util.stream.Stream;
3144

32-
import org.assertj.core.api.Assertions;
3345
import org.junit.jupiter.api.BeforeEach;
3446
import org.junit.jupiter.api.Test;
3547
import org.mockito.ArgumentCaptor;
36-
3748
import org.springframework.core.convert.converter.Converter;
3849
import org.springframework.core.env.StandardEnvironment;
3950
import org.springframework.dao.DataAccessException;
@@ -105,7 +116,7 @@ void emptyQueryThrowsException() {
105116

106117
JdbcQueryMethod queryMethod = createMethod("noAnnotation");
107118

108-
Assertions.assertThatExceptionOfType(IllegalStateException.class) //
119+
assertThatExceptionOfType(IllegalStateException.class) //
109120
.isThrownBy(() -> createQuery(queryMethod).execute(new Object[] {}));
110121
}
111122

@@ -299,6 +310,37 @@ void convertsEnumCollectionParameterIntoStringCollectionParameter() {
299310
assertThat(sqlParameterSource.getValue("directions")).asList().containsExactlyInAnyOrder("LEFT", "RIGHT");
300311
}
301312

313+
@Test // GH-1212
314+
void spelParametersSqlTypesArePropagatedCorrectly() {
315+
316+
String type = "TYPE";
317+
int score = 12;
318+
Instant creationDate = Instant.now();
319+
DayOfWeek dayOfWeek = DayOfWeek.SUNDAY;
320+
ComplexEntity expressionRootObject = new ComplexEntity(type, score, creationDate, dayOfWeek);
321+
322+
SqlParameterSource sqlParameterSource = forMethod("spelContainingQuery", ComplexEntity.class)
323+
.withArguments(expressionRootObject).extractParameterSource();
324+
325+
var expectedSqlTypes = Map.<Object, Integer>of(
326+
type, Types.VARCHAR,
327+
score, Types.INTEGER,
328+
creationDate, Types.TIMESTAMP,
329+
dayOfWeek, Types.VARCHAR
330+
);
331+
332+
assertThat(sqlParameterSource.getParameterNames()).hasSize(5); // 1 root + 4 expressions
333+
assertThat(sqlParameterSource.getParameterNames()).satisfies(parameterNames -> {
334+
for (var paramName : parameterNames) {
335+
if (paramName.equalsIgnoreCase("complexEntity")) {
336+
continue; // do not check root for sqlType
337+
}
338+
Object value = sqlParameterSource.getValue(paramName);
339+
assertThat(sqlParameterSource.getSqlType(paramName)).isEqualTo(expectedSqlTypes.get(value));
340+
}
341+
});
342+
}
343+
302344
@Test // GH-1212
303345
void convertsEnumCollectionParameterUsingCustomConverterWhenRegisteredForType() {
304346

@@ -506,6 +548,15 @@ interface MyRepository extends Repository<Object, Long> {
506548
@Query(value = "some sql statement")
507549
List<Object> findByEnumTypeIn(Set<Direction> directions);
508550

551+
@Query(value = """
552+
SELECT * FROM my_table
553+
WHERE t = :#{#complexEntity.type}
554+
AND s = :#{#complexEntity.score}
555+
AND cd = :#{#complexEntity.creationDate}
556+
AND dow = :#{#complexEntity.dayOfWeek}
557+
""")
558+
List<Object> spelContainingQuery(ComplexEntity complexEntity);
559+
509560
@Query(value = "some sql statement")
510561
List<Object> findBySimpleValue(Integer value);
511562

@@ -652,6 +703,37 @@ public Object getRootObject() {
652703
}
653704
}
654705

706+
static class ComplexEntity {
707+
708+
String type;
709+
Integer score;
710+
Instant creationDate;
711+
DayOfWeek dayOfWeek;
712+
713+
public ComplexEntity(String type, Integer score, Instant creationDate, DayOfWeek dayOfWeek) {
714+
this.type = type;
715+
this.score = score;
716+
this.creationDate = creationDate;
717+
this.dayOfWeek = dayOfWeek;
718+
}
719+
720+
public String getType() {
721+
return type;
722+
}
723+
724+
public Integer getScore() {
725+
return score;
726+
}
727+
728+
public Instant getCreationDate() {
729+
return creationDate;
730+
}
731+
732+
public DayOfWeek getDayOfWeek() {
733+
return dayOfWeek;
734+
}
735+
}
736+
655737
private class StubRowMapperFactory implements RowMapperFactory {
656738

657739
private final String preparedReference;

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-db2.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ DROP TABLE WITH_DELIMITED_COLUMN;
66
DROP TABLE ENTITY_WITH_SEQUENCE;
77
DROP SEQUENCE ENTITY_SEQUENCE;
88
DROP TABLE PROVIDED_ID_ENTITY;
9+
DROP TABLE EXPRESSION_SQL_TYPE_PROPAGATION;
910

1011
CREATE TABLE dummy_entity
1112
(
@@ -63,3 +64,8 @@ CREATE TABLE PROVIDED_ID_ENTITY
6364
ID BIGINT NOT NULL PRIMARY KEY,
6465
NAME VARCHAR(30)
6566
);
67+
68+
CREATE TABLE EXPRESSION_SQL_TYPE_PROPAGATION(
69+
IDENTIFIER BIGINT NOT NULL PRIMARY KEY,
70+
ENUM_CLASS VARCHAR(30)
71+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-h2.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,8 @@ CREATE TABLE PROVIDED_ID_ENTITY
5454
ID BIGINT PRIMARY KEY,
5555
NAME VARCHAR(30)
5656
);
57+
58+
CREATE TABLE EXPRESSION_SQL_TYPE_PROPAGATION(
59+
IDENTIFIER BIGINT PRIMARY KEY,
60+
ENUM_CLASS VARCHAR(30)
61+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,8 @@ CREATE TABLE PROVIDED_ID_ENTITY
5454
ID BIGINT PRIMARY KEY,
5555
NAME VARCHAR(30)
5656
);
57+
58+
CREATE TABLE EXPRESSION_SQL_TYPE_PROPAGATION(
59+
IDENTIFIER BIGINT PRIMARY KEY,
60+
ENUM_CLASS VARCHAR(30)
61+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mariadb.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,8 @@ CREATE TABLE PROVIDED_ID_ENTITY
5454
ID BIGINT PRIMARY KEY,
5555
NAME VARCHAR(30)
5656
);
57+
58+
CREATE TABLE EXPRESSION_SQL_TYPE_PROPAGATION(
59+
IDENTIFIER BIGINT PRIMARY KEY,
60+
ENUM_CLASS VARCHAR(30)
61+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mssql.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ DROP TABLE IF EXISTS WITH_DELIMITED_COLUMN;
66
DROP TABLE IF EXISTS ENTITY_WITH_SEQUENCE;
77
DROP SEQUENCE IF EXISTS ENTITY_SEQUENCE;
88
DROP TABLE IF EXISTS PROVIDED_ID_ENTITY;
9+
DROP TABLE IF EXISTS EXPRESSION_SQL_TYPE_PROPAGATION;
910

1011
CREATE TABLE dummy_entity
1112
(
@@ -63,3 +64,8 @@ CREATE TABLE PROVIDED_ID_ENTITY
6364
ID BIGINT PRIMARY KEY,
6465
NAME VARCHAR(30)
6566
);
67+
68+
CREATE TABLE EXPRESSION_SQL_TYPE_PROPAGATION(
69+
IDENTIFIER BIGINT PRIMARY KEY,
70+
ENUM_CLASS VARCHAR(30)
71+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,8 @@ CREATE TABLE PROVIDED_ID_ENTITY
4949
ID BIGINT PRIMARY KEY,
5050
NAME VARCHAR(30)
5151
);
52+
53+
CREATE TABLE EXPRESSION_SQL_TYPE_PROPAGATION(
54+
IDENTIFIER BIGINT PRIMARY KEY,
55+
ENUM_CLASS VARCHAR(30)
56+
);

‎spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ DROP TABLE WITH_DELIMITED_COLUMN CASCADE CONSTRAINTS PURGE;
66
DROP TABLE ENTITY_WITH_SEQUENCE CASCADE CONSTRAINTS PURGE;
77
DROP SEQUENCE ENTITY_SEQUENCE;
88
DROP TABLE PROVIDED_ID_ENTITY CASCADE CONSTRAINTS PURGE;
9+
DROP TABLE EXPRESSION_SQL_TYPE_PROPAGATION CASCADE CONSTRAINTS PURGE;
910

1011
CREATE TABLE DUMMY_ENTITY
1112
(
@@ -63,3 +64,8 @@ CREATE TABLE PROVIDED_ID_ENTITY
6364
ID NUMBER PRIMARY KEY,
6465
NAME VARCHAR2(30)
6566
);
67+
68+
CREATE TABLE EXPRESSION_SQL_TYPE_PROPAGATION(
69+
IDENTIFIER BIGINT PRIMARY KEY,
70+
ENUM_CLASS VARCHAR2(30)
71+
);

0 commit comments

Comments
(0)

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