Skip to content

Commit 3ec4063

Browse files
odrotbohmmp911de
authored andcommitted
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.
1 parent e5e8e67 commit 3ec4063

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

src/main/java/org/springframework/data/mapping/model/ConvertingPropertyAccessor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ private <S> S convertIfNecessary(@Nullable Object source, Class<S> type) {
116116

117117
return (S) (source == null //
118118
? null //
119-
: ClassUtils.resolvePrimitiveIfNecessary(type).isAssignableFrom(source.getClass()) //
119+
: ClassUtils.isAssignable(type, source.getClass())
120120
? source //
121121
: conversionService.convert(source, type));
122122
}

src/test/java/org/springframework/data/mapping/model/ConvertingPropertyAccessorUnitTests.java

+64
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,23 @@
1616
package org.springframework.data.mapping.model;
1717

1818
import static org.assertj.core.api.Assertions.*;
19+
import static org.mockito.ArgumentMatchers.*;
1920
import static org.mockito.Mockito.*;
2021

2122
import lombok.AllArgsConstructor;
2223
import lombok.Data;
2324
import lombok.Value;
2425

26+
import java.util.stream.Stream;
27+
28+
import org.junit.jupiter.api.DynamicTest;
29+
import org.junit.jupiter.api.Named;
2530
import org.junit.jupiter.api.Test;
31+
import org.junit.jupiter.api.TestFactory;
2632
import org.springframework.core.convert.ConversionService;
2733
import org.springframework.core.convert.support.DefaultConversionService;
34+
import org.springframework.data.mapping.PersistentEntity;
35+
import org.springframework.data.mapping.PersistentProperty;
2836
import org.springframework.data.mapping.PersistentPropertyAccessor;
2937
import org.springframework.data.mapping.PersistentPropertyPath;
3038
import org.springframework.data.mapping.context.SampleMappingContext;
@@ -133,6 +141,40 @@ public void shouldConvertToPropertyPathLeafType() {
133141
assertThat(convertingAccessor.getBean().getCustomer().getFirstname()).isEqualTo("2");
134142
}
135143

144+
@TestFactory // #2546
145+
Stream<DynamicTest> doesNotInvokeConversionForMatchingPrimitives() {
146+
147+
IntegerWrapper wrapper = new IntegerWrapper();
148+
wrapper.primitive = 42;
149+
wrapper.boxed = 42;
150+
151+
SampleMappingContext context = new SampleMappingContext();
152+
PersistentEntity<Object, SamplePersistentProperty> entity = context
153+
.getRequiredPersistentEntity(IntegerWrapper.class);
154+
155+
SamplePersistentProperty primitiveProperty = entity.getRequiredPersistentProperty("primitive");
156+
SamplePersistentProperty boxedProperty = entity.getRequiredPersistentProperty("boxed");
157+
158+
PersistentPropertyAccessor<IntegerWrapper> accessor = entity.getPropertyAccessor(wrapper);
159+
ConversionService conversionService = mock(ConversionService.class);
160+
161+
ConvertingPropertyAccessor<IntegerWrapper> convertingAccessor = new ConvertingPropertyAccessor<>(accessor,
162+
conversionService);
163+
164+
Stream<PrimitiveFixture> fixtures = Stream.of(
165+
PrimitiveFixture.$(boxedProperty, int.class),
166+
PrimitiveFixture.$(boxedProperty, Integer.class),
167+
PrimitiveFixture.$(primitiveProperty, int.class),
168+
PrimitiveFixture.$(primitiveProperty, Integer.class));
169+
170+
return DynamicTest.stream(fixtures, it -> {
171+
172+
convertingAccessor.getProperty(it.property, it.type);
173+
174+
verify(conversionService, never()).convert(any(), eq(it.type));
175+
});
176+
}
177+
136178
private static ConvertingPropertyAccessor getAccessor(Object entity, ConversionService conversionService) {
137179

138180
PersistentPropertyAccessor wrapper = new BeanWrapper<>(entity);
@@ -161,4 +203,26 @@ static class Order {
161203
static class Customer {
162204
String firstname;
163205
}
206+
207+
static class IntegerWrapper {
208+
int primitive;
209+
Integer boxed;
210+
}
211+
212+
@Value(staticConstructor = "$")
213+
static class PrimitiveFixture implements Named<PrimitiveFixture> {
214+
215+
PersistentProperty<?> property;
216+
Class<?> type;
217+
218+
@Override
219+
public String getName() {
220+
return String.format("Accessing %s as %s does not cause conversion.", property, type);
221+
}
222+
223+
@Override
224+
public PrimitiveFixture getPayload() {
225+
return this;
226+
}
227+
}
164228
}

0 commit comments

Comments
 (0)