Skip to content

Commit 3c2522d

Browse files
committed
DATACMNS-1322 - Added @immutable to allow users to express a persistent entity to be considered immutable.
This is needed for downstream projects that attempt to merge persistent entity instances and previously didn't have a chance to detect that an object had to be set as-is instead of being merged recursively.
1 parent 1f4d494 commit 3c2522d

File tree

4 files changed

+66
-1
lines changed

4 files changed

+66
-1
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.annotation;
17+
18+
import java.lang.annotation.ElementType;
19+
import java.lang.annotation.Retention;
20+
import java.lang.annotation.RetentionPolicy;
21+
import java.lang.annotation.Target;
22+
23+
/**
24+
* Annotation for persistent entities to indicate the class is designed in immutable way.
25+
*
26+
* @author Oliver Gierke
27+
* @since 2.1
28+
*/
29+
@Target(ElementType.TYPE)
30+
@Retention(RetentionPolicy.RUNTIME)
31+
public @interface Immutable {
32+
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.lang.annotation.Annotation;
1919
import java.util.Iterator;
2020

21+
import org.springframework.data.annotation.Immutable;
2122
import org.springframework.data.convert.EntityInstantiator;
2223
import org.springframework.data.util.TypeInformation;
2324
import org.springframework.lang.Nullable;
@@ -305,4 +306,14 @@ default <A extends Annotation> A getRequiredAnnotation(Class<A> annotationType)
305306
* @return whether the given bean is considered a new instance.
306307
*/
307308
boolean isNew(Object bean);
309+
310+
/**
311+
* Returns whether the entity is considered immutable, i.e. clients shouldn't attempt to change instances via the
312+
* {@link PersistentPropertyAccessor} obtained via {@link #getPropertyAccessor(Object)}.
313+
*
314+
* @return
315+
* @see Immutable
316+
* @since 2.1
317+
*/
318+
boolean isImmutable();
308319
}

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import java.util.stream.Collectors;
3434

3535
import org.springframework.core.annotation.AnnotatedElementUtils;
36+
import org.springframework.data.annotation.Immutable;
3637
import org.springframework.data.annotation.TypeAlias;
3738
import org.springframework.data.domain.Persistable;
3839
import org.springframework.data.mapping.Alias;
@@ -93,6 +94,7 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
9394

9495
private final Lazy<Alias> typeAlias;
9596
private final Lazy<IsNewStrategy> isNewStrategy;
97+
private final Lazy<Boolean> isImmutable;
9698

9799
/**
98100
* Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}.
@@ -130,7 +132,7 @@ public BasicPersistentEntity(TypeInformation<T> information, @Nullable Comparato
130132
this.isNewStrategy = Lazy.of(() -> Persistable.class.isAssignableFrom(information.getType()) //
131133
? PersistableIsNewStrategy.INSTANCE
132134
: PersistentEntityIsNewStrategy.of(this));
133-
135+
this.isImmutable = Lazy.of(() -> isAnnotationPresent(Immutable.class));
134136
}
135137

136138
/*
@@ -496,6 +498,15 @@ public boolean isNew(Object bean) {
496498
return isNewStrategy.get().isNew(bean);
497499
}
498500

501+
/*
502+
* (non-Javadoc)
503+
* @see org.springframework.data.mapping.PersistentEntity#isImmutable()
504+
*/
505+
@Override
506+
public boolean isImmutable() {
507+
return isImmutable.get();
508+
}
509+
499510
/*
500511
* (non-Javadoc)
501512
* @see java.lang.Iterable#iterator()

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.springframework.data.annotation.AccessType.Type;
4242
import org.springframework.data.annotation.CreatedBy;
4343
import org.springframework.data.annotation.CreatedDate;
44+
import org.springframework.data.annotation.Immutable;
4445
import org.springframework.data.annotation.LastModifiedBy;
4546
import org.springframework.data.annotation.Persistent;
4647
import org.springframework.data.annotation.TypeAlias;
@@ -347,6 +348,13 @@ public void supportsPersistableViaIdentifierAccessor() {
347348
assertThat(entity.getIdentifierAccessor(new PersistableEntity()).getRequiredIdentifier()).isEqualTo(4711L);
348349
}
349350

351+
@Test // DATACMNS-1322
352+
public void detectsImmutableEntity() {
353+
354+
assertThat(createEntity(SomeValue.class).isImmutable()).isTrue();
355+
assertThat(createEntity(Entity.class).isImmutable()).isFalse();
356+
}
357+
350358
private <S> BasicPersistentEntity<S, T> createEntity(Class<S> type) {
351359
return createEntity(type, null);
352360
}
@@ -416,4 +424,7 @@ public boolean isNew() {
416424
return false;
417425
}
418426
}
427+
428+
@Immutable
429+
static class SomeValue {}
419430
}

0 commit comments

Comments
 (0)