Browse Source

Convert AggregateReference by converters instead of custom code paths.

Closes #992
Original pull request #993
pull/1018/head
Jens Schauder 5 years ago committed by Mark Paluch
parent
commit
221d11324f
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 120
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReferenceConverters.java
  2. 25
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java
  3. 19
      spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcCustomConversions.java
  4. 83
      spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/AggregateReferenceConvertersUnitTests.java
  5. 2
      spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/BasicRelationalConverter.java

120
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/AggregateReferenceConverters.java

@ -0,0 +1,120 @@ @@ -0,0 +1,120 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core.convert;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import org.springframework.lang.Nullable;
/**
* Converters for aggregate references. They need a {@link ConversionService} in order to delegate the conversion of the
* content of the {@link AggregateReference}.
*
* @author Jens Schauder
* @since 2.6
*/
class AggregateReferenceConverters {
/**
* Prevent instantiation.
*/
private AggregateReferenceConverters() {}
/**
* Converts from an AggregateReference to its id, leaving the conversion of the id to the ultimate target type to the
* delegate {@link ConversionService}.
*/
@WritingConverter
static class AggregateReferenceToSimpleTypeConverter implements GenericConverter {
private final ConversionService delegate;
AggregateReferenceToSimpleTypeConverter(ConversionService delegate) {
this.delegate = delegate;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(AggregateReference.class, Object.class));
}
@Override
public Object convert(@Nullable Object source, TypeDescriptor sourceDescriptor, TypeDescriptor targetDescriptor) {
if (source == null) {
return null;
}
// if the target type is an AggregateReference we just going to assume it is of the correct type,
// because it was already converted.
Class<?> objectType = targetDescriptor.getObjectType();
if (objectType.isAssignableFrom(AggregateReference.class)) {
return source;
}
Object id = ((AggregateReference<?, ?>) source).getId();
if (id == null) {
throw new IllegalStateException(
String.format("Aggregate references id must not be null when converting to %s from %s to %s", source,
sourceDescriptor, targetDescriptor));
}
return delegate.convert(id, TypeDescriptor.valueOf(id.getClass()), targetDescriptor);
}
}
/**
* Convert any simple type to an {@link AggregateReference}. If the {@literal targetDescriptor} contains information
* about the generic type id will properly get converted to the desired type by the delegate
* {@link ConversionService}.
*/
@ReadingConverter
static class SimpleTypeToAggregateReferenceConverter implements GenericConverter {
private final ConversionService delegate;
SimpleTypeToAggregateReferenceConverter(ConversionService delegate) {
this.delegate = delegate;
}
@Override
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object.class, AggregateReference.class));
}
@Override
public Object convert(@Nullable Object source, TypeDescriptor sourceDescriptor, TypeDescriptor targetDescriptor) {
if (source == null) {
return null;
}
ResolvableType componentType = targetDescriptor.getResolvableType().getGenerics()[1];
TypeDescriptor targetType = TypeDescriptor.valueOf(componentType.resolve());
Object convertedId = delegate.convert(source, TypeDescriptor.valueOf(source.getClass()), targetType);
return AggregateReference.to(convertedId);
}
}
}

25
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/BasicJdbcConverter.java

@ -26,7 +26,9 @@ import org.slf4j.Logger; @@ -26,7 +26,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
@ -220,16 +222,12 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc @@ -220,16 +222,12 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
}
if (getConversions().hasCustomReadTarget(value.getClass(), type.getType())) {
return getConversionService().convert(value, type.getType());
}
if (AggregateReference.class.isAssignableFrom(type.getType())) {
if (type.getType().isAssignableFrom(value.getClass())) {
return value;
}
TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(value.getClass());
TypeDescriptor targetDescriptor = typeInformationToTypeDescriptor(type);
return readAggregateReference(value, type);
return getConversionService().convert(value, sourceDescriptor,
targetDescriptor);
}
if (value instanceof Array) {
@ -243,12 +241,11 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc @@ -243,12 +241,11 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
return super.readValue(value, type);
}
@SuppressWarnings("ConstantConditions")
private Object readAggregateReference(@Nullable Object value, TypeInformation<?> type) {
private static TypeDescriptor typeInformationToTypeDescriptor(TypeInformation<?> type) {
TypeInformation<?> idType = type.getSuperTypeInformation(AggregateReference.class).getTypeArguments().get(1);
Class<?>[] generics = type.getTypeArguments().stream().map(TypeInformation::getType).toArray(Class[]::new);
return AggregateReference.to(readValue(value, idType));
return new TypeDescriptor(ResolvableType.forClassWithGenerics(type.getType(), generics), null, null);
}
/*
@ -263,10 +260,6 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc @@ -263,10 +260,6 @@ public class BasicJdbcConverter extends BasicRelationalConverter implements Jdbc
return null;
}
if (AggregateReference.class.isAssignableFrom(value.getClass())) {
return writeValue(((AggregateReference) value).getId(), type);
}
return super.writeValue(value, type);
}

19
spring-data-jdbc/src/main/java/org/springframework/data/jdbc/core/convert/JdbcCustomConversions.java

@ -15,11 +15,15 @@ @@ -15,11 +15,15 @@
*/
package org.springframework.data.jdbc.core.convert;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.GenericConverter.ConvertiblePair;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
@ -36,8 +40,19 @@ import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes; @@ -36,8 +40,19 @@ import org.springframework.data.jdbc.core.mapping.JdbcSimpleTypes;
*/
public class JdbcCustomConversions extends CustomConversions {
private static final Collection<Object> STORE_CONVERTERS = Collections
.unmodifiableCollection(Jsr310TimestampBasedConverters.getConvertersToRegister());
private static final Collection<Object> STORE_CONVERTERS;
static {
List<Object> converters = new ArrayList<>(Jsr310TimestampBasedConverters.getConvertersToRegister());
ConversionService conversionService = DefaultConversionService.getSharedInstance();
converters.add(new AggregateReferenceConverters.AggregateReferenceToSimpleTypeConverter(conversionService));
converters.add(new AggregateReferenceConverters.SimpleTypeToAggregateReferenceConverter(conversionService));
STORE_CONVERTERS = Collections.unmodifiableCollection(converters);
}
private static final StoreConversions STORE_CONVERSIONS = StoreConversions.of(JdbcSimpleTypes.HOLDER,
STORE_CONVERTERS);

83
spring-data-jdbc/src/test/java/org/springframework/data/jdbc/core/convert/AggregateReferenceConvertersUnitTests.java

@ -0,0 +1,83 @@ @@ -0,0 +1,83 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.jdbc.core.convert;
import org.junit.jupiter.api.Test;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.jdbc.core.mapping.AggregateReference;
import static org.assertj.core.api.Assertions.*;
/**
* Tests for converters from an to {@link org.springframework.data.jdbc.core.mapping.AggregateReference}.
*
* @author Jens Schauder
*/
class AggregateReferenceConvertersUnitTests {
AggregateReferenceConverters.SimpleTypeToAggregateReferenceConverter simpleToAggregate = new AggregateReferenceConverters.SimpleTypeToAggregateReferenceConverter(DefaultConversionService.getSharedInstance());
AggregateReferenceConverters.AggregateReferenceToSimpleTypeConverter aggregateToSimple = new AggregateReferenceConverters.AggregateReferenceToSimpleTypeConverter(DefaultConversionService.getSharedInstance());
@Test // #992
void convertsFromSimpleValue() {
ResolvableType aggregateReferenceWithIdTypeInteger = ResolvableType.forClassWithGenerics(AggregateReference.class, String.class, Integer.class);
final Object converted = simpleToAggregate.convert(23, TypeDescriptor.forObject(23), new TypeDescriptor(aggregateReferenceWithIdTypeInteger, null, null));
assertThat(converted).isEqualTo(AggregateReference.to(23));
}
@Test // #992
void convertsFromSimpleValueThatNeedsSeparateConversion() {
ResolvableType aggregateReferenceWithIdTypeInteger = ResolvableType.forClassWithGenerics(AggregateReference.class, String.class, Long.class);
final Object converted = simpleToAggregate.convert(23, TypeDescriptor.forObject(23), new TypeDescriptor(aggregateReferenceWithIdTypeInteger, null, null));
assertThat(converted).isEqualTo(AggregateReference.to(23L));
}
@Test // #992
void convertsFromSimpleValueWithMissingTypeInformation() {
final Object converted = simpleToAggregate.convert(23, TypeDescriptor.forObject(23), TypeDescriptor.valueOf(AggregateReference.class));
assertThat(converted).isEqualTo(AggregateReference.to(23));
}
@Test // #992
void convertsToSimpleValue() {
final AggregateReference<Object, Integer> source = AggregateReference.to(23);
final Object converted = aggregateToSimple.convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(Integer.class));
assertThat(converted).isEqualTo(23);
}
@Test // #992
void convertsToSimpleValueThatNeedsSeparateConversion() {
final AggregateReference<Object, Integer> source = AggregateReference.to(23);
final Object converted = aggregateToSimple.convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(Long.class));
assertThat(converted).isEqualTo(23L);
}
}

2
spring-data-relational/src/main/java/org/springframework/data/relational/core/conversion/BasicRelationalConverter.java

@ -19,7 +19,9 @@ import java.util.Collections; @@ -19,7 +19,9 @@ import java.util.Collections;
import java.util.Optional;
import java.util.function.Function;
import org.springframework.core.ResolvableType;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.ConfigurableConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.convert.CustomConversions;

Loading…
Cancel
Save