Skip to content

Commit d1ebfcf

Browse files
committed
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.
1 parent 6cb3587 commit d1ebfcf

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

src/main/java/org/springframework/data/mapping/PersistentPropertyAccessor.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,21 @@ default void setProperty(PersistentPropertyPath<? extends PersistentProperty<?>>
7171
getBean().getClass().getName()));
7272
}
7373

74-
PersistentPropertyAccessor accessor = leafProperty.getOwner().getPropertyAccessor(parent);
74+
PersistentPropertyAccessor accessor = parent == getBean() //
75+
? this //
76+
: leafProperty.getOwner().getPropertyAccessor(parent);
7577

7678
accessor.setProperty(leafProperty, value);
79+
80+
if (parentPath.isEmpty()) {
81+
return;
82+
}
83+
84+
Object bean = accessor.getBean();
85+
86+
if (bean != parent) {
87+
setProperty(parentPath, bean);
88+
}
7789
}
7890

7991
/**

src/test/java/org/springframework/data/mapping/PersistentPropertyAccessorUnitTests.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717

1818
import static org.assertj.core.api.Assertions.*;
1919

20+
import lombok.AccessLevel;
2021
import lombok.AllArgsConstructor;
2122
import lombok.Data;
2223
import lombok.Value;
24+
import lombok.experimental.Wither;
2325

2426
import org.junit.Test;
2527
import org.springframework.data.mapping.context.SampleMappingContext;
28+
import org.springframework.data.mapping.context.SamplePersistentProperty;
2629

2730
/**
2831
* @author Oliver Gierke
@@ -90,6 +93,27 @@ public void rejectsIntermediateNullValuesForWrite() {
9093
.isThrownBy(() -> accessor.setProperty(path, "Oliver August"));
9194
}
9295

96+
@Test // DATACMNS-1322
97+
public void correctlyReplacesObjectInstancesWhenSettingPropertyPathOnImmutableObjects() {
98+
99+
PersistentEntity<Object, SamplePersistentProperty> entity = context.getPersistentEntity(Outer.class);
100+
PersistentPropertyPath<SamplePersistentProperty> path = context.getPersistentPropertyPath("immutable.value",
101+
entity.getType());
102+
103+
NestedImmutable immutable = new NestedImmutable("foo");
104+
Outer outer = new Outer(immutable);
105+
106+
PersistentPropertyAccessor accessor = entity.getPropertyAccessor(outer);
107+
accessor.setProperty(path, "bar");
108+
109+
Object result = accessor.getBean();
110+
111+
assertThat(result).isInstanceOfSatisfying(Outer.class, it -> {
112+
assertThat(it.immutable).isNotSameAs(immutable);
113+
assertThat(it).isNotSameAs(outer);
114+
});
115+
}
116+
93117
@Value
94118
static class Order {
95119
Customer customer;
@@ -100,4 +124,18 @@ static class Order {
100124
static class Customer {
101125
String firstname;
102126
}
127+
128+
// DATACMNS-1322
129+
130+
@Value
131+
@Wither(AccessLevel.PACKAGE)
132+
static class NestedImmutable {
133+
String value;
134+
}
135+
136+
@Value
137+
@Wither(AccessLevel.PACKAGE)
138+
static class Outer {
139+
NestedImmutable immutable;
140+
}
103141
}

0 commit comments

Comments
 (0)