Skip to content

Commit 92d8de0

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 0129161 commit 92d8de0

File tree

2 files changed

+66
-8
lines changed

2 files changed

+66
-8
lines changed

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

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

105105
return (S) (source == null //
106106
? null //
107-
: ClassUtils.resolvePrimitiveIfNecessary(type).isAssignableFrom(source.getClass()) //
107+
: ClassUtils.isAssignable(type, source.getClass())
108108
? source //
109109
: conversionService.convert(source, type));
110110
}

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

+65-7
Original file line numberDiff line numberDiff line change
@@ -16,16 +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

25-
import org.junit.jupiter.api.Test;
26+
import java.util.stream.Stream;
2627

28+
import org.junit.jupiter.api.DynamicTest;
29+
import org.junit.jupiter.api.Named;
30+
import org.junit.jupiter.api.Test;
31+
import org.junit.jupiter.api.TestFactory;
2732
import org.springframework.core.convert.ConversionService;
2833
import org.springframework.core.convert.support.DefaultConversionService;
34+
import org.springframework.data.mapping.PersistentEntity;
35+
import org.springframework.data.mapping.PersistentProperty;
2936
import org.springframework.data.mapping.PersistentPropertyAccessor;
3037
import org.springframework.data.mapping.context.SampleMappingContext;
3138
import org.springframework.data.mapping.context.SamplePersistentProperty;
@@ -122,17 +129,47 @@ public void shouldConvertToPropertyPathLeafType() {
122129
var context = new SampleMappingContext();
123130

124131
var accessor = context.getPersistentEntity(Order.class).getPropertyAccessor(order);
125-
var convertingAccessor = new ConvertingPropertyAccessor<Order>(accessor,
126-
new DefaultConversionService());
132+
var convertingAccessor = new ConvertingPropertyAccessor<Order>(accessor, new DefaultConversionService());
127133

128-
var path = context.getPersistentPropertyPath("customer.firstname",
129-
Order.class);
134+
var path = context.getPersistentPropertyPath("customer.firstname", Order.class);
130135

131136
convertingAccessor.setProperty(path, 2);
132137

133138
assertThat(convertingAccessor.getBean().getCustomer().getFirstname()).isEqualTo("2");
134139
}
135140

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

138175
PersistentPropertyAccessor wrapper = new BeanWrapper<>(entity);
@@ -142,8 +179,7 @@ private static ConvertingPropertyAccessor getAccessor(Object entity, ConversionS
142179
private static SamplePersistentProperty getIdProperty() {
143180

144181
var mappingContext = new SampleMappingContext();
145-
var entity = mappingContext
146-
.getRequiredPersistentEntity(Entity.class);
182+
var entity = mappingContext.getRequiredPersistentEntity(Entity.class);
147183
return entity.getPersistentProperty("id");
148184
}
149185

@@ -161,4 +197,26 @@ static class Order {
161197
static class Customer {
162198
String firstname;
163199
}
200+
201+
static class IntegerWrapper {
202+
int primitive;
203+
Integer boxed;
204+
}
205+
206+
@Value(staticConstructor = "$")
207+
static class PrimitiveFixture implements Named<PrimitiveFixture> {
208+
209+
PersistentProperty<?> property;
210+
Class<?> type;
211+
212+
@Override
213+
public String getName() {
214+
return String.format("Accessing %s as %s does not cause conversion.", property, type);
215+
}
216+
217+
@Override
218+
public PrimitiveFixture getPayload() {
219+
return this;
220+
}
221+
}
164222
}

0 commit comments

Comments
 (0)