Simpler check to avoid primitive conversion in ConvertingPropertyAccessor.

We now use Spring's ClassUtils.isAssignable(…) directly. Unit tests to verify that conversion is skipped for primitive / boxed scenarios.

See #2546.
This commit is contained in:
Oliver Drotbohm
2022-02-14 09:28:28 +01:00
parent f52bc25046
commit 44a472c695
2 changed files with 65 additions and 1 deletions

View File

@@ -116,7 +116,7 @@ public class ConvertingPropertyAccessor<T> extends SimplePersistentPropertyPathA
return (S) (source == null //
? null //
: ClassUtils.resolvePrimitiveIfNecessary(type).isAssignableFrom(source.getClass()) //
: ClassUtils.isAssignable(type, source.getClass())
? source //
: conversionService.convert(source, type));
}

View File

@@ -16,15 +16,23 @@
package org.springframework.data.mapping.model;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Value;
import java.util.stream.Stream;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.Named;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.PersistentPropertyAccessor;
import org.springframework.data.mapping.PersistentPropertyPath;
import org.springframework.data.mapping.context.SampleMappingContext;
@@ -133,6 +141,40 @@ public class ConvertingPropertyAccessorUnitTests {
assertThat(convertingAccessor.getBean().getCustomer().getFirstname()).isEqualTo("2");
}
@TestFactory // #2546
Stream<DynamicTest> doesNotInvokeConversionForMatchingPrimitives() {
IntegerWrapper wrapper = new IntegerWrapper();
wrapper.primitive = 42;
wrapper.boxed = 42;
SampleMappingContext context = new SampleMappingContext();
PersistentEntity<Object, SamplePersistentProperty> entity = context
.getRequiredPersistentEntity(IntegerWrapper.class);
SamplePersistentProperty primitiveProperty = entity.getRequiredPersistentProperty("primitive");
SamplePersistentProperty boxedProperty = entity.getRequiredPersistentProperty("boxed");
PersistentPropertyAccessor<IntegerWrapper> accessor = entity.getPropertyAccessor(wrapper);
ConversionService conversionService = mock(ConversionService.class);
ConvertingPropertyAccessor<IntegerWrapper> convertingAccessor = new ConvertingPropertyAccessor<>(accessor,
conversionService);
Stream<PrimitiveFixture> fixtures = Stream.of(
PrimitiveFixture.$(boxedProperty, int.class),
PrimitiveFixture.$(boxedProperty, Integer.class),
PrimitiveFixture.$(primitiveProperty, int.class),
PrimitiveFixture.$(primitiveProperty, Integer.class));
return DynamicTest.stream(fixtures, it -> {
convertingAccessor.getProperty(it.property, it.type);
verify(conversionService, never()).convert(any(), eq(it.type));
});
}
private static ConvertingPropertyAccessor getAccessor(Object entity, ConversionService conversionService) {
PersistentPropertyAccessor wrapper = new BeanWrapper<>(entity);
@@ -161,4 +203,26 @@ public class ConvertingPropertyAccessorUnitTests {
static class Customer {
String firstname;
}
static class IntegerWrapper {
int primitive;
Integer boxed;
}
@Value(staticConstructor = "$")
static class PrimitiveFixture implements Named<PrimitiveFixture> {
PersistentProperty<?> property;
Class<?> type;
@Override
public String getName() {
return String.format("Accessing %s as %s does not cause conversion.", property, type);
}
@Override
public PrimitiveFixture getPayload() {
return this;
}
}
}