diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java index 3c13ea9514f..d7e09287520 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcConverter.java @@ -125,6 +125,15 @@ default R readAndResolve(Class type, RowDocument source, Identifier ident */ SQLType getTargetSqlType(RelationalPersistentProperty property); + /** + * The SQL type constant used when using this property as a parameter for a SQL statement. + * + * @return Must not be {@code null}. + * @see java.sql.Types + * @since 2.0 + */ + SQLType getTargetSqlType(Class property); + @Override RelationalMappingContext getMappingContext(); diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java index 2b3ad379456..8700ac8ec56 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/MappingJdbcConverter.java @@ -21,7 +21,6 @@ import java.sql.SQLType; import java.util.Iterator; import java.util.Map; -import java.util.Optional; import java.util.function.Function; import org.apache.commons.logging.Log; @@ -152,6 +151,11 @@ public SQLType getTargetSqlType(RelationalPersistentProperty property) { return JdbcUtil.targetSqlTypeFor(getColumnType(property)); } + @Override + public SQLType getTargetSqlType(Class property) { + return JdbcUtil.targetSqlTypeFor(property); + } + @Override public Class getColumnType(RelationalPersistentProperty property) { return doGetColumnType(property); @@ -205,13 +209,13 @@ public Object readValue(@Nullable Object value, TypeInformation type) { @Override @Nullable - public Object writeValue(@Nullable Object value, TypeInformation type) { + public Object writeValue(@Nullable Object value, TypeInformation targetType) { if (value == null) { return null; } - return super.writeValue(value, type); + return super.writeValue(value, targetType); } private boolean canWriteAsJdbcValue(@Nullable Object value) { @@ -236,8 +240,9 @@ private boolean canWriteAsJdbcValue(@Nullable Object value) { return true; } - Optional> customWriteTarget = getConversions().getCustomWriteTarget(value.getClass()); - return customWriteTarget.isPresent() && customWriteTarget.get().isAssignableFrom(JdbcValue.class); + return getCustomConversions().getCustomWriteTarget(value.getClass()) + .filter(it -> it.isAssignableFrom(JdbcValue.class)) + .isPresent(); } @Override @@ -353,7 +358,7 @@ public T getPropertyValue(RelationalPersistentProperty property) { AggregatePath aggregatePath = this.context.aggregatePath(); - if (getConversions().isSimpleType(property.getActualType())) { + if (getCustomConversions().isSimpleType(property.getActualType())) { return (T) delegate.getValue(aggregatePath); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java index e38e5c02d74..1fc350d92a4 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/QueryMapper.java @@ -381,10 +381,12 @@ private JdbcValue convertToJdbcValue(RelationalPersistentProperty property, @Nul */ private JdbcValue getWriteValue(RelationalPersistentProperty property, Object value) { + Class columnType = converter.getColumnType(property); + return converter.writeJdbcValue( // value, // - converter.getColumnType(property), // - converter.getTargetSqlType(property) // + columnType, // + converter.getTargetSqlType(columnType) // ); } diff --git a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/AggregateReference.java b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/AggregateReference.java index 2ae0fa42589..449771ac739 100644 --- a/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/AggregateReference.java +++ b/spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/mapping/AggregateReference.java @@ -15,8 +15,6 @@ */ package org.springframework.data.jdbc.core.mapping; -import java.util.Objects; - import org.springframework.lang.Nullable; import org.springframework.util.Assert; @@ -27,6 +25,7 @@ * @param the type of the id of the referenced aggregate root. * @author Jens Schauder * @author Myeonghyeon Lee + * @author Mikhail Polivakha * @since 1.0 */ public interface AggregateReference { @@ -48,42 +47,15 @@ static AggregateReference to(ID id) { * @param * @param */ - class IdOnlyAggregateReference implements AggregateReference { - - private final ID id; - - public IdOnlyAggregateReference(ID id) { + record IdOnlyAggregateReference(ID id) implements AggregateReference { + public IdOnlyAggregateReference { Assert.notNull(id, "Id must not be null"); - - this.id = id; } @Override public ID getId() { - return id; - } - - @Override - public boolean equals(@Nullable Object o) { - - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - IdOnlyAggregateReference that = (IdOnlyAggregateReference) o; - return id.equals(that.id); - } - - @Override - public int hashCode() { - return Objects.hash(id); - } - - @Override - public String toString() { - - return "IdOnlyAggregateReference{" + "id=" + id + '}'; + return id(); } } } diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java index e4bb257361b..7c677f9b5b7 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/convert/MappingR2dbcConverter.java @@ -101,7 +101,7 @@ public R read(Class type, Row row, @Nullable RowMetadata metadata) { return type.cast(row); } - if (getConversions().hasCustomReadTarget(Row.class, rawType) + if (getCustomConversions().hasCustomReadTarget(Row.class, rawType) && getConversionService().canConvert(Row.class, rawType)) { return getConversionService().convert(row, rawType); } @@ -170,7 +170,7 @@ public void write(Object source, OutboundRow sink) { Class userClass = ClassUtils.getUserClass(source); - Optional> customTarget = getConversions().getCustomWriteTarget(userClass, OutboundRow.class); + Optional> customTarget = getCustomConversions().getCustomWriteTarget(userClass, OutboundRow.class); if (customTarget.isPresent()) { OutboundRow result = getConversionService().convert(source, OutboundRow.class); @@ -212,7 +212,7 @@ private void writeProperties(OutboundRow sink, RelationalPersistentEntity ent continue; } - if (getConversions().isSimpleType(value.getClass())) { + if (getCustomConversions().isSimpleType(value.getClass())) { writeSimpleInternal(sink, value, isNew, property); } else { writePropertyInternal(sink, value, isNew, property); @@ -286,7 +286,7 @@ private List writeCollectionInternal(Collection source, @Nullable Typ Class elementType = element == null ? null : element.getClass(); - if (elementType == null || getConversions().isSimpleType(elementType)) { + if (elementType == null || getCustomConversions().isSimpleType(elementType)) { collection.add(getPotentiallyConvertedSimpleWrite(element, componentType != null ? componentType.getType() : Object.class)); } else if (element instanceof Collection || elementType.isArray()) { @@ -306,7 +306,7 @@ private void writeNullInternal(OutboundRow sink, RelationalPersistentProperty pr private Class getPotentiallyConvertedSimpleNullType(Class type) { - Optional> customTarget = getConversions().getCustomWriteTarget(type); + Optional> customTarget = getCustomConversions().getCustomWriteTarget(type); if (customTarget.isPresent()) { return customTarget.get(); @@ -353,7 +353,7 @@ private Object getPotentiallyConvertedSimpleWrite(@Nullable Object value, Class< } } - Optional> customTarget = getConversions().getCustomWriteTarget(value.getClass()); + Optional> customTarget = getCustomConversions().getCustomWriteTarget(value.getClass()); if (customTarget.isPresent()) { return getConversionService().convert(value, customTarget.get()); @@ -393,7 +393,7 @@ public Object getArrayValue(ArrayColumns arrayColumns, RelationalPersistentPrope @Override public Class getTargetType(Class valueType) { - Optional> writeTarget = getConversions().getCustomWriteTarget(valueType); + Optional> writeTarget = getCustomConversions().getCustomWriteTarget(valueType); return writeTarget.orElseGet(() -> { return Enum.class.isAssignableFrom(valueType) ? String.class : valueType; @@ -402,7 +402,7 @@ public Class getTargetType(Class valueType) { @Override public boolean isSimpleType(Class type) { - return getConversions().isSimpleType(type); + return getCustomConversions().isSimpleType(type); } // ---------------------------------- diff --git a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java index bbc55b40a4b..ccdad521a89 100644 --- a/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java +++ b/spring-data-r2dbc/src/main/java/org/springframework/data/r2dbc/core/R2dbcEntityTemplate.java @@ -852,7 +852,7 @@ public RowsFetchSpec getRowsFetchSpec(DatabaseClient.GenericExecuteSpec e // Bridge-code: Consider Converter until we have fully migrated to RowDocument if (converter instanceof AbstractRelationalConverter relationalConverter - && relationalConverter.getConversions().hasCustomReadTarget(Row.class, resultType)) { + && relationalConverter.getCustomConversions().hasCustomReadTarget(Row.class, resultType)) { ConversionService conversionService = relationalConverter.getConversionService(); rowMapper = (row, rowMetadata) -> (T) conversionService.convert(row, resultType); diff --git a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactoryBeanUnitTests.java b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactoryBeanUnitTests.java index 329ed876fa6..64e086e9390 100644 --- a/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactoryBeanUnitTests.java +++ b/spring-data-r2dbc/src/test/java/org/springframework/data/r2dbc/repository/support/R2dbcRepositoryFactoryBeanUnitTests.java @@ -18,6 +18,7 @@ import static org.assertj.core.api.Assertions.*; import static org.mockito.Mockito.*; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.ListableBeanFactory; @@ -26,6 +27,8 @@ import org.springframework.data.r2dbc.repository.R2dbcRepository; import org.springframework.data.spel.EvaluationContextProvider; import org.springframework.data.spel.ReactiveExtensionAwareEvaluationContextProvider; +import org.springframework.data.repository.query.ReactiveExtensionAwareQueryMethodEvaluationContextProvider; +import org.springframework.data.repository.query.ReactiveQueryMethodEvaluationContextProvider; import org.springframework.r2dbc.core.DatabaseClient; import org.springframework.test.util.ReflectionTestUtils; @@ -56,7 +59,5 @@ void shouldConfigureReactiveExtensionAwareQueryMethodEvaluationContextProvider() static class Person {} - interface PersonRepository extends R2dbcRepository - - {} + interface PersonRepository extends R2dbcRepository {} } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/AbstractRelationalConverter.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/AbstractRelationalConverter.java index f9fe08a9d75..4ca2285e96d 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/AbstractRelationalConverter.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/AbstractRelationalConverter.java @@ -89,7 +89,7 @@ public ConversionService getConversionService() { return conversionService; } - public CustomConversions getConversions() { + public CustomConversions getCustomConversions() { return conversions; } diff --git a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java index c7a07679fae..880066b636d 100644 --- a/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java +++ b/spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/MappingRelationalConverter.java @@ -111,7 +111,7 @@ public MappingRelationalConverter(RelationalMappingContext context) { super(context); this.spELContext = new SpELContext(DocumentPropertyAccessor.INSTANCE); - this.introspector = createIntrospector(projectionFactory, getConversions(), getMappingContext()); + this.introspector = createIntrospector(projectionFactory, getCustomConversions(), getMappingContext()); } /** @@ -126,7 +126,7 @@ public MappingRelationalConverter(RelationalMappingContext context, CustomConver super(context, conversions); this.spELContext = new SpELContext(DocumentPropertyAccessor.INSTANCE); - this.introspector = createIntrospector(projectionFactory, getConversions(), getMappingContext()); + this.introspector = createIntrospector(projectionFactory, getCustomConversions(), getMappingContext()); } private static EntityProjectionIntrospector createIntrospector(ProjectionFactory projectionFactory, @@ -164,7 +164,7 @@ protected ConversionContext getConversionContext(ObjectPath path) { Assert.notNull(path, "ObjectPath must not be null"); - return new DefaultConversionContext(this, getConversions(), path, this::readAggregate, this::readCollectionOrArray, + return new DefaultConversionContext(this, getCustomConversions(), path, this::readAggregate, this::readCollectionOrArray, this::readMap, this::getPotentiallyConvertedSimpleRead); } @@ -202,7 +202,7 @@ public R project(EntityProjection projection, RowDocument document) { } protected ProjectingConversionContext newProjectingConversionContext(EntityProjection projection) { - return new ProjectingConversionContext(this, getConversions(), ObjectPath.ROOT, this::readCollectionOrArray, + return new ProjectingConversionContext(this, getCustomConversions(), ObjectPath.ROOT, this::readCollectionOrArray, this::readMap, this::getPotentiallyConvertedSimpleRead, projection); } @@ -334,7 +334,7 @@ protected S readAggregate(ConversionContext context, RowDocumentAccessor doc Class rawType = typeHint.getType(); - if (getConversions().hasCustomReadTarget(RowDocument.class, rawType)) { + if (getCustomConversions().hasCustomReadTarget(RowDocument.class, rawType)) { return doConvert(documentAccessor.getDocument(), rawType, typeHint.getType()); } @@ -627,7 +627,7 @@ public Object readValue(@Nullable Object value, TypeInformation type) { @Nullable private Object getPotentiallyConvertedSimpleWrite(Object value) { - Optional> customTarget = getConversions().getCustomWriteTarget(value.getClass()); + Optional> customTarget = getCustomConversions().getCustomWriteTarget(value.getClass()); if (customTarget.isPresent()) { return getConversionService().convert(value, customTarget.get()); @@ -649,7 +649,7 @@ protected Object getPotentiallyConvertedSimpleRead(Object value, TypeInformation Class target = type.getType(); - if (getConversions().hasCustomReadTarget(value.getClass(), target)) { + if (getCustomConversions().hasCustomReadTarget(value.getClass(), target)) { return getConversionService().convert(value, TypeDescriptor.forObject(value), createTypeDescriptor(type)); } @@ -677,76 +677,63 @@ private static TypeDescriptor createTypeDescriptor(TypeInformation type) { @Override @Nullable - public Object writeValue(@Nullable Object value, TypeInformation type) { + public Object writeValue(@Nullable Object value, TypeInformation targetType) { if (value == null) { return null; } - if (getConversions().isSimpleType(value.getClass())) { + Optional> customTarget; - Optional> customWriteTarget = getConversions().hasCustomWriteTarget(value.getClass(), type.getType()) - ? getConversions().getCustomWriteTarget(value.getClass(), type.getType()) - : getConversions().getCustomWriteTarget(type.getType()); - - if (customWriteTarget.isPresent()) { - return getConversionService().convert(value, customWriteTarget.get()); - } + if ((customTarget = getCustomConversions().getCustomWriteTarget(value.getClass())).isPresent()) { + return getConversionService().convert(value, customTarget.get()); + } - if (TypeInformation.OBJECT != type) { + if (getCustomConversions().isSimpleType(value.getClass())) { - if (type.getType().isAssignableFrom(value.getClass())) { + if (targetType.getType().isAssignableFrom(value.getClass())) { - if (value.getClass().isEnum()) { - return getPotentiallyConvertedSimpleWrite(value); - } + if (value.getClass().isEnum()) { + return ((Enum) value).name(); + } - return value; - } else { - if (getConversionService().canConvert(value.getClass(), type.getType())) { - value = getConversionService().convert(value, type.getType()); - } + return value; + } else { + if (getConversionService().canConvert(value.getClass(), targetType.getType())) { + value = getConversionService().convert(value, targetType.getType()); } } - return getPotentiallyConvertedSimpleWrite(value); + return value; } if (value.getClass().isArray()) { - return writeArray(value, type); + return writeArray(value, targetType); } if (value instanceof Collection) { - return writeCollection((Iterable) value, type); + return writeCollection((Iterable) value, targetType); } if (getMappingContext().hasPersistentEntityFor(value.getClass())) { - - RelationalPersistentEntity persistentEntity = getMappingContext().getPersistentEntity(value.getClass()); - - if (persistentEntity != null) { - - Object id = persistentEntity.getIdentifierAccessor(value).getIdentifier(); - return writeValue(id, type); - } + RelationalPersistentEntity persistentEntity = getMappingContext().getRequiredPersistentEntity(value.getClass()); + Object id = persistentEntity.getIdentifierAccessor(value).getIdentifier(); + return writeValue(id, targetType); } - return - - getConversionService().convert(value, type.getType()); + return getConversionService().convert(value, targetType.getType()); } private Object writeArray(Object value, TypeInformation type) { Class componentType = value.getClass().getComponentType(); - Optional> optionalWriteTarget = getConversions().getCustomWriteTarget(componentType); + Optional> optionalWriteTarget = getCustomConversions().getCustomWriteTarget(componentType); if (optionalWriteTarget.isEmpty() && !componentType.isEnum()) { return value; } - Class customWriteTarget = optionalWriteTarget - .orElseGet(() -> componentType.isEnum() ? String.class : componentType); + Class customWriteTarget = optionalWriteTarget.orElse(String.class); // Enum -> String.class // optimization: bypass identity conversion if (customWriteTarget.equals(componentType)) {