diff --git a/pom.xml b/pom.xml index eeaa0b9e93..460f239464 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-relational-parent - 2.3.0-SNAPSHOT + 2.3.0-908-boolean-for-derived-queries-SNAPSHOT pom Spring Data Relational Parent diff --git a/spring-data-jdbc-distribution/pom.xml b/spring-data-jdbc-distribution/pom.xml index 03d6a5c2a0..a86a0a2724 100644 --- a/spring-data-jdbc-distribution/pom.xml +++ b/spring-data-jdbc-distribution/pom.xml @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 2.3.0-SNAPSHOT + 2.3.0-908-boolean-for-derived-queries-SNAPSHOT ../pom.xml diff --git a/spring-data-jdbc/pom.xml b/spring-data-jdbc/pom.xml index af9ad0904e..f99d52e942 100644 --- a/spring-data-jdbc/pom.xml +++ b/spring-data-jdbc/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-jdbc - 2.3.0-SNAPSHOT + 2.3.0-908-boolean-for-derived-queries-SNAPSHOT Spring Data JDBC Spring Data module for JDBC repositories. @@ -15,7 +15,7 @@ org.springframework.data spring-data-relational-parent - 2.3.0-SNAPSHOT + 2.3.0-908-boolean-for-derived-queries-SNAPSHOT diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java index be6398b3cf..7aca5b75f0 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfiguration.java @@ -133,7 +133,7 @@ public JdbcCustomConversions jdbcCustomConversions() { } } - private List userConverters() { + protected List userConverters() { return Collections.emptyList(); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/QueryMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/QueryMapper.java index f8ee2d7b1e..328e088295 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/QueryMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/repository/query/QueryMapper.java @@ -402,11 +402,15 @@ private Condition createCondition(Column column, @Nullable Object mappedValue, i } if (comparator == Comparator.IS_TRUE) { - return column.isEqualTo(SQL.literalOf(true)); + + Expression bind = bindBoolean(column, parameterSource, true); + return column.isEqualTo(bind); } if (comparator == Comparator.IS_FALSE) { - return column.isEqualTo(SQL.literalOf(false)); + + Expression bind = bindBoolean(column, parameterSource, false); + return column.isEqualTo(bind); } Expression columnExpression = column; @@ -495,6 +499,12 @@ private Condition createCondition(Column column, @Nullable Object mappedValue, i } } + private Expression bindBoolean(Column column, MapSqlParameterSource parameterSource, boolean value) { + + Object converted = converter.writeValue(value, ClassTypeInformation.OBJECT); + return bind(converted, Types.BIT, parameterSource, column.getName().getReference()); + } + Field createPropertyField(@Nullable RelationalPersistentEntity entity, SqlIdentifier key) { return entity == null ? new Field(key) : new MetadataBackedField(key, entity, mappingContext, converter); } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java index 54497a5d44..f3c30237dc 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/JdbcRepositoryIntegrationTests.java @@ -492,6 +492,19 @@ public void pageQueryProjectionShouldReturnProjectedEntities() { assertThat(result.getContent().get(0).getName()).isEqualTo("Entity Name"); } + @Test // #908 + void derivedQueryWithBooleanLiteralFindsCorrectValues() { + + repository.save(createDummyEntity()); + DummyEntity entity = createDummyEntity(); + entity.flag = true; + entity = repository.save(entity); + + List result = repository.findByFlagTrue(); + + assertThat(result).extracting(e -> e.idProp).containsExactly(entity.idProp); + } + private Instant createDummyBeforeAndAfterNow() { Instant now = Instant.now(); @@ -557,6 +570,8 @@ interface DummyEntityRepository extends CrudRepository { @Query("SELECT * FROM DUMMY_ENTITY WHERE OFFSET_DATE_TIME > :threshhold") List findByOffsetDateTime(@Param("threshhold") OffsetDateTime threshhold); + + List findByFlagTrue(); } @Configuration @@ -603,10 +618,12 @@ public void onApplicationEvent(AbstractRelationalEvent event) { @Data @NoArgsConstructor static class DummyEntity { + String name; Instant pointInTime; OffsetDateTime offsetDateTime; @Id private Long idProp; + boolean flag; public DummyEntity(String name) { this.name = name; diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfigurationIntegrationTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfigurationIntegrationTests.java index 1ec72619c9..e2cad273d7 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfigurationIntegrationTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/config/AbstractJdbcConfigurationIntegrationTests.java @@ -15,12 +15,13 @@ */ package org.springframework.data.jdbc.repository.config; +import static java.util.Arrays.*; import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; -import java.util.Arrays; -import java.util.Collections; +import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.function.Consumer; import org.junit.jupiter.api.Test; @@ -29,6 +30,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.ReadingConverter; import org.springframework.data.convert.WritingConverter; import org.springframework.data.jdbc.core.JdbcAggregateTemplate; import org.springframework.data.jdbc.core.convert.DataAccessStrategy; @@ -36,7 +38,9 @@ import org.springframework.data.jdbc.core.convert.JdbcCustomConversions; import org.springframework.data.jdbc.core.mapping.JdbcMappingContext; import org.springframework.data.relational.core.dialect.Dialect; -import org.springframework.data.relational.core.dialect.HsqlDbDialect; +import org.springframework.data.relational.core.dialect.LimitClause; +import org.springframework.data.relational.core.dialect.LockClause; +import org.springframework.data.relational.core.sql.render.SelectRenderContext; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; @@ -53,7 +57,7 @@ void configuresInfrastructureComponents() { assertApplicationContext(context -> { - List> expectedBeanTypes = Arrays.asList(DataAccessStrategy.class, // + List> expectedBeanTypes = asList(DataAccessStrategy.class, // JdbcMappingContext.class, // JdbcConverter.class, // JdbcCustomConversions.class, // @@ -70,11 +74,26 @@ void configuresInfrastructureComponents() { void registersSimpleTypesFromCustomConversions() { assertApplicationContext(context -> { + JdbcMappingContext mappingContext = context.getBean(JdbcMappingContext.class); assertThat( // mappingContext.getPersistentEntity(AbstractJdbcConfigurationUnderTest.Blah.class) // ).describedAs("Blah should not be an entity, since there is a WritingConversion configured for it") // .isNull(); + + }, AbstractJdbcConfigurationUnderTest.class, Infrastructure.class); + } + + @Test // #908 + void userProvidedConversionsOverwriteDialectSpecificConversions() { + + assertApplicationContext(applicationContext -> { + + Optional> customWriteTarget = applicationContext.getBean(JdbcCustomConversions.class) + .getCustomWriteTarget(Boolean.class); + + assertThat(customWriteTarget).contains(String.class); + }, AbstractJdbcConfigurationUnderTest.class, Infrastructure.class); } @@ -106,12 +125,12 @@ static class AbstractJdbcConfigurationUnderTest extends AbstractJdbcConfiguratio @Override @Bean public Dialect jdbcDialect(NamedParameterJdbcOperations operations) { - return HsqlDbDialect.INSTANCE; + return new DummyDialect(); } @Override - public JdbcCustomConversions jdbcCustomConversions() { - return new JdbcCustomConversions(Collections.singletonList(Blah2BlubbConverter.INSTANCE)); + protected List userConverters() { + return asList(Blah2BlubbConverter.INSTANCE, BooleanToYnConverter.INSTANCE); } @WritingConverter @@ -127,6 +146,59 @@ public Blubb convert(Blah blah) { private static class Blah {} private static class Blubb {} + + private static class DummyDialect implements Dialect { + @Override + public LimitClause limit() { + return null; + } + + @Override + public LockClause lock() { + return null; + } + + @Override + public SelectRenderContext getSelectContext() { + return null; + } + + @Override + public Collection getConverters() { + return asList(BooleanToNumberConverter.INSTANCE, NumberToBooleanConverter.INSTANCE); + } + } + + @WritingConverter + enum BooleanToNumberConverter implements Converter { + INSTANCE; + + @Override + public Number convert(Boolean source) { + return source ? 1 : 0; + } + } + + @ReadingConverter + enum NumberToBooleanConverter implements Converter { + INSTANCE; + + @Override + public Boolean convert(Number source) { + return source.intValue() == 0; + } + } + + @WritingConverter + enum BooleanToYnConverter implements Converter { + INSTANCE; + + @Override + public String convert(Boolean source) { + return source ? "Y" : "N"; + } + } + } } diff --git a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java index cd24ec3960..3416ae0bfa 100644 --- a/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java +++ b/spring-data-jdbc/src/test/java/org/springframework/data/jdbc/repository/query/PartTreeJdbcQueryUnitTests.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Properties; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; @@ -457,7 +458,7 @@ public void createsQueryToFindAllEntitiesByBooleanAttributeTrue() throws Excepti RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[0]); ParametrizedQuery query = jdbcQuery.createQuery(accessor, returnedType); - assertThat(query.getQuery()).isEqualTo(BASE_SELECT + " WHERE " + TABLE + ".\"ACTIVE\" = TRUE"); + assertThat(query.getQuery()).isEqualTo(BASE_SELECT + " WHERE " + TABLE + ".\"ACTIVE\" = :active"); } @Test // DATAJDBC-318 @@ -468,7 +469,7 @@ public void createsQueryToFindAllEntitiesByBooleanAttributeFalse() throws Except RelationalParametersParameterAccessor accessor = getAccessor(queryMethod, new Object[0]); ParametrizedQuery query = jdbcQuery.createQuery(accessor, returnedType); - assertThat(query.getQuery()).isEqualTo(BASE_SELECT + " WHERE " + TABLE + ".\"ACTIVE\" = FALSE"); + assertThat(query.getQuery()).isEqualTo(BASE_SELECT + " WHERE " + TABLE + ".\"ACTIVE\" = :active"); } @Test // DATAJDBC-318 diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-db2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-db2.sql index daa415344a..d41d8accdd 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-db2.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-db2.sql @@ -5,5 +5,6 @@ CREATE TABLE dummy_entity id_Prop BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100), POINT_IN_TIME TIMESTAMP, - OFFSET_DATE_TIME TIMESTAMP -- with time zone is only supported with z/OS + OFFSET_DATE_TIME TIMESTAMP, -- with time zone is only supported with z/OS + FLAG BOOLEAN ); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-h2.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-h2.sql index b9b3101690..5a3f1654a2 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-h2.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-h2.sql @@ -3,5 +3,6 @@ CREATE TABLE dummy_entity id_Prop BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100), POINT_IN_TIME TIMESTAMP, - OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE + OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE, + FLAG BOOLEAN ); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql index b9b3101690..5a3f1654a2 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-hsql.sql @@ -3,5 +3,6 @@ CREATE TABLE dummy_entity id_Prop BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100), POINT_IN_TIME TIMESTAMP, - OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE + OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE, + FLAG BOOLEAN ); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mariadb.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mariadb.sql index f9b086443b..663446bdcc 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mariadb.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mariadb.sql @@ -3,5 +3,6 @@ CREATE TABLE dummy_entity id_Prop BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100), POINT_IN_TIME TIMESTAMP(3), - OFFSET_DATE_TIME TIMESTAMP(3) + OFFSET_DATE_TIME TIMESTAMP(3), + FLAG BOOLEAN ); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mssql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mssql.sql index c71942b4c1..f18b9da5cc 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mssql.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mssql.sql @@ -4,5 +4,6 @@ CREATE TABLE dummy_entity id_Prop BIGINT IDENTITY PRIMARY KEY, NAME VARCHAR(100), POINT_IN_TIME DATETIME, - OFFSET_DATE_TIME DATETIMEOFFSET + OFFSET_DATE_TIME DATETIMEOFFSET, + FLAG BIT ); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql index 9c4085b27a..60e23ca6bd 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-mysql.sql @@ -1,9 +1,10 @@ SET SQL_MODE='ALLOW_INVALID_DATES'; -CREATE TABLE dummy_entity +CREATE TABLE DUMMY_ENTITY ( - id_Prop BIGINT AUTO_INCREMENT PRIMARY KEY, + ID_PROP BIGINT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(100), - POINT_IN_TIME TIMESTAMP(3) default null, - OFFSET_DATE_TIME TIMESTAMP(3) default null + POINT_IN_TIME TIMESTAMP(3) DEFAULT NULL, + OFFSET_DATE_TIME TIMESTAMP(3) DEFAULT NULL, + FLAG BIT(1) ); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql index a3d831346d..5a92e2a238 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-oracle.sql @@ -5,5 +5,6 @@ CREATE TABLE DUMMY_ENTITY ID_PROP NUMBER GENERATED BY DEFAULT ON NULL AS IDENTITY PRIMARY KEY, NAME VARCHAR2(100), POINT_IN_TIME TIMESTAMP, - OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE + OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE, + FLAG NUMBER(1,0) ); diff --git a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-postgres.sql b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-postgres.sql index 5e670bfe77..05f4908e71 100644 --- a/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-postgres.sql +++ b/spring-data-jdbc/src/test/resources/org.springframework.data.jdbc.repository/JdbcRepositoryIntegrationTests-postgres.sql @@ -4,5 +4,6 @@ CREATE TABLE dummy_entity id_Prop SERIAL PRIMARY KEY, NAME VARCHAR(100), POINT_IN_TIME TIMESTAMP, - OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE + OFFSET_DATE_TIME TIMESTAMP WITH TIME ZONE, + FLAG BOOLEAN ); diff --git a/spring-data-relational/pom.xml b/spring-data-relational/pom.xml index 4e42a006ec..943d75d4f8 100644 --- a/spring-data-relational/pom.xml +++ b/spring-data-relational/pom.xml @@ -6,7 +6,7 @@ 4.0.0 spring-data-relational - 2.3.0-SNAPSHOT + 2.3.0-908-boolean-for-derived-queries-SNAPSHOT Spring Data Relational Spring Data Relational support @@ -14,7 +14,7 @@ org.springframework.data spring-data-relational-parent - 2.3.0-SNAPSHOT + 2.3.0-908-boolean-for-derived-queries-SNAPSHOT diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java index 76f0c72cda..90f7d466e7 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/dialect/OracleDialect.java @@ -15,9 +15,15 @@ */ package org.springframework.data.relational.core.dialect; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.ReadingConverter; +import org.springframework.data.convert.WritingConverter; + import java.util.Collection; import java.util.Collections; +import static java.util.Arrays.*; + /** * An SQL dialect for Oracle. * @@ -47,7 +53,25 @@ public IdGeneration getIdGeneration() { @Override public Collection getConverters() { - return Collections.singletonList(TimestampAtUtcToOffsetDateTimeConverter.INSTANCE); + return asList(TimestampAtUtcToOffsetDateTimeConverter.INSTANCE, NumberToBooleanConverter.INSTANCE, BooleanToIntegerConverter.INSTANCE); + } + + @ReadingConverter + enum NumberToBooleanConverter implements Converter { + INSTANCE; + + @Override + public Boolean convert(Number number) { + return number.intValue() != 0; + } } + @WritingConverter + enum BooleanToIntegerConverter implements Converter { + INSTANCE; + @Override + public Integer convert(Boolean bool) { + return bool ? 1 : 0; + } + } } diff --git a/src/main/asciidoc/jdbc-custom-conversions.adoc b/src/main/asciidoc/jdbc-custom-conversions.adoc index ea4c5fd204..2a7a1b2f5f 100644 --- a/src/main/asciidoc/jdbc-custom-conversions.adoc +++ b/src/main/asciidoc/jdbc-custom-conversions.adoc @@ -1,4 +1,6 @@ [[jdbc.custom-converters]] +// for backward compatibility only: +[[jdbc.entity-persistence.custom-converters]] == Custom Conversions Spring Data JDBC allows registration of custom converters to influence how values are mapped in the database. @@ -55,12 +57,26 @@ class MyJdbcConfiguration extends AbstractJdbcConfiguration { // … - @Overwrite - @Bean - public JdbcCustomConversions jdbcCustomConversions() { - return new JdbcCustomConversions(Arrays.asList(new BooleanToStringConverter(), new StringToBooleanConverter())); - } + @Override + protected List userConverters() { + return Arrays.asList(new BooleanToStringConverter(), new StringToBooleanConverter()); + } + } ---- +NOTE: In previous versions of Spring Data JDBC it was recommended to directly overwrite `AbstractJdbcConfiguration.jdbcCustomConversions()`. +This is no longer necessary or even recommended, since that method assembles conversions intended for all databases, conversions registered by the `Dialect` used and conversions registered by the user. +If you are migrating from an older version of Spring Data JDBC and have `AbstractJdbcConfiguration.jdbcCustomConversions()` overwritten conversions from your `Dialect` will not get registered. + +[[jdbc.custom-converters.jdbc-value]] +// for backward compatibility only: +[[jdbc.entity-persistence.custom-converters.jdbc-value]] +=== JdbcValue + +Value conversion uses `JdbcValue` to enrich values propagated to JDBC operations with a `java.sql.Types` type. +Register a custom write converter if you need to specify a JDBC-specific type instead of using type derivation. +This converter should convert the value to `JdbcValue` which has a field for the value and for the actual `JDBCType`. + + include::{spring-data-commons-docs}/custom-conversions.adoc[leveloffset=+3] diff --git a/src/main/asciidoc/jdbc.adoc b/src/main/asciidoc/jdbc.adoc index d55d0b1d11..ce7d4c2538 100644 --- a/src/main/asciidoc/jdbc.adoc +++ b/src/main/asciidoc/jdbc.adoc @@ -263,48 +263,7 @@ p1.bestFriend = AggregateReference.to(p2.id); ---- ==== -[[jdbc.entity-persistence.custom-converters]] -=== Custom converters - -Custom converters can be registered, for types that are not supported by default, by inheriting your configuration from `AbstractJdbcConfiguration` and overwriting the method `jdbcCustomConversions()`. - -==== -[source,java] ----- -@Configuration -class DataJdbcConfiguration extends AbstractJdbcConfiguration { - - @Override - public JdbcCustomConversions jdbcCustomConversions() { - return new JdbcCustomConversions(Collections.singletonList(TimestampTzToDateConverter.INSTANCE)); - } - - @ReadingConverter - enum TimestampTzToDateConverter implements Converter { - - INSTANCE; - - @Override - public Date convert(TIMESTAMPTZ source) { - //... - } - } -} ----- -==== - -The constructor of `JdbcCustomConversions` accepts a list of `org.springframework.core.convert.converter.Converter`. - -Converters should be annotated with `@ReadingConverter` or `@WritingConverter` in order to control their applicability to only reading from or to writing to the database. - -`TIMESTAMPTZ` in the example is a database specific data type that needs conversion into something more suitable for a domain model. - -[[jdbc.entity-persistence.custom-converters.jdbc-value]] -==== JdbcValue - -Value conversion uses `JdbcValue` to enrich values propagated to JDBC operations with a `java.sql.Types` type. -Register a custom write converter if you need to specify a JDBC-specific type instead of using type derivation. -This converter should convert the value to `JdbcValue` which has a field for the value and for the actual `JDBCType`. +* Types for which you registered suitable [[jdbc.custom-converters, custom conversions]]. [[jdbc.entity-persistence.naming-strategy]] === `NamingStrategy`