diff --git a/src/main/java/org/springframework/data/mapping/model/ConvertingPropertyAccessor.java b/src/main/java/org/springframework/data/mapping/model/ConvertingPropertyAccessor.java index bb5944af3..327da9e80 100644 --- a/src/main/java/org/springframework/data/mapping/model/ConvertingPropertyAccessor.java +++ b/src/main/java/org/springframework/data/mapping/model/ConvertingPropertyAccessor.java @@ -104,7 +104,7 @@ public class ConvertingPropertyAccessor extends SimplePersistentPropertyPathA return (S) (source == null // ? null // - : ClassUtils.resolvePrimitiveIfNecessary(type).isAssignableFrom(source.getClass()) // + : ClassUtils.isAssignable(type, source.getClass()) ? source // : conversionService.convert(source, type)); } diff --git a/src/test/java/org/springframework/data/mapping/model/ConvertingPropertyAccessorUnitTests.java b/src/test/java/org/springframework/data/mapping/model/ConvertingPropertyAccessorUnitTests.java index 38dc4f647..fa1fac7d3 100755 --- a/src/test/java/org/springframework/data/mapping/model/ConvertingPropertyAccessorUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/model/ConvertingPropertyAccessorUnitTests.java @@ -16,16 +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 org.junit.jupiter.api.Test; +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.context.SampleMappingContext; import org.springframework.data.mapping.context.SamplePersistentProperty; @@ -122,17 +129,47 @@ public class ConvertingPropertyAccessorUnitTests { var context = new SampleMappingContext(); var accessor = context.getPersistentEntity(Order.class).getPropertyAccessor(order); - var convertingAccessor = new ConvertingPropertyAccessor(accessor, - new DefaultConversionService()); + var convertingAccessor = new ConvertingPropertyAccessor(accessor, new DefaultConversionService()); - var path = context.getPersistentPropertyPath("customer.firstname", - Order.class); + var path = context.getPersistentPropertyPath("customer.firstname", Order.class); convertingAccessor.setProperty(path, 2); assertThat(convertingAccessor.getBean().getCustomer().getFirstname()).isEqualTo("2"); } + @TestFactory // #2546 + Stream doesNotInvokeConversionForMatchingPrimitives() { + + IntegerWrapper wrapper = new IntegerWrapper(); + wrapper.primitive = 42; + wrapper.boxed = 42; + + SampleMappingContext context = new SampleMappingContext(); + PersistentEntity entity = context + .getRequiredPersistentEntity(IntegerWrapper.class); + + SamplePersistentProperty primitiveProperty = entity.getRequiredPersistentProperty("primitive"); + SamplePersistentProperty boxedProperty = entity.getRequiredPersistentProperty("boxed"); + + PersistentPropertyAccessor accessor = entity.getPropertyAccessor(wrapper); + ConversionService conversionService = mock(ConversionService.class); + + ConvertingPropertyAccessor convertingAccessor = new ConvertingPropertyAccessor<>(accessor, + conversionService); + + Stream 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); @@ -142,8 +179,7 @@ public class ConvertingPropertyAccessorUnitTests { private static SamplePersistentProperty getIdProperty() { var mappingContext = new SampleMappingContext(); - var entity = mappingContext - .getRequiredPersistentEntity(Entity.class); + var entity = mappingContext.getRequiredPersistentEntity(Entity.class); return entity.getPersistentProperty("id"); } @@ -161,4 +197,26 @@ public class ConvertingPropertyAccessorUnitTests { static class Customer { String firstname; } + + static class IntegerWrapper { + int primitive; + Integer boxed; + } + + @Value(staticConstructor = "$") + static class PrimitiveFixture implements Named { + + 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; + } + } }