Convert AggregateReference by converters instead of custom code paths.
Closes #992 Original pull request #993
This commit is contained in:
committed by
Mark Paluch
parent
340e8c6648
commit
221d11324f
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
if (getConversions().hasCustomReadTarget(value.getClass(), type.getType())) {
|
||||
return getConversionService().convert(value, type.getType());
|
||||
}
|
||||
|
||||
if (AggregateReference.class.isAssignableFrom(type.getType())) {
|
||||
TypeDescriptor sourceDescriptor = TypeDescriptor.valueOf(value.getClass());
|
||||
TypeDescriptor targetDescriptor = typeInformationToTypeDescriptor(type);
|
||||
|
||||
if (type.getType().isAssignableFrom(value.getClass())) {
|
||||
return value;
|
||||
}
|
||||
|
||||
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
|
||||
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
|
||||
return null;
|
||||
}
|
||||
|
||||
if (AggregateReference.class.isAssignableFrom(value.getClass())) {
|
||||
return writeValue(((AggregateReference) value).getId(), type);
|
||||
}
|
||||
|
||||
return super.writeValue(value, type);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
*/
|
||||
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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user