Browse Source
Restructured reading conversion process into: - converting technology base types (JDBC Arrays). - standard and custom conversions. - module specific conversions (AggregateReference). Closes #1828 Original pull request #2062pull/2076/merge
9 changed files with 248 additions and 449 deletions
@ -1,136 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2021-2025 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.Arrays; |
|
||||||
import java.util.Collection; |
|
||||||
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 |
|
||||||
* @author Mark Paluch |
|
||||||
* @since 2.3 |
|
||||||
*/ |
|
||||||
class AggregateReferenceConverters { |
|
||||||
|
|
||||||
/** |
|
||||||
* Returns the converters to be registered. |
|
||||||
* |
|
||||||
* @return a collection of converters. Guaranteed to be not {@literal null}. |
|
||||||
*/ |
|
||||||
public static Collection<GenericConverter> getConvertersToRegister(ConversionService conversionService) { |
|
||||||
|
|
||||||
return Arrays.asList(new AggregateReferenceToSimpleTypeConverter(conversionService), |
|
||||||
new SimpleTypeToAggregateReferenceConverter(conversionService)); |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Converts from an AggregateReference to its id, leaving the conversion of the id to the ultimate target type to the |
|
||||||
* delegate {@link ConversionService}. |
|
||||||
*/ |
|
||||||
@WritingConverter |
|
||||||
private static class AggregateReferenceToSimpleTypeConverter implements GenericConverter { |
|
||||||
|
|
||||||
private static final Set<ConvertiblePair> CONVERTIBLE_TYPES = Collections |
|
||||||
.singleton(new ConvertiblePair(AggregateReference.class, Object.class)); |
|
||||||
|
|
||||||
private final ConversionService delegate; |
|
||||||
|
|
||||||
AggregateReferenceToSimpleTypeConverter(ConversionService delegate) { |
|
||||||
this.delegate = delegate; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Set<ConvertiblePair> getConvertibleTypes() { |
|
||||||
return CONVERTIBLE_TYPES; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Object convert(@Nullable Object source, TypeDescriptor sourceDescriptor, TypeDescriptor targetDescriptor) { |
|
||||||
|
|
||||||
if (source == null) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
// if the target type is an AggregateReference we are 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 |
|
||||||
private static class SimpleTypeToAggregateReferenceConverter implements GenericConverter { |
|
||||||
|
|
||||||
private static final Set<ConvertiblePair> CONVERTIBLE_TYPES = Collections |
|
||||||
.singleton(new ConvertiblePair(Object.class, AggregateReference.class)); |
|
||||||
|
|
||||||
private final ConversionService delegate; |
|
||||||
|
|
||||||
SimpleTypeToAggregateReferenceConverter(ConversionService delegate) { |
|
||||||
this.delegate = delegate; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Set<ConvertiblePair> getConvertibleTypes() { |
|
||||||
return CONVERTIBLE_TYPES; |
|
||||||
} |
|
||||||
|
|
||||||
@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); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,99 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2021-2025 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 static org.assertj.core.api.Assertions.*; |
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach; |
|
||||||
import org.junit.jupiter.api.Test; |
|
||||||
|
|
||||||
import org.springframework.core.ResolvableType; |
|
||||||
import org.springframework.core.convert.TypeDescriptor; |
|
||||||
import org.springframework.core.convert.support.ConfigurableConversionService; |
|
||||||
import org.springframework.core.convert.support.DefaultConversionService; |
|
||||||
import org.springframework.data.jdbc.core.mapping.AggregateReference; |
|
||||||
|
|
||||||
/** |
|
||||||
* Tests for converters from an to {@link org.springframework.data.jdbc.core.mapping.AggregateReference}. |
|
||||||
* |
|
||||||
* @author Jens Schauder |
|
||||||
* @author Mark Paluch |
|
||||||
*/ |
|
||||||
class AggregateReferenceConvertersUnitTests { |
|
||||||
|
|
||||||
ConfigurableConversionService conversionService; |
|
||||||
|
|
||||||
@BeforeEach |
|
||||||
void setUp() { |
|
||||||
conversionService = new DefaultConversionService(); |
|
||||||
AggregateReferenceConverters.getConvertersToRegister(DefaultConversionService.getSharedInstance()) |
|
||||||
.forEach(it -> conversionService.addConverter(it)); |
|
||||||
} |
|
||||||
|
|
||||||
@Test // GH-992
|
|
||||||
void convertsFromSimpleValue() { |
|
||||||
|
|
||||||
ResolvableType aggregateReferenceWithIdTypeInteger = ResolvableType.forClassWithGenerics(AggregateReference.class, |
|
||||||
String.class, Integer.class); |
|
||||||
Object converted = conversionService.convert(23, TypeDescriptor.forObject(23), |
|
||||||
new TypeDescriptor(aggregateReferenceWithIdTypeInteger, null, null)); |
|
||||||
|
|
||||||
assertThat(converted).isEqualTo(AggregateReference.to(23)); |
|
||||||
} |
|
||||||
|
|
||||||
@Test // GH-992
|
|
||||||
void convertsFromSimpleValueThatNeedsSeparateConversion() { |
|
||||||
|
|
||||||
ResolvableType aggregateReferenceWithIdTypeInteger = ResolvableType.forClassWithGenerics(AggregateReference.class, |
|
||||||
String.class, Long.class); |
|
||||||
Object converted = conversionService.convert(23, TypeDescriptor.forObject(23), |
|
||||||
new TypeDescriptor(aggregateReferenceWithIdTypeInteger, null, null)); |
|
||||||
|
|
||||||
assertThat(converted).isEqualTo(AggregateReference.to(23L)); |
|
||||||
} |
|
||||||
|
|
||||||
@Test // GH-992
|
|
||||||
void convertsFromSimpleValueWithMissingTypeInformation() { |
|
||||||
|
|
||||||
Object converted = conversionService.convert(23, TypeDescriptor.forObject(23), |
|
||||||
TypeDescriptor.valueOf(AggregateReference.class)); |
|
||||||
|
|
||||||
assertThat(converted).isEqualTo(AggregateReference.to(23)); |
|
||||||
} |
|
||||||
|
|
||||||
@Test // GH-992
|
|
||||||
void convertsToSimpleValue() { |
|
||||||
|
|
||||||
AggregateReference<Object, Integer> source = AggregateReference.to(23); |
|
||||||
|
|
||||||
Object converted = conversionService.convert(source, TypeDescriptor.forObject(source), |
|
||||||
TypeDescriptor.valueOf(Integer.class)); |
|
||||||
|
|
||||||
assertThat(converted).isEqualTo(23); |
|
||||||
} |
|
||||||
|
|
||||||
@Test // GH-992
|
|
||||||
void convertsToSimpleValueThatNeedsSeparateConversion() { |
|
||||||
|
|
||||||
AggregateReference<Object, Integer> source = AggregateReference.to(23); |
|
||||||
|
|
||||||
Object converted = conversionService.convert(source, TypeDescriptor.forObject(source), |
|
||||||
TypeDescriptor.valueOf(Long.class)); |
|
||||||
|
|
||||||
assertThat(converted).isEqualTo(23L); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
@ -1 +1,13 @@ |
|||||||
CREATE TABLE aggregate_one ( id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, NAME VARCHAR(100), two INTEGER); |
CREATE TABLE aggregate_one |
||||||
|
( |
||||||
|
id BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, |
||||||
|
NAME VARCHAR(100), |
||||||
|
two INTEGER |
||||||
|
); |
||||||
|
|
||||||
|
CREATE TABLE REFERENCING_AGGREGATE |
||||||
|
( |
||||||
|
ID BIGINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1 ) PRIMARY KEY, |
||||||
|
NAME VARCHAR(100), |
||||||
|
REF INTEGER |
||||||
|
); |
||||||
|
|||||||
Loading…
Reference in new issue