15
15
*/
16
16
package org .springframework .data .mapping .context ;
17
17
18
+ import java .util .ArrayList ;
18
19
import java .util .Collection ;
19
20
import java .util .Collections ;
20
21
import java .util .Iterator ;
23
24
import java .util .function .Consumer ;
24
25
25
26
import org .springframework .data .mapping .PropertyPath ;
27
+ import org .springframework .data .projection .ProjectionInformation ;
26
28
import org .springframework .data .util .ClassTypeInformation ;
27
29
import org .springframework .data .util .Streamable ;
28
30
import org .springframework .data .util .TypeInformation ;
34
36
*
35
37
* @param <M> the mapped type acting as view onto the domain type.
36
38
* @param <D> the domain type.
39
+ * @author Mark Paluch
40
+ * @author Christoph Strobl
37
41
* @since 2.7
38
42
*/
39
43
public class EntityProjection <M , D > implements Streamable <EntityProjection .PropertyProjection <?, ?>> {
@@ -42,61 +46,63 @@ public class EntityProjection<M, D> implements Streamable<EntityProjection.Prope
42
46
private final TypeInformation <D > domainType ;
43
47
private final List <PropertyProjection <?, ?>> properties ;
44
48
private final boolean projection ;
45
- private final boolean closedProjection ;
49
+ private final ProjectionType projectionType ;
46
50
47
51
EntityProjection (TypeInformation <M > mappedType , TypeInformation <D > domainType ,
48
- List <PropertyProjection <?, ?>> properties , boolean projection , boolean closedProjection ) {
52
+ List <PropertyProjection <?, ?>> properties , boolean projection , ProjectionType projectionType ) {
49
53
this .mappedType = mappedType ;
50
54
this .domainType = domainType ;
51
- this .properties = properties ;
55
+ this .properties = new ArrayList <>( properties ) ;
52
56
this .projection = projection ;
53
- this .closedProjection = closedProjection ;
57
+ this .projectionType = projectionType ;
54
58
}
55
59
56
60
/**
57
61
* Create a projecting variant of a mapped type.
58
62
*
59
- * @param mappedType
60
- * @param domainType
61
- * @param properties
62
- * @return
63
+ * @param mappedType the target projection type. Must not be {@literal null}.
64
+ * @param domainType the source domain type. Must not be {@literal null}.
65
+ * @param properties properties to include.
66
+ * @param projectionType must not be {@literal null}.
67
+ * @return new instance of {@link EntityProjection}.
63
68
*/
64
69
public static <M , D > EntityProjection <M , D > projecting (TypeInformation <M > mappedType , TypeInformation <D > domainType ,
65
- List <PropertyProjection <?, ?>> properties , boolean closedProjection ) {
66
- return new EntityProjection <>(mappedType , domainType , properties , true , closedProjection );
70
+ List <PropertyProjection <?, ?>> properties , ProjectionType projectionType ) {
71
+ return new EntityProjection <>(mappedType , domainType , properties , true , projectionType );
67
72
}
68
73
69
74
/**
70
75
* Create a non-projecting variant of a mapped type.
71
76
*
72
- * @param mappedType
73
- * @param domainType
74
- * @param properties
75
- * @return
77
+ * @param mappedType the target projection type. Must not be {@literal null}.
78
+ * @param domainType the source domain type. Must not be {@literal null}.
79
+ * @param properties properties to include.
80
+ * @return new instance of {@link EntityProjection}.
76
81
*/
77
82
public static <M , D > EntityProjection <M , D > nonProjecting (TypeInformation <M > mappedType ,
78
83
TypeInformation <D > domainType , List <PropertyProjection <?, ?>> properties ) {
79
- return new EntityProjection <>(mappedType , domainType , properties , false , false );
84
+ return new EntityProjection <>(mappedType , domainType , properties , false , ProjectionType . CLOSED );
80
85
}
81
86
82
87
/**
83
88
* Create a non-projecting variant of a {@code type}.
84
89
*
85
- * @param type
86
- * @return
90
+ * @param type must not be {@literal null}.
91
+ * @return new instance of {@link EntityProjection}.
87
92
*/
88
93
public static <T > EntityProjection <T , T > nonProjecting (Class <T > type ) {
89
94
ClassTypeInformation <T > typeInformation = ClassTypeInformation .from (type );
90
- return new EntityProjection <>(typeInformation , typeInformation , Collections .emptyList (), false , false );
95
+ return new EntityProjection <>(typeInformation , typeInformation , Collections .emptyList (), false ,
96
+ ProjectionType .CLOSED );
91
97
}
92
98
93
99
/**
94
100
* Performs the given action for each element of the {@link Streamable} recursively until all elements of the graph
95
- * have been processed or the action throws an exception . Unless otherwise specified by the implementing class,
96
- * actions are performed in the order of iteration (if an iteration order is specified). Exceptions thrown by the
97
- * action are relayed to the caller.
101
+ * have been processed or the action throws an {@link Exception} . Unless otherwise specified by the implementing
102
+ * class, actions are performed in the order of iteration (if an iteration order is specified). Exceptions thrown by
103
+ * the action are relayed to the caller.
98
104
*
99
- * @param action
105
+ * @param action must not be {@literal null}.
100
106
*/
101
107
public void forEachRecursive (Consumer <? super PropertyProjection <?, ?>> action ) {
102
108
@@ -128,6 +134,7 @@ public TypeInformation<M> getMappedType() {
128
134
/**
129
135
* @return the actual mapped type used by this type view. Should be used for collection-like and map-like properties
130
136
* to determine the actual view type.
137
+ * @throws IllegalStateException if the actual type cannot be resolved.
131
138
*/
132
139
public TypeInformation <?> getActualMappedType () {
133
140
return mappedType .getRequiredActualType ();
@@ -143,6 +150,7 @@ public TypeInformation<D> getDomainType() {
143
150
/**
144
151
* @return the actual domain type represented by this type view. Should be used for collection-like and map-like
145
152
* properties to determine the actual domain type.
153
+ * @throws IllegalStateException if the actual type cannot be resolved.
146
154
*/
147
155
public TypeInformation <?> getActualDomainType () {
148
156
return domainType .getRequiredActualType ();
@@ -159,7 +167,8 @@ public boolean isProjection() {
159
167
* @return {@code true} if the {@link #getMappedType()} is a closed projection.
160
168
*/
161
169
public boolean isClosedProjection () {
162
- return isProjection () && closedProjection ;
170
+ return isProjection ()
171
+ && (ProjectionType .CLOSED .equals (projectionType ) || ProjectionType .DTO .equals (projectionType ));
163
172
}
164
173
165
174
List <PropertyProjection <?, ?>> getProperties () {
@@ -207,36 +216,38 @@ public static class PropertyProjection<M, D> extends EntityProjection<M, D> {
207
216
private final PropertyPath propertyPath ;
208
217
209
218
PropertyProjection (PropertyPath propertyPath , TypeInformation <M > mappedType , TypeInformation <D > domainType ,
210
- List <PropertyProjection <?, ?>> properties , boolean projecting , boolean closedProjection ) {
211
- super (mappedType , domainType , properties , projecting , closedProjection );
219
+ List <PropertyProjection <?, ?>> properties , boolean projecting , ProjectionType projectionType ) {
220
+ super (mappedType , domainType , properties , projecting , projectionType );
212
221
this .propertyPath = propertyPath ;
213
222
}
214
223
215
224
/**
216
225
* Create a projecting variant of a mapped type.
217
226
*
218
- * @param propertyPath
219
- * @param mappedType
220
- * @param domainType
221
- * @param properties
222
- * @return
227
+ * @param propertyPath the {@link PropertyPath path} to the actual property.
228
+ * @param mappedType the target projection type. Must not be {@literal null}.
229
+ * @param domainType the source domain type. Must not be {@literal null}.
230
+ * @param properties properties to include.
231
+ * @param projectionType must not be {@literal null}.
232
+ * @return new instance of {@link PropertyProjection}.
223
233
*/
224
234
public static <M , D > PropertyProjection <M , D > projecting (PropertyPath propertyPath , TypeInformation <M > mappedType ,
225
- TypeInformation <D > domainType , List <PropertyProjection <?, ?>> properties , boolean closedProjection ) {
226
- return new PropertyProjection <>(propertyPath , mappedType , domainType , properties , true , closedProjection );
235
+ TypeInformation <D > domainType , List <PropertyProjection <?, ?>> properties , ProjectionType projectionType ) {
236
+ return new PropertyProjection <>(propertyPath , mappedType , domainType , properties , true , projectionType );
227
237
}
228
238
229
239
/**
230
240
* Create a non-projecting variant of a mapped type.
231
241
*
232
- * @param propertyPath
233
- * @param mappedType
234
- * @param domainType
242
+ * @param propertyPath the {@link PropertyPath path} to the actual property.
243
+ * @param mappedType the target projection type. Must not be {@literal null}.
244
+ * @param domainType the source domain type. Must not be {@literal null}.
235
245
* @return
236
246
*/
237
247
public static <M , D > PropertyProjection <M , D > nonProjecting (PropertyPath propertyPath ,
238
248
TypeInformation <M > mappedType , TypeInformation <D > domainType ) {
239
- return new PropertyProjection <>(propertyPath , mappedType , domainType , Collections .emptyList (), false , false );
249
+ return new PropertyProjection <>(propertyPath , mappedType , domainType , Collections .emptyList (), false ,
250
+ ProjectionType .OPEN );
240
251
}
241
252
242
253
/**
@@ -264,39 +275,79 @@ public String toString() {
264
275
public static class ContainerPropertyProjection <M , D > extends PropertyProjection <M , D > {
265
276
266
277
ContainerPropertyProjection (PropertyPath propertyPath , TypeInformation <M > mappedType , TypeInformation <D > domainType ,
267
- List <PropertyProjection <?, ?>> properties , boolean projecting , boolean closedProjection ) {
268
- super (propertyPath , mappedType , domainType , properties , projecting , closedProjection );
278
+ List <PropertyProjection <?, ?>> properties , boolean projecting , ProjectionType projectionType ) {
279
+ super (propertyPath , mappedType , domainType , properties , projecting , projectionType );
269
280
}
270
281
271
282
/**
272
283
* Create a projecting variant of a mapped type.
273
284
*
274
- * @param propertyPath
275
- * @param mappedType
276
- * @param domainType
277
- * @param properties
278
- * @return
285
+ * @param propertyPath the {@link PropertyPath path} to the actual property.
286
+ * @param mappedType the target projection type. Must not be {@literal null}.
287
+ * @param domainType the source domain type. Must not be {@literal null}.
288
+ * @param properties properties to include.
289
+ * @param projectionType must not be {@literal null}.
290
+ * @return new instance of {@link ContainerPropertyProjection}.
279
291
*/
280
292
public static <M , D > ContainerPropertyProjection <M , D > projecting (PropertyPath propertyPath ,
281
293
TypeInformation <M > mappedType , TypeInformation <D > domainType , List <PropertyProjection <?, ?>> properties ,
282
- boolean closedProjection ) {
283
- return new ContainerPropertyProjection <>(propertyPath , mappedType , domainType , properties , true ,
284
- closedProjection );
294
+ ProjectionType projectionType ) {
295
+ return new ContainerPropertyProjection <>(propertyPath , mappedType , domainType , properties , true , projectionType );
285
296
}
286
297
287
298
/**
288
299
* Create a non-projecting variant of a mapped type.
289
300
*
290
- * @param propertyPath
291
- * @param mappedType
292
- * @param domainType
293
- * @return
301
+ * @param propertyPath the {@link PropertyPath path} to the actual property.
302
+ * @param mappedType the target projection type. Must not be {@literal null}.
303
+ * @param domainType the source domain type. Must not be {@literal null}.
304
+ * @return new instance of {@link ContainerPropertyProjection}.
294
305
*/
295
306
public static <M , D > ContainerPropertyProjection <M , D > nonProjecting (PropertyPath propertyPath ,
296
307
TypeInformation <M > mappedType , TypeInformation <D > domainType ) {
297
308
return new ContainerPropertyProjection <>(propertyPath , mappedType , domainType , Collections .emptyList (), false ,
298
- false );
309
+ ProjectionType . OPEN );
299
310
}
300
311
301
312
}
313
+
314
+ /**
315
+ * Projection type.
316
+ *
317
+ * @since 2.7
318
+ */
319
+ public enum ProjectionType {
320
+
321
+ /**
322
+ * A DTO projection defines a value type that hold properties for the fields that are supposed to be retrieved.
323
+ */
324
+ DTO ,
325
+
326
+ /**
327
+ * An open projection has accessor methods in the interface that can be used to compute new values by using the
328
+ * {@link org.springframework.beans.factory.annotation.Value} annotation.
329
+ */
330
+ OPEN ,
331
+
332
+ /**
333
+ * A closed projection only contains accessor methods that all match properties of the target aggregate.
334
+ */
335
+ CLOSED ;
336
+
337
+ /**
338
+ * Obtain the {@link ProjectionType} from a given {@link ProjectionInformation}.
339
+ *
340
+ * @param information must not be {@literal null}.
341
+ * @return the {@link ProjectionType} according to {@link ProjectionInformation#getType() type} and
342
+ * {@link ProjectionInformation#isClosed()}.
343
+ */
344
+ public static ProjectionType from (ProjectionInformation information ) {
345
+
346
+ if (!information .getType ().isInterface ()) {
347
+ return DTO ;
348
+ }
349
+
350
+ return information .isClosed () ? CLOSED : OPEN ;
351
+ }
352
+ }
302
353
}
0 commit comments