diff --git a/src/main/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactory.java b/src/main/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactory.java index 4ae837e54..254905258 100644 --- a/src/main/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactory.java +++ b/src/main/java/org/springframework/data/auditing/MappingAuditableBeanWrapperFactory.java @@ -26,6 +26,7 @@ import org.springframework.data.annotation.LastModifiedDate; import org.springframework.data.domain.Auditable; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.BeanWrapper; import org.springframework.util.Assert; @@ -82,7 +83,8 @@ class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory { metadataCache.put(type, metadata); } - return metadata.isAuditable() ? new MappingMetadataAuditableBeanWrapper(source, metadata) : null; + PersistentPropertyAccessor accessor = entity.getPropertyAccessor(source); + return metadata.isAuditable() ? new MappingMetadataAuditableBeanWrapper(accessor, metadata) : null; } /** @@ -132,7 +134,7 @@ class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory { */ static class MappingMetadataAuditableBeanWrapper extends DateConvertingAuditableBeanWrapper { - private final BeanWrapper wrapper; + private final PersistentPropertyAccessor accessor; private final MappingAuditingMetadata metadata; /** @@ -142,12 +144,12 @@ class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory { * @param target must not be {@literal null}. * @param metadata must not be {@literal null}. */ - public MappingMetadataAuditableBeanWrapper(Object target, MappingAuditingMetadata metadata) { + public MappingMetadataAuditableBeanWrapper(PersistentPropertyAccessor accessor, MappingAuditingMetadata metadata) { - Assert.notNull(target, "Target object must not be null!"); + Assert.notNull(accessor, "Target object must not be null!"); Assert.notNull(metadata, "Auditing metadata must not be null!"); - this.wrapper = BeanWrapper.create(target, null); + this.accessor = accessor; this.metadata = metadata; } @@ -159,7 +161,7 @@ class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory { public void setCreatedBy(Object value) { if (metadata.createdByProperty != null) { - this.wrapper.setProperty(metadata.createdByProperty, value); + this.accessor.setProperty(metadata.createdByProperty, value); } } @@ -173,7 +175,7 @@ class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory { PersistentProperty property = metadata.createdDateProperty; if (property != null) { - this.wrapper.setProperty(property, getDateValueToSet(value, property.getType(), property)); + this.accessor.setProperty(property, getDateValueToSet(value, property.getType(), property)); } } @@ -185,7 +187,7 @@ class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory { public void setLastModifiedBy(Object value) { if (metadata.lastModifiedByProperty != null) { - this.wrapper.setProperty(metadata.lastModifiedByProperty, value); + this.accessor.setProperty(metadata.lastModifiedByProperty, value); } } @@ -199,7 +201,7 @@ class MappingAuditableBeanWrapperFactory extends AuditableBeanWrapperFactory { PersistentProperty property = metadata.lastModifiedDateProperty; if (property != null) { - this.wrapper.setProperty(property, getDateValueToSet(value, property.getType(), property)); + this.accessor.setProperty(property, getDateValueToSet(value, property.getType(), property)); } } } diff --git a/src/main/java/org/springframework/data/mapping/PersistentEntity.java b/src/main/java/org/springframework/data/mapping/PersistentEntity.java index 9ab078e43..52ad9cf9e 100644 --- a/src/main/java/org/springframework/data/mapping/PersistentEntity.java +++ b/src/main/java/org/springframework/data/mapping/PersistentEntity.java @@ -170,4 +170,13 @@ public interface PersistentEntity> { * @since 1.8 */ A findAnnotation(Class annotationType); + + /** + * Returns a {@link PersistentPropertyAccessor} to access property values of the given bean. + * + * @param bean must not be {@literal null}. + * @return + * @since 1.10 + */ + PersistentPropertyAccessor getPropertyAccessor(Object bean); } diff --git a/src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java b/src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java new file mode 100644 index 000000000..edac609e0 --- /dev/null +++ b/src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java @@ -0,0 +1,53 @@ +/* + * Copyright 2014 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 + * + * http://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.mapping; + +import org.springframework.core.convert.ConversionService; +import org.springframework.data.mapping.model.MappingException; + +/** + * Domain service to allow accessing and setting {@link PersistentProperty}s of an entity. + * + * @author Oliver Gierke + */ +public interface PersistentPropertyAccessor { + + /** + * Sets the given {@link PersistentProperty} to the given value. Will do type conversion if a + * {@link ConversionService} is configured. + * + * @param property must not be {@literal null}. + * @param value can be {@literal null}. + * @throws MappingException in case an exception occurred when setting the property value. + */ + void setProperty(PersistentProperty property, Object value); + + /** + * Returns the value of the given {@link PersistentProperty} of the underlying bean instance. + * + * @param + * @param property must not be {@literal null}. + * @return can be {@literal null}. + */ + Object getProperty(PersistentProperty property); + + /** + * Returns the underlying bean. + * + * @return will never be {@literal null}. + */ + Object getBean(); +} diff --git a/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java b/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java index 902662087..3453b31ed 100644 --- a/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java +++ b/src/main/java/org/springframework/data/mapping/context/MappingContextIsNewStrategyFactory.java @@ -17,7 +17,7 @@ package org.springframework.data.mapping.context; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; -import org.springframework.data.mapping.model.BeanWrapper; +import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.model.MappingException; import org.springframework.data.support.IsNewStrategy; import org.springframework.data.support.IsNewStrategyFactory; @@ -95,8 +95,8 @@ public class MappingContextIsNewStrategyFactory extends IsNewStrategyFactorySupp */ public boolean isNew(Object entity) { - BeanWrapper wrapper = BeanWrapper.create(entity, null); - Object propertyValue = wrapper.getProperty(property); + PersistentPropertyAccessor accessor = property.getOwner().getPropertyAccessor(entity); + Object propertyValue = accessor.getProperty(property); return decideIsNew(propertyValue); } diff --git a/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java b/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java index 5db6a9a65..6354a4a56 100644 --- a/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java +++ b/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java @@ -30,6 +30,7 @@ import org.springframework.data.mapping.Association; import org.springframework.data.mapping.AssociationHandler; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.PreferredConstructor; import org.springframework.data.mapping.PropertyHandler; import org.springframework.data.mapping.SimpleAssociationHandler; @@ -357,11 +358,23 @@ public class BasicPersistentEntity> implement return annotation; } - /* (non-Javadoc) + /* + * (non-Javadoc) * @see org.springframework.data.mapping.MutablePersistentEntity#verify() */ - public void verify() { + public void verify() {} + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentEntity#getPropertyAccessor(java.lang.Object) + */ + @Override + public PersistentPropertyAccessor getPropertyAccessor(Object bean) { + + Assert.notNull(bean, "Target bean must not be null!"); + Assert.isTrue(getType().equals(bean.getClass()), "Target bean is not of type of the persistent entity!"); + + return new BeanWrapper(bean); } /** diff --git a/src/main/java/org/springframework/data/mapping/model/BeanWrapper.java b/src/main/java/org/springframework/data/mapping/model/BeanWrapper.java index 464adb36a..3fd83a5b6 100644 --- a/src/main/java/org/springframework/data/mapping/model/BeanWrapper.java +++ b/src/main/java/org/springframework/data/mapping/model/BeanWrapper.java @@ -19,48 +19,48 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; import org.springframework.core.convert.ConversionService; +import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.util.Assert; import org.springframework.util.ReflectionUtils; /** - * Value object to allow creation of objects using the metamodel, setting and getting properties. + * Domain service to allow accessing the values of {@link PersistentProperty}s on a given bean. * * @author Oliver Gierke */ -public class BeanWrapper { +public class BeanWrapper implements PersistentPropertyAccessor { private final T bean; - private final ConversionService conversionService; /** - * Creates a new {@link BeanWrapper} for the given bean instance and {@link ConversionService}. If - * {@link ConversionService} is {@literal null} no property type conversion will take place. + * Creates a new {@link BeanWrapper} for the given bean and {@link ConversionService}.TODO: remove! * - * @param * @param bean must not be {@literal null}. * @param conversionService can be {@literal null}. * @return + * @deprecated use {@link PersistentEntity#getPropertyAccessor(Object)} instead. Will be removed in 1.10 RC1. */ + @Deprecated public static BeanWrapper create(T bean, ConversionService conversionService) { - - Assert.notNull(bean, "Wrapped instance must not be null!"); - return new BeanWrapper(bean, conversionService); - } - - private BeanWrapper(T bean, ConversionService conversionService) { - - this.bean = bean; - this.conversionService = conversionService; + return new BeanWrapper(bean); } /** - * Sets the given {@link PersistentProperty} to the given value. Will do type conversion if a - * {@link ConversionService} is configured. + * Creates a new {@link BeanWrapper} for the given bean. * - * @param property must not be {@literal null}. - * @param value can be {@literal null}. - * @throws MappingException in case an exception occurred when setting the property value. + * @param bean must not be {@literal null}. + */ + protected BeanWrapper(T bean) { + + Assert.notNull(bean, "Bean must not be null!"); + this.bean = bean; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentPropertyAccessor#setProperty(org.springframework.data.mapping.PersistentProperty, java.lang.Object) */ public void setProperty(PersistentProperty property, Object value) { @@ -72,16 +72,13 @@ public class BeanWrapper { if (!property.usePropertyAccess()) { - Object valueToSet = getPotentiallyConvertedValue(value, property.getType()); ReflectionUtils.makeAccessible(property.getField()); - ReflectionUtils.setField(property.getField(), bean, valueToSet); + ReflectionUtils.setField(property.getField(), bean, value); } else if (property.usePropertyAccess() && setter != null) { - Class[] paramTypes = setter.getParameterTypes(); - Object valueToSet = getPotentiallyConvertedValue(value, paramTypes[0]); ReflectionUtils.makeAccessible(setter); - ReflectionUtils.invokeMethod(setter, bean, valueToSet); + ReflectionUtils.invokeMethod(setter, bean, value); } } catch (IllegalStateException e) { @@ -89,12 +86,9 @@ public class BeanWrapper { } } - /** - * Returns the value of the given {@link PersistentProperty} of the underlying bean instance. - * - * @param - * @param property must not be {@literal null}. - * @return + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentPropertyAccessor#getProperty(org.springframework.data.mapping.PersistentProperty) */ public Object getProperty(PersistentProperty property) { return getProperty(property, property.getType()); @@ -109,59 +103,38 @@ public class BeanWrapper { * @return * @throws MappingException in case an exception occured when accessing the property. */ + @SuppressWarnings("unchecked") public S getProperty(PersistentProperty property, Class type) { Assert.notNull(property, "PersistentProperty must not be null!"); try { - Object obj = null; Method getter = property.getGetter(); if (!property.usePropertyAccess()) { Field field = property.getField(); ReflectionUtils.makeAccessible(field); - obj = ReflectionUtils.getField(field, bean); + return (S) ReflectionUtils.getField(field, bean); } else if (property.usePropertyAccess() && getter != null) { ReflectionUtils.makeAccessible(getter); - obj = ReflectionUtils.invokeMethod(getter, bean); + return (S) ReflectionUtils.invokeMethod(getter, bean); + } else { + return null; } - return getPotentiallyConvertedValue(obj, type); - } catch (IllegalStateException e) { throw new MappingException(String.format("Could not read property %s of %s!", property.toString(), bean.toString()), e); } } - /** - * Converts the given source value if it is not assignable to the given target type. - * - * @param source can be {@literal null}. - * @param targetType can be {@literal null}. - * @return - */ - @SuppressWarnings("unchecked") - private S getPotentiallyConvertedValue(Object source, Class targetType) { - - boolean conversionServiceAvailable = conversionService != null; - boolean conversionNeeded = source == null || !targetType.isAssignableFrom(source.getClass()); - - if (conversionServiceAvailable && conversionNeeded) { - return conversionService.convert(source, targetType); - } - - return (S) source; - } - - /** - * Returns the underlying bean instance. - * - * @return will never be {@literal null}. + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentPropertyAccessor#getBean() */ public T getBean() { return bean; diff --git a/src/main/java/org/springframework/data/mapping/model/ConvertingPropertyAccessor.java b/src/main/java/org/springframework/data/mapping/model/ConvertingPropertyAccessor.java new file mode 100644 index 000000000..c9c9cb514 --- /dev/null +++ b/src/main/java/org/springframework/data/mapping/model/ConvertingPropertyAccessor.java @@ -0,0 +1,107 @@ +/* + * Copyright 2014 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 + * + * http://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.mapping.model; + +import org.springframework.core.convert.ConversionService; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PersistentPropertyAccessor; +import org.springframework.util.Assert; + +/** + * {@link PersistentPropertyAccessor} that potentially converts the value handed to + * {@link #setProperty(PersistentProperty, Object)} to the type of the {@link PersistentProperty} using a + * {@link ConversionService}. Exposes {@link #getProperty(PersistentProperty, Class)} to allow obtaining the value of a + * property in a type the {@link ConversionService} can convert the raw type to. + * + * @author Oliver Gierke + */ +public class ConvertingPropertyAccessor implements PersistentPropertyAccessor { + + private final PersistentPropertyAccessor accessor; + private final ConversionService conversionService; + + /** + * Creates a new {@link ConvertingPropertyAccessor} for the given delegate {@link PersistentPropertyAccessor} and + * {@link ConversionService}. + * + * @param accessor must not be {@literal null}. + * @param conversionService must not be {@literal null}. + */ + public ConvertingPropertyAccessor(PersistentPropertyAccessor accessor, ConversionService conversionService) { + + Assert.notNull(accessor, "PersistentPropertyAccessor must not be null!"); + Assert.notNull(conversionService, "ConversionService must not be null!"); + + this.accessor = accessor; + this.conversionService = conversionService; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentPropertyAccessor#setProperty(org.springframework.data.mapping.PersistentProperty, java.lang.Object) + */ + @Override + public void setProperty(PersistentProperty property, Object value) { + accessor.setProperty(property, convertIfNecessary(value, property.getType())); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentPropertyAccessor#getProperty(org.springframework.data.mapping.PersistentProperty) + */ + @Override + public Object getProperty(PersistentProperty property) { + return accessor.getProperty(property); + } + + /** + * Returns the value of the given {@link PersistentProperty} converted to the given type. + * + * @param property must not be {@literal null}. + * @param targetType must not be {@literal null}. + * @return + */ + public T getProperty(PersistentProperty property, Class targetType) { + + Assert.notNull(property, "PersistentProperty must not be null!"); + Assert.notNull(targetType, "Target type must not be null!"); + + return convertIfNecessary(getProperty(property), targetType); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentPropertyAccessor#getBean() + */ + @Override + public Object getBean() { + return accessor.getBean(); + } + + /** + * Triggers the conversion of the source value into the target type unless the value already is a value of given + * target type. + * + * @param source can be {@literal null}. + * @param type must not be {@literal null}. + * @return + */ + @SuppressWarnings("unchecked") + private T convertIfNecessary(Object source, Class type) { + return (T) (source == null ? source : type.isAssignableFrom(source.getClass()) ? source : conversionService + .convert(source, type)); + } +} diff --git a/src/main/java/org/springframework/data/repository/core/support/PersistentEntityInformation.java b/src/main/java/org/springframework/data/repository/core/support/PersistentEntityInformation.java index 750182188..00c9cc3e9 100644 --- a/src/main/java/org/springframework/data/repository/core/support/PersistentEntityInformation.java +++ b/src/main/java/org/springframework/data/repository/core/support/PersistentEntityInformation.java @@ -49,7 +49,7 @@ public class PersistentEntityInformation extends Abs */ @Override public ID getId(T entity) { - return (ID) BeanWrapper.create(entity, null).getProperty(this.persistentEntity.getIdProperty()); + return (ID) persistentEntity.getPropertyAccessor(entity).getProperty(this.persistentEntity.getIdProperty()); } /* diff --git a/src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java b/src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java index ff93584c6..cd05a48b2 100644 --- a/src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java @@ -37,6 +37,7 @@ import org.springframework.data.annotation.TypeAlias; import org.springframework.data.mapping.PersistentEntity; import org.springframework.data.mapping.PersistentEntitySpec; import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.mapping.PersistentPropertyAccessor; import org.springframework.data.mapping.Person; import org.springframework.data.mapping.context.SampleMappingContext; import org.springframework.data.mapping.context.SamplePersistentProperty; @@ -169,6 +170,46 @@ public class BasicPersistentEntityUnitTests> { assertThat(entity.getPersistentProperty(CreatedDate.class), is(nullValue())); } + /** + * @see DATACMNS-596 + */ + @Test + public void returnsBeanWrapperForPropertyAccessor() { + + SampleMappingContext context = new SampleMappingContext(); + PersistentEntity entity = context.getPersistentEntity(Entity.class); + + Entity value = new Entity(); + PersistentPropertyAccessor accessor = entity.getPropertyAccessor(value); + + assertThat(accessor, is(instanceOf(BeanWrapper.class))); + assertThat(accessor.getBean(), is((Object) value)); + } + + /** + * @see DATACMNS-596 + */ + @Test(expected = IllegalArgumentException.class) + public void rejectsNullBeanForPropertyAccessor() { + + SampleMappingContext context = new SampleMappingContext(); + PersistentEntity entity = context.getPersistentEntity(Entity.class); + + entity.getPropertyAccessor(null); + } + + /** + * @see DATACMNS-596 + */ + @Test(expected = IllegalArgumentException.class) + public void rejectsNonMatchingBeanForPropertyAccessor() { + + SampleMappingContext context = new SampleMappingContext(); + PersistentEntity entity = context.getPersistentEntity(Entity.class); + + entity.getPropertyAccessor("foo"); + } + private BasicPersistentEntity createEntity(Comparator comparator) { return new BasicPersistentEntity(ClassTypeInformation.from(Person.class), comparator); } diff --git a/src/test/java/org/springframework/data/mapping/model/ConvertingPropertyAccessorUnitTests.java b/src/test/java/org/springframework/data/mapping/model/ConvertingPropertyAccessorUnitTests.java new file mode 100644 index 000000000..c1835b331 --- /dev/null +++ b/src/test/java/org/springframework/data/mapping/model/ConvertingPropertyAccessorUnitTests.java @@ -0,0 +1,149 @@ +/* + * Copyright 2014 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 + * + * http://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.mapping.model; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import org.junit.Test; +import org.springframework.core.convert.ConversionService; +import org.springframework.data.mapping.PersistentPropertyAccessor; +import org.springframework.data.mapping.context.SampleMappingContext; +import org.springframework.data.mapping.context.SamplePersistentProperty; +import org.springframework.format.support.DefaultFormattingConversionService; + +/** + * Unit tests for {@link ConvertingPropertyAccessor}. + * + * @author Oliver Gierke + */ +public class ConvertingPropertyAccessorUnitTests { + + static final ConversionService CONVERSION_SERVICE = new DefaultFormattingConversionService(); + + /** + * @see DATACMNS-596 + */ + @Test(expected = IllegalArgumentException.class) + public void rejectsNullPropertyAccessorDelegate() { + new ConvertingPropertyAccessor(null, CONVERSION_SERVICE); + } + + /** + * @see DATACMNS-596 + */ + @Test(expected = IllegalArgumentException.class) + public void rejectsNullConversionService() { + new ConvertingPropertyAccessor(new BeanWrapper(new Object()), null); + } + + /** + * @see DATACMNS-596 + */ + @Test + public void returnsBeanFromDelegate() { + + Object entity = new Entity(); + assertThat(getAccessor(entity, CONVERSION_SERVICE).getBean(), is(entity)); + } + + /** + * @see DATACMNS-596 + */ + @Test + public void convertsPropertyValueToExpectedType() { + + Entity entity = new Entity(); + entity.id = 1L; + + ConvertingPropertyAccessor accessor = getAccessor(entity, CONVERSION_SERVICE); + + assertThat(accessor.getProperty(getIdProperty(), String.class), is("1")); + } + + /** + * @see DATACMNS-596 + */ + @Test + public void doesNotInvokeConversionForNullValues() { + + ConversionService conversionService = mock(ConversionService.class); + ConvertingPropertyAccessor accessor = getAccessor(new Entity(), conversionService); + + assertThat(accessor.getProperty(getIdProperty(), Number.class), is(nullValue())); + verify(conversionService, times(0)).convert(1L, Number.class); + } + + /** + * @see DATACMNS-596 + */ + @Test + public void doesNotInvokeConversionIfTypeAlreadyMatches() { + + Entity entity = new Entity(); + entity.id = 1L; + + ConversionService conversionService = mock(ConversionService.class); + ConvertingPropertyAccessor accessor = getAccessor(entity, conversionService); + + assertThat(accessor.getProperty(getIdProperty(), Number.class), is((Number) 1L)); + verify(conversionService, times(0)).convert(1L, Number.class); + } + + /** + * @see DATACMNS-596 + */ + @Test + public void convertsValueOnSetIfTypesDontMatch() { + + Entity entity = new Entity(); + ConvertingPropertyAccessor accessor = getAccessor(entity, CONVERSION_SERVICE); + + accessor.setProperty(getIdProperty(), "1"); + + assertThat(entity.id, is(1L)); + } + + /** + * @see DATACMNS-596 + */ + @Test + public void doesNotInvokeConversionIfTypeAlreadyMatchesOnSet() { + + ConvertingPropertyAccessor accessor = getAccessor(new Entity(), mock(ConversionService.class)); + + accessor.setProperty(getIdProperty(), 1L); + verify(mock(ConversionService.class), times(0)).convert(1L, Long.class); + } + + private static ConvertingPropertyAccessor getAccessor(Object entity, ConversionService conversionService) { + + PersistentPropertyAccessor wrapper = new BeanWrapper(entity); + return new ConvertingPropertyAccessor(wrapper, conversionService); + } + + private static SamplePersistentProperty getIdProperty() { + + SampleMappingContext mappingContext = new SampleMappingContext(); + BasicPersistentEntity entity = mappingContext.getPersistentEntity(Entity.class); + return entity.getPersistentProperty("id"); + } + + static class Entity { + Long id; + } +}