diff --git a/src/main/java/org/springframework/data/annotation/Immutable.java b/src/main/java/org/springframework/data/annotation/Immutable.java new file mode 100644 index 000000000..44f8aa809 --- /dev/null +++ b/src/main/java/org/springframework/data/annotation/Immutable.java @@ -0,0 +1,32 @@ +/* + * Copyright 2018 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.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation for persistent entities to indicate the class is designed in immutable way. + * + * @author Oliver Gierke + * @since 2.1 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Immutable { +} diff --git a/src/main/java/org/springframework/data/mapping/PersistentEntity.java b/src/main/java/org/springframework/data/mapping/PersistentEntity.java index 0773203bb..a919e39fd 100644 --- a/src/main/java/org/springframework/data/mapping/PersistentEntity.java +++ b/src/main/java/org/springframework/data/mapping/PersistentEntity.java @@ -18,6 +18,7 @@ package org.springframework.data.mapping; import java.lang.annotation.Annotation; import java.util.Iterator; +import org.springframework.data.annotation.Immutable; import org.springframework.data.convert.EntityInstantiator; import org.springframework.data.util.TypeInformation; import org.springframework.lang.Nullable; @@ -305,4 +306,14 @@ public interface PersistentEntity> extends It * @return whether the given bean is considered a new instance. */ boolean isNew(Object bean); + + /** + * Returns whether the entity is considered immutable, i.e. clients shouldn't attempt to change instances via the + * {@link PersistentPropertyAccessor} obtained via {@link #getPropertyAccessor(Object)}. + * + * @return + * @see Immutable + * @since 2.1 + */ + boolean isImmutable(); } 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 d005ce2f1..e790d8e0a 100644 --- a/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java +++ b/src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java @@ -33,6 +33,7 @@ import java.util.TreeSet; import java.util.stream.Collectors; import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.data.annotation.Immutable; import org.springframework.data.annotation.TypeAlias; import org.springframework.data.domain.Persistable; import org.springframework.data.mapping.Alias; @@ -93,6 +94,7 @@ public class BasicPersistentEntity> implement private final Lazy typeAlias; private final Lazy isNewStrategy; + private final Lazy isImmutable; /** * Creates a new {@link BasicPersistentEntity} from the given {@link TypeInformation}. @@ -130,7 +132,7 @@ public class BasicPersistentEntity> implement this.isNewStrategy = Lazy.of(() -> Persistable.class.isAssignableFrom(information.getType()) // ? PersistableIsNewStrategy.INSTANCE : PersistentEntityIsNewStrategy.of(this)); - + this.isImmutable = Lazy.of(() -> isAnnotationPresent(Immutable.class)); } /* @@ -496,6 +498,15 @@ public class BasicPersistentEntity> implement return isNewStrategy.get().isNew(bean); } + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.PersistentEntity#isImmutable() + */ + @Override + public boolean isImmutable() { + return isImmutable.get(); + } + /* * (non-Javadoc) * @see java.lang.Iterable#iterator() 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 51073570f..632b765c1 100755 --- a/src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java +++ b/src/test/java/org/springframework/data/mapping/model/BasicPersistentEntityUnitTests.java @@ -41,6 +41,7 @@ import org.springframework.data.annotation.AccessType; import org.springframework.data.annotation.AccessType.Type; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.Immutable; import org.springframework.data.annotation.LastModifiedBy; import org.springframework.data.annotation.Persistent; import org.springframework.data.annotation.TypeAlias; @@ -347,6 +348,13 @@ public class BasicPersistentEntityUnitTests> { assertThat(entity.getIdentifierAccessor(new PersistableEntity()).getRequiredIdentifier()).isEqualTo(4711L); } + @Test // DATACMNS-1322 + public void detectsImmutableEntity() { + + assertThat(createEntity(SomeValue.class).isImmutable()).isTrue(); + assertThat(createEntity(Entity.class).isImmutable()).isFalse(); + } + private BasicPersistentEntity createEntity(Class type) { return createEntity(type, null); } @@ -416,4 +424,7 @@ public class BasicPersistentEntityUnitTests> { return false; } } + + @Immutable + static class SomeValue {} }