Skip to content

Commit 1e38b4c

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 ec0c0d3 commit 1e38b4c

25 files changed

+117
-149
lines changed

src/main/asciidoc/object-mapping.adoc

Lines changed: 7 additions & 7 deletions
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

Lines changed: 0 additions & 33 deletions
This file was deleted.

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

Lines changed: 5 additions & 3 deletions
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

Lines changed: 5 additions & 5 deletions
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-
* @since 3.0
27+
* @author Oliver Drotbohm
28+
* @since 2.7
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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
* Value object to encapsulate the factory method to be used when mapping persistent data to objects.
2525
*
2626
* @author Mark Paluch
27-
* @since 3.0
27+
* @since 2.7
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

Lines changed: 5 additions & 4 deletions
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-
* @since 3.0
24+
* @author Oliver Drotbohm
25+
* @since 2.7
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

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,23 @@
2828
* persistent data to objects.
2929
*
3030
* @author Mark Paluch
31-
* @since 3.0
31+
* @author Oliver Drotbohm
32+
* @since 2.7
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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,23 +49,23 @@ 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 2.7, 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
6363
* customer {@link org.springframework.data.mapping.model.EntityInstantiator} or handled by custom conversion
6464
* mechanisms entirely.
65-
* @since 3.0
65+
* @since 2.7
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
6363

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

66-
private final @Nullable EntityCreatorMetadata<P> creator;
66+
private final @Nullable InstanceCreatorMetadata<P> creator;
6767
private final TypeInformation<T> information;
6868
private final List<P> properties;
6969
private final List<P> persistentPropertiesCache;
@@ -109,7 +109,7 @@ public BasicPersistentEntity(TypeInformation<T> information, @Nullable Comparato
109109
this.properties = new ArrayList<>();
110110
this.persistentPropertiesCache = new ArrayList<>();
111111
this.comparator = comparator;
112-
this.creator = EntityCreatorMetadataDiscoverer.discover(this);
112+
this.creator = InstanceCreatorMetadataDiscoverer.discover(this);
113113
this.associations = comparator == null ? new HashSet<>() : new TreeSet<>(new AssociationComparator<>(comparator));
114114

115115
this.propertyCache = new HashMap<>(16, 1f);
@@ -138,7 +138,7 @@ public PreferredConstructor<T, P> getPersistenceConstructor() {
138138
}
139139

140140
@Override
141-
public EntityCreatorMetadata<P> getEntityCreator() {
141+
public InstanceCreatorMetadata<P> getInstanceCreatorMetadata() {
142142
return creator;
143143
}
144144

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

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
import org.springframework.beans.BeanInstantiationException;
3535
import org.springframework.cglib.core.ReflectUtils;
3636
import org.springframework.core.NativeDetector;
37-
import org.springframework.data.mapping.EntityCreatorMetadata;
3837
import org.springframework.data.mapping.FactoryMethod;
38+
import org.springframework.data.mapping.InstanceCreatorMetadata;
3939
import org.springframework.data.mapping.Parameter;
4040
import org.springframework.data.mapping.PersistentEntity;
4141
import org.springframework.data.mapping.PersistentProperty;
@@ -164,7 +164,7 @@ private EntityInstantiator createEntityInstantiator(PersistentEntity<?, ?> entit
164164
*/
165165
protected EntityInstantiator doCreateEntityInstantiator(PersistentEntity<?, ?> entity) {
166166
return new EntityInstantiatorAdapter(
167-
createObjectInstantiator(entity, entity.getEntityCreator()));
167+
createObjectInstantiator(entity, entity.getInstanceCreatorMetadata()));
168168
}
169169

170170
/**
@@ -192,7 +192,7 @@ boolean shouldUseReflectionEntityInstantiator(PersistentEntity<?, ?> entity) {
192192
return true;
193193
}
194194

195-
EntityCreatorMetadata<?> entityCreator = entity.getEntityCreator();
195+
InstanceCreatorMetadata<?> entityCreator = entity.getInstanceCreatorMetadata();
196196

197197
if (entityCreator == null) {
198198
return true;
@@ -236,15 +236,15 @@ static Object[] allocateArguments(int argumentCount) {
236236

237237
/**
238238
* Creates a dynamically generated {@link ObjectInstantiator} for the given {@link PersistentEntity} and
239-
* {@link EntityCreatorMetadata}. There will always be exactly one {@link ObjectInstantiator} instance per
239+
* {@link InstanceCreatorMetadata}. There will always be exactly one {@link ObjectInstantiator} instance per
240240
* {@link PersistentEntity}.
241241
*
242242
* @param entity
243243
* @param constructor
244244
* @return
245245
*/
246246
ObjectInstantiator createObjectInstantiator(PersistentEntity<?, ?> entity,
247-
@Nullable EntityCreatorMetadata<?> constructor) {
247+
@Nullable InstanceCreatorMetadata<?> constructor) {
248248

249249
try {
250250
return (ObjectInstantiator) this.generator.generateCustomInstantiatorClass(entity, constructor).newInstance();
@@ -282,7 +282,7 @@ public EntityInstantiatorAdapter(ObjectInstantiator instantiator) {
282282
public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
283283
ParameterValueProvider<P> provider) {
284284

285-
Object[] params = extractInvocationArguments(entity.getEntityCreator(), provider);
285+
Object[] params = extractInvocationArguments(entity.getInstanceCreatorMetadata(), provider);
286286

287287
try {
288288
return (T) instantiator.newInstance(params);
@@ -300,7 +300,7 @@ public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentPrope
300300
* @return
301301
*/
302302
static <P extends PersistentProperty<P>, T> Object[] extractInvocationArguments(
303-
@Nullable EntityCreatorMetadata<P> constructor, ParameterValueProvider<P> provider) {
303+
@Nullable InstanceCreatorMetadata<P> constructor, ParameterValueProvider<P> provider) {
304304

305305
if (constructor == null || !constructor.hasParameters()) {
306306
return allocateArguments(0);
@@ -353,7 +353,7 @@ public static EntityInstantiator create(Class<?> typeToCreate) {
353353
public <T, E extends PersistentEntity<? extends T, P>, P extends PersistentProperty<P>> T createInstance(E entity,
354354
ParameterValueProvider<P> provider) {
355355

356-
Object[] params = extractInvocationArguments(entity.getEntityCreator(), provider);
356+
Object[] params = extractInvocationArguments(entity.getInstanceCreatorMetadata(), provider);
357357

358358
throw new MappingInstantiationException(entity, Arrays.asList(params),
359359
new BeanInstantiationException(typeToCreate, "Class is abstract"));
@@ -417,7 +417,7 @@ static class ObjectInstantiatorClassGenerator {
417417
* @return
418418
*/
419419
public Class<?> generateCustomInstantiatorClass(PersistentEntity<?, ?> entity,
420-
@Nullable EntityCreatorMetadata<?> constructor) {
420+
@Nullable InstanceCreatorMetadata<?> constructor) {
421421

422422
String className = generateClassName(entity);
423423
Class<?> type = entity.getType();
@@ -458,7 +458,7 @@ private String generateClassName(PersistentEntity<?, ?> entity) {
458458
* @return
459459
*/
460460
public byte[] generateBytecode(String internalClassName, PersistentEntity<?, ?> entity,
461-
@Nullable EntityCreatorMetadata<?> entityCreator) {
461+
@Nullable InstanceCreatorMetadata<?> entityCreator) {
462462

463463
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
464464

@@ -493,7 +493,7 @@ private void visitDefaultConstructor(ClassWriter cw) {
493493
* @param entityCreator
494494
*/
495495
private void visitCreateMethod(ClassWriter cw, PersistentEntity<?, ?> entity,
496-
@Nullable EntityCreatorMetadata<?> entityCreator) {
496+
@Nullable InstanceCreatorMetadata<?> entityCreator) {
497497

498498
String entityTypeResourcePath = Type.getInternalName(entity.getType());
499499

0 commit comments

Comments
 (0)