Skip to content

Commit ac9f99f

Browse files
committed
Polishing of annotation model for object creators.
Move to @PersistenceCreator as canonical annotation to explicitly express constructors and methods to be used to create domain object instances from persistence operations. Removed @factorymethod as it's not needed anymore. @PersistenceConstructor is now deprecated. Renamed EntityCreatorMetadata(Support|Discoverer) to InstanceCreatorMetadata(Support|Discoverer) to avoid further manifestation of the notion of an entity in the metamodel as it's not used to only handle entities. Issue #2476.
1 parent c4a324e commit ac9f99f

25 files changed

+117
-162
lines changed

src/main/asciidoc/object-mapping.adoc

+7-7
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ This means we need two fundamental steps:
1717
Spring Data automatically tries to detect a persistent entity's constructor to be used to materialize objects of that type.
1818
The resolution algorithm works as follows:
1919

20-
1. If there is a single static factory method annotated with `@FactoryMethod` then it is used.
20+
1. If there is a single static factory method annotated with `@PersistenceCreator` then it is used.
2121
2. If there is a single constructor, it is used.
22-
3. If there are multiple constructors and exactly one is annotated with `@PersistenceConstructor`, it is used.
22+
3. If there are multiple constructors and exactly one is annotated with `@PersistenceCreator`, it is used.
2323
4. If there's a no-argument constructor, it is used.
2424
Other constructors will be ignored.
2525

@@ -205,9 +205,9 @@ Even if the intent is that the calculation should be preferred, it's important t
205205
<4> The `comment` property is mutable is populated by setting its field directly.
206206
<5> The `remarks` properties are mutable and populated by setting the `comment` field directly or by invoking the setter method for
207207
<6> The class exposes a factory method and a constructor for object creation.
208-
The core idea here is to use factory methods instead of additional constructors to avoid the need for constructor disambiguation through `@PersistenceConstructor`.
208+
The core idea here is to use factory methods instead of additional constructors to avoid the need for constructor disambiguation through `@PersistenceCreator`.
209209
Instead, defaulting of properties is handled within the factory method.
210-
If you want Spring Data to use the factory method for object instantiation, annotate it with `@FactoryMethod`.
210+
If you want Spring Data to use the factory method for object instantiation, annotate it with `@PersistenceCreator`.
211211

212212
[[mapping.general-recommendations]]
213213
== General recommendations
@@ -217,7 +217,7 @@ Also, this avoids your domain objects to be littered with setter methods that al
217217
If you need those, prefer to make them package protected so that they can only be invoked by a limited amount of co-located types.
218218
Constructor-only materialization is up to 30% faster than properties population.
219219
* _Provide an all-args constructor_ -- Even if you cannot or don't want to model your entities as immutable values, there's still value in providing a constructor that takes all properties of the entity as arguments, including the mutable ones, as this allows the object mapping to skip the property population for optimal performance.
220-
* _Use factory methods instead of overloaded constructors to avoid ``@PersistenceConstructor``_ -- With an all-argument constructor needed for optimal performance, we usually want to expose more application use case specific constructors that omit things like auto-generated identifiers etc.
220+
* _Use factory methods instead of overloaded constructors to avoid ``@PersistenceCreator``_ -- With an all-argument constructor needed for optimal performance, we usually want to expose more application use case specific constructors that omit things like auto-generated identifiers etc.
221221
It's an established pattern to rather use static factory methods to expose these variants of the all-args constructor.
222222
* _Make sure you adhere to the constraints that allow the generated instantiator and property accessor classes to be used_ --
223223
* _For identifiers to be generated, still use a final field in combination with an all-arguments persistence constructor (preferred) or a `with…` method_ --
@@ -307,14 +307,14 @@ data class Person(val id: String, val name: String)
307307
----
308308
====
309309

310-
The class above compiles to a typical class with an explicit constructor.We can customize this class by adding another constructor and annotate it with `@PersistenceConstructor` to indicate a constructor preference:
310+
The class above compiles to a typical class with an explicit constructor.We can customize this class by adding another constructor and annotate it with `@PersistenceCreator` to indicate a constructor preference:
311311

312312
====
313313
[source,kotlin]
314314
----
315315
data class Person(var id: String, val name: String) {
316316
317-
@PersistenceConstructor
317+
@PersistenceCreator
318318
constructor(id: String) : this(id, "unknown")
319319
}
320320
----

src/main/java/org/springframework/data/annotation/FactoryMethod.java

-33
This file was deleted.

src/main/java/org/springframework/data/annotation/PersistenceConstructor.java

+5-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,11 @@
2525
*
2626
* @author Jon Brisbin
2727
* @author Mark Paluch
28+
* @author Oliver Drotbohm
29+
* @deprecated in favor of {@link PersistenceCreator} since 3.0, to be removed in 3.1
2830
*/
2931
@Retention(RetentionPolicy.RUNTIME)
3032
@Target({ ElementType.CONSTRUCTOR, ElementType.ANNOTATION_TYPE })
31-
@EntityCreatorAnnotation
32-
public @interface PersistenceConstructor {
33-
}
33+
@PersistenceCreator
34+
@Deprecated
35+
public @interface PersistenceConstructor {}

src/main/java/org/springframework/data/annotation/EntityCreatorAnnotation.java renamed to src/main/java/org/springframework/data/annotation/PersistenceCreator.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2021 the original author or authors.
2+
* Copyright 2011-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,9 +24,9 @@
2424
* Marker annotation to declare a constructor or factory method annotation as factory/preferred constructor annotation.
2525
*
2626
* @author Mark Paluch
27+
* @author Oliver Drotbohm
2728
* @since 3.0
2829
*/
2930
@Retention(RetentionPolicy.RUNTIME)
30-
@Target({ ElementType.ANNOTATION_TYPE })
31-
public @interface EntityCreatorAnnotation {
32-
}
31+
@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
32+
public @interface PersistenceCreator {}

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* @author Mark Paluch
2727
* @since 3.0
2828
*/
29-
public final class FactoryMethod<T, P extends PersistentProperty<P>> extends EntityCreatorMetadataSupport<T, P> {
29+
public final class FactoryMethod<T, P extends PersistentProperty<P>> extends InstanceCreatorMetadataSupport<T, P> {
3030

3131
/**
3232
* Creates a new {@link FactoryMethod} from the given {@link Constructor} and {@link Parameter}s.
@@ -49,5 +49,4 @@ public FactoryMethod(Method factoryMethod, Parameter<Object, P>... parameters) {
4949
public Method getFactoryMethod() {
5050
return (Method) getExecutable();
5151
}
52-
5352
}

src/main/java/org/springframework/data/mapping/EntityCreatorMetadata.java renamed to src/main/java/org/springframework/data/mapping/InstanceCreatorMetadata.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 the original author or authors.
2+
* Copyright 2021-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -18,12 +18,13 @@
1818
import java.util.List;
1919

2020
/**
21-
* Metadata describing a mechanism to create an entity instance.
21+
* Metadata describing a mechanism to create instances of persistent types.
2222
*
2323
* @author Mark Paluch
24+
* @author Oliver Drotbohm
2425
* @since 3.0
2526
*/
26-
public interface EntityCreatorMetadata<P extends PersistentProperty<P>> {
27+
public interface InstanceCreatorMetadata<P extends PersistentProperty<P>> {
2728

2829
/**
2930
* Check whether the given {@link PersistentProperty} is being used as creator parameter.

src/main/java/org/springframework/data/mapping/EntityCreatorMetadataSupport.java renamed to src/main/java/org/springframework/data/mapping/InstanceCreatorMetadataSupport.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,23 @@
2828
* persistent data to objects.
2929
*
3030
* @author Mark Paluch
31+
* @author Oliver Drotbohm
3132
* @since 3.0
3233
*/
33-
class EntityCreatorMetadataSupport<T, P extends PersistentProperty<P>> implements EntityCreatorMetadata<P> {
34+
class InstanceCreatorMetadataSupport<T, P extends PersistentProperty<P>> implements InstanceCreatorMetadata<P> {
3435

3536
private final Executable executable;
3637
private final List<Parameter<Object, P>> parameters;
3738
private final Map<PersistentProperty<?>, Boolean> isPropertyParameterCache = new ConcurrentHashMap<>();
3839

3940
/**
40-
* Creates a new {@link EntityCreatorMetadataSupport} from the given {@link Executable} and {@link Parameter}s.
41+
* Creates a new {@link InstanceCreatorMetadataSupport} from the given {@link Executable} and {@link Parameter}s.
4142
*
4243
* @param executable must not be {@literal null}.
4344
* @param parameters must not be {@literal null}.
4445
*/
4546
@SafeVarargs
46-
public EntityCreatorMetadataSupport(Executable executable, Parameter<Object, P>... parameters) {
47+
public InstanceCreatorMetadataSupport(Executable executable, Parameter<Object, P>... parameters) {
4748

4849
Assert.notNull(executable, "Executable must not be null!");
4950
Assert.notNull(parameters, "Parameters must not be null!");
@@ -72,7 +73,7 @@ public List<Parameter<Object, P>> getParameters() {
7273

7374
/**
7475
* Returns whether the given {@link PersistentProperty} is referenced in a creator argument of the
75-
* {@link PersistentEntity} backing this {@link EntityCreatorMetadataSupport}.
76+
* {@link PersistentEntity} backing this {@link InstanceCreatorMetadataSupport}.
7677
* <p>
7778
* Results of this call are cached and reused on the next invocation. Calling this method for a
7879
* {@link PersistentProperty} that was not yet added to its owning {@link PersistentEntity} will capture that state

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,14 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
4949
* indicates that the instantiation of the object of that persistent entity is done through either a customer
5050
* {@link org.springframework.data.mapping.model.EntityInstantiator} or handled by custom conversion
5151
* mechanisms entirely.
52-
* @deprecated since 3.0, use {@link #getEntityCreator()}.
52+
* @deprecated since 3.0, use {@link #getInstanceCreatorMetadata()}.
5353
*/
5454
@Nullable
5555
@Deprecated
5656
PreferredConstructor<T, P> getPersistenceConstructor();
5757

5858
/**
59-
* Returns the {@link EntityCreatorMetadata} to be used to instantiate objects of this {@link PersistentEntity}.
59+
* Returns the {@link InstanceCreatorMetadata} to be used to instantiate objects of this {@link PersistentEntity}.
6060
*
6161
* @return {@literal null} in case no suitable creation mechanism for automatic construction can be found. This
6262
* usually indicates that the instantiation of the object of that persistent entity is done through either a
@@ -65,7 +65,7 @@ public interface PersistentEntity<T, P extends PersistentProperty<P>> extends It
6565
* @since 3.0
6666
*/
6767
@Nullable
68-
EntityCreatorMetadata<P> getEntityCreator();
68+
InstanceCreatorMetadata<P> getInstanceCreatorMetadata();
6969

7070
/**
7171
* Returns whether the given {@link PersistentProperty} is referred to by a constructor argument of the

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
* @author Myeonghyeon Lee
3636
* @author Xeno Amess
3737
*/
38-
public final class PreferredConstructor<T, P extends PersistentProperty<P>> extends EntityCreatorMetadataSupport<T, P> {
38+
public final class PreferredConstructor<T, P extends PersistentProperty<P>> extends InstanceCreatorMetadataSupport<T, P> {
3939

4040
private final List<Parameter<Object, P>> parameters;
4141

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

+6-13
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,7 @@
1717

1818
import java.io.Serializable;
1919
import java.lang.annotation.Annotation;
20-
import java.util.ArrayList;
21-
import java.util.Comparator;
22-
import java.util.HashMap;
23-
import java.util.HashSet;
24-
import java.util.Iterator;
25-
import java.util.List;
26-
import java.util.Map;
27-
import java.util.Optional;
28-
import java.util.Set;
29-
import java.util.TreeSet;
20+
import java.util.*;
3021
import java.util.stream.Collectors;
3122

3223
import org.springframework.core.annotation.AnnotatedElementUtils;
@@ -63,7 +54,7 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
6354

6455
private static final String TYPE_MISMATCH = "Target bean of type %s is not of type of the persistent entity (%s)!";
6556

66-
private final @Nullable EntityCreatorMetadata<P> creator;
57+
private final @Nullable InstanceCreatorMetadata<P> creator;
6758
private final TypeInformation<T> information;
6859
private final List<P> properties;
6960
private final List<P> persistentPropertiesCache;
@@ -109,7 +100,7 @@ public BasicPersistentEntity(TypeInformation<T> information, @Nullable Comparato
109100
this.properties = new ArrayList<>();
110101
this.persistentPropertiesCache = new ArrayList<>();
111102
this.comparator = comparator;
112-
this.creator = EntityCreatorMetadataDiscoverer.discover(this);
103+
this.creator = InstanceCreatorMetadataDiscoverer.discover(this);
113104
this.associations = comparator == null ? new HashSet<>() : new TreeSet<>(new AssociationComparator<>(comparator));
114105

115106
this.propertyCache = new HashMap<>(16, 1f);
@@ -129,12 +120,14 @@ public BasicPersistentEntity(TypeInformation<T> information, @Nullable Comparato
129120

130121
@Nullable
131122
@Override
123+
@SuppressWarnings("unchecked")
132124
public PreferredConstructor<T, P> getPersistenceConstructor() {
133125
return creator instanceof PreferredConstructor ? (PreferredConstructor<T, P>) creator : null;
134126
}
135127

128+
@Nullable
136129
@Override
137-
public EntityCreatorMetadata<P> getEntityCreator() {
130+
public InstanceCreatorMetadata<P> getInstanceCreatorMetadata() {
138131
return creator;
139132
}
140133

0 commit comments

Comments
 (0)