From d1ebfcfc5e6746aa57528d418a167a2f1c1d0a69 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Mon, 18 Jun 2018 16:49:01 +0200 Subject: [PATCH] DATACMNS-1322 - Add support for immutable entities in PersistentPropertyAccessor.setProperty(PersistentPropertyPath, Object). As setting a PersistentProperty can actually change the object that the property is changed on, we now recursively traverse property paths up as longs as the setting of the property results in the bean not being replaced. --- .../mapping/PersistentPropertyAccessor.java | 14 ++++++- .../PersistentPropertyAccessorUnitTests.java | 38 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java b/src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java index f8569df5e..ece298a9f 100644 --- a/src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java +++ b/src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java @@ -71,9 +71,21 @@ public interface PersistentPropertyAccessor { getBean().getClass().getName())); } - PersistentPropertyAccessor accessor = leafProperty.getOwner().getPropertyAccessor(parent); + PersistentPropertyAccessor accessor = parent == getBean() // + ? this // + : leafProperty.getOwner().getPropertyAccessor(parent); accessor.setProperty(leafProperty, value); + + if (parentPath.isEmpty()) { + return; + } + + Object bean = accessor.getBean(); + + if (bean != parent) { + setProperty(parentPath, bean); + } } /** diff --git a/src/test/java/org/springframework/data/mapping/PersistentPropertyAccessorUnitTests.java b/src/test/java/org/springframework/data/mapping/PersistentPropertyAccessorUnitTests.java index 18a2aff49..f426ed4f3 100644 --- a/src/test/java/org/springframework/data/mapping/PersistentPropertyAccessorUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/PersistentPropertyAccessorUnitTests.java @@ -17,12 +17,15 @@ package org.springframework.data.mapping; import static org.assertj.core.api.Assertions.*; +import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Data; import lombok.Value; +import lombok.experimental.Wither; import org.junit.Test; import org.springframework.data.mapping.context.SampleMappingContext; +import org.springframework.data.mapping.context.SamplePersistentProperty; /** * @author Oliver Gierke @@ -90,6 +93,27 @@ public class PersistentPropertyAccessorUnitTests { .isThrownBy(() -> accessor.setProperty(path, "Oliver August")); } + @Test // DATACMNS-1322 + public void correctlyReplacesObjectInstancesWhenSettingPropertyPathOnImmutableObjects() { + + PersistentEntity entity = context.getPersistentEntity(Outer.class); + PersistentPropertyPath path = context.getPersistentPropertyPath("immutable.value", + entity.getType()); + + NestedImmutable immutable = new NestedImmutable("foo"); + Outer outer = new Outer(immutable); + + PersistentPropertyAccessor accessor = entity.getPropertyAccessor(outer); + accessor.setProperty(path, "bar"); + + Object result = accessor.getBean(); + + assertThat(result).isInstanceOfSatisfying(Outer.class, it -> { + assertThat(it.immutable).isNotSameAs(immutable); + assertThat(it).isNotSameAs(outer); + }); + } + @Value static class Order { Customer customer; @@ -100,4 +124,18 @@ public class PersistentPropertyAccessorUnitTests { static class Customer { String firstname; } + + // DATACMNS-1322 + + @Value + @Wither(AccessLevel.PACKAGE) + static class NestedImmutable { + String value; + } + + @Value + @Wither(AccessLevel.PACKAGE) + static class Outer { + NestedImmutable immutable; + } }