17
17
18
18
import java .util .ArrayList ;
19
19
import java .util .Collection ;
20
+ import java .util .LinkedHashMap ;
20
21
import java .util .List ;
21
22
import java .util .Map ;
22
23
import java .util .function .Predicate ;
23
24
25
+ import org .springframework .beans .BeansException ;
26
+ import org .springframework .context .ApplicationContext ;
27
+ import org .springframework .context .ApplicationContextAware ;
24
28
import org .springframework .core .CollectionFactory ;
25
29
import org .springframework .core .convert .ConversionService ;
26
30
import org .springframework .data .convert .CustomConversions ;
27
31
import org .springframework .data .mapping .InstanceCreatorMetadata ;
28
32
import org .springframework .data .mapping .MappingException ;
29
33
import org .springframework .data .mapping .Parameter ;
30
34
import org .springframework .data .mapping .PersistentEntity ;
35
+ import org .springframework .data .mapping .PersistentProperty ;
31
36
import org .springframework .data .mapping .PersistentPropertyAccessor ;
32
37
import org .springframework .data .mapping .context .MappingContext ;
33
38
import org .springframework .data .mapping .model .ConvertingPropertyAccessor ;
39
44
import org .springframework .data .mapping .model .SpELContext ;
40
45
import org .springframework .data .mapping .model .SpELExpressionEvaluator ;
41
46
import org .springframework .data .mapping .model .SpELExpressionParameterValueProvider ;
47
+ import org .springframework .data .projection .EntityProjection ;
48
+ import org .springframework .data .projection .EntityProjectionIntrospector ;
49
+ import org .springframework .data .projection .EntityProjectionIntrospector .ProjectionPredicate ;
50
+ import org .springframework .data .projection .ProjectionFactory ;
51
+ import org .springframework .data .projection .SpelAwareProxyProjectionFactory ;
42
52
import org .springframework .data .relational .core .mapping .Embedded ;
43
53
import org .springframework .data .relational .core .mapping .Embedded .OnEmpty ;
54
+ import org .springframework .data .relational .core .mapping .PersistentPropertyTranslator ;
44
55
import org .springframework .data .relational .core .mapping .RelationalMappingContext ;
45
56
import org .springframework .data .relational .core .mapping .RelationalPersistentEntity ;
46
57
import org .springframework .data .relational .core .mapping .RelationalPersistentProperty ;
47
58
import org .springframework .data .relational .domain .RowDocument ;
59
+ import org .springframework .data .util .Predicates ;
48
60
import org .springframework .data .util .TypeInformation ;
49
61
import org .springframework .lang .Nullable ;
50
62
import org .springframework .util .Assert ;
57
69
* @author Mark Paluch
58
70
* @since 3.2
59
71
*/
60
- public class MappingRelationalConverter extends BasicRelationalConverter {
72
+ public class MappingRelationalConverter extends BasicRelationalConverter implements ApplicationContextAware {
61
73
62
74
private SpELContext spELContext ;
63
75
76
+ private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory ();
77
+
78
+ private final EntityProjectionIntrospector introspector ;
79
+
64
80
/**
65
81
* Creates a new {@link MappingRelationalConverter} given the new {@link RelationalMappingContext}.
66
82
*
@@ -71,6 +87,7 @@ public MappingRelationalConverter(RelationalMappingContext context) {
71
87
super (context );
72
88
73
89
this .spELContext = new SpELContext (DocumentPropertyAccessor .INSTANCE );
90
+ this .introspector = createIntrospector (projectionFactory , getConversions (), getMappingContext ());
74
91
}
75
92
76
93
/**
@@ -85,6 +102,29 @@ public MappingRelationalConverter(RelationalMappingContext context, CustomConver
85
102
super (context , conversions );
86
103
87
104
this .spELContext = new SpELContext (DocumentPropertyAccessor .INSTANCE );
105
+ this .introspector = createIntrospector (projectionFactory , getConversions (), getMappingContext ());
106
+
107
+ }
108
+
109
+ private static EntityProjectionIntrospector createIntrospector (ProjectionFactory projectionFactory ,
110
+ CustomConversions conversions , MappingContext <?, ?> mappingContext ) {
111
+
112
+ return EntityProjectionIntrospector .create (projectionFactory ,
113
+ ProjectionPredicate .typeHierarchy ().and ((target , underlyingType ) -> !conversions .isSimpleType (target )),
114
+ mappingContext );
115
+ }
116
+
117
+ @ Override
118
+ public void setApplicationContext (ApplicationContext applicationContext ) throws BeansException {
119
+
120
+ this .spELContext = new SpELContext (this .spELContext , applicationContext );
121
+ this .projectionFactory .setBeanFactory (applicationContext );
122
+ this .projectionFactory .setBeanClassLoader (applicationContext .getClassLoader ());
123
+ }
124
+
125
+ @ Override
126
+ public ProjectionFactory getProjectionFactory () {
127
+ return this .projectionFactory ;
88
128
}
89
129
90
130
/**
@@ -100,6 +140,128 @@ protected ConversionContext getConversionContext(ObjectPath path) {
100
140
this ::readMap , this ::getPotentiallyConvertedSimpleRead );
101
141
}
102
142
143
+ @ Override
144
+ public <M , D > EntityProjection <M , D > introspectProjection (Class <M > resultType , Class <D > entityType ) {
145
+
146
+ RelationalPersistentEntity <?> persistentEntity = getMappingContext ().getPersistentEntity (entityType );
147
+ if (persistentEntity == null && !resultType .isInterface ()
148
+ || ClassUtils .isAssignable (RowDocument .class , resultType )) {
149
+ return (EntityProjection ) EntityProjection .nonProjecting (resultType );
150
+ }
151
+ return introspector .introspect (resultType , entityType );
152
+ }
153
+
154
+ @ Override
155
+ public <R > R project (EntityProjection <R , ?> projection , RowDocument document ) {
156
+
157
+ if (!projection .isProjection ()) { // backed by real object
158
+
159
+ TypeInformation <?> typeToRead = projection .getMappedType ().getType ().isInterface () ? projection .getDomainType ()
160
+ : projection .getMappedType ();
161
+ return (R ) read (typeToRead , document );
162
+ }
163
+
164
+ ProjectingConversionContext context = new ProjectingConversionContext (this , getConversions (), ObjectPath .ROOT ,
165
+ this ::readCollectionOrArray , this ::readMap , this ::getPotentiallyConvertedSimpleRead , projection );
166
+
167
+ return doReadProjection (context , document , projection );
168
+ }
169
+
170
+ @ SuppressWarnings ("unchecked" )
171
+ private <R > R doReadProjection (ConversionContext context , RowDocument document , EntityProjection <R , ?> projection ) {
172
+
173
+ RelationalPersistentEntity <?> entity = getMappingContext ()
174
+ .getRequiredPersistentEntity (projection .getActualDomainType ());
175
+ TypeInformation <?> mappedType = projection .getActualMappedType ();
176
+ RelationalPersistentEntity <R > mappedEntity = (RelationalPersistentEntity <R >) getMappingContext ()
177
+ .getPersistentEntity (mappedType );
178
+ SpELExpressionEvaluator evaluator = new DefaultSpELExpressionEvaluator (document , spELContext );
179
+
180
+ boolean isInterfaceProjection = mappedType .getType ().isInterface ();
181
+ if (isInterfaceProjection ) {
182
+
183
+ PersistentPropertyTranslator propertyTranslator = PersistentPropertyTranslator .create (mappedEntity );
184
+ RowDocumentAccessor documentAccessor = new RowDocumentAccessor (document );
185
+ PersistentPropertyAccessor <?> accessor = new MapPersistentPropertyAccessor ();
186
+
187
+ PersistentPropertyAccessor <?> convertingAccessor = PropertyTranslatingPropertyAccessor
188
+ .create (new ConvertingPropertyAccessor <>(accessor , getConversionService ()), propertyTranslator );
189
+ RelationalPropertyValueProvider valueProvider = new RelationalPropertyValueProvider (context , documentAccessor ,
190
+ evaluator , spELContext );
191
+
192
+ readProperties (context , entity , convertingAccessor , documentAccessor , valueProvider , Predicates .isTrue ());
193
+ return (R ) projectionFactory .createProjection (mappedType .getType (), accessor .getBean ());
194
+ }
195
+
196
+ // DTO projection
197
+ if (mappedEntity == null ) {
198
+ throw new MappingException (String .format ("No mapping metadata found for %s" , mappedType .getType ().getName ()));
199
+ }
200
+
201
+ // create target instance, merge metadata from underlying DTO type
202
+ PersistentPropertyTranslator propertyTranslator = PersistentPropertyTranslator .create (entity ,
203
+ Predicates .negate (RelationalPersistentProperty ::hasExplicitColumnName ));
204
+ RowDocumentAccessor documentAccessor = new RowDocumentAccessor (document ) {
205
+
206
+ @ Override
207
+ String getColumnName (RelationalPersistentProperty prop ) {
208
+ return propertyTranslator .translate (prop ).getColumnName ().getReference ();
209
+ }
210
+ };
211
+
212
+ InstanceCreatorMetadata <RelationalPersistentProperty > instanceCreatorMetadata = mappedEntity
213
+ .getInstanceCreatorMetadata ();
214
+ ParameterValueProvider <RelationalPersistentProperty > provider = instanceCreatorMetadata != null
215
+ && instanceCreatorMetadata .hasParameters ()
216
+ ? getParameterProvider (context , mappedEntity , documentAccessor , evaluator )
217
+ : NoOpParameterValueProvider .INSTANCE ;
218
+
219
+ EntityInstantiator instantiator = getEntityInstantiators ().getInstantiatorFor (mappedEntity );
220
+ R instance = instantiator .createInstance (mappedEntity , provider );
221
+ PersistentPropertyAccessor <R > accessor = mappedEntity .getPropertyAccessor (instance );
222
+
223
+ populateProperties (context , mappedEntity , documentAccessor , evaluator , instance );
224
+
225
+ PersistentPropertyAccessor <?> convertingAccessor = new ConvertingPropertyAccessor <>(accessor ,
226
+ getConversionService ());
227
+ RelationalPropertyValueProvider valueProvider = new RelationalPropertyValueProvider (context , documentAccessor ,
228
+ evaluator , spELContext );
229
+
230
+ readProperties (context , mappedEntity , convertingAccessor , documentAccessor , valueProvider , Predicates .isTrue ());
231
+
232
+ return accessor .getBean ();
233
+ }
234
+
235
+ private Object doReadOrProject (ConversionContext context , RowDocument source , TypeInformation <?> typeHint ,
236
+ EntityProjection <?, ?> typeDescriptor ) {
237
+
238
+ if (typeDescriptor .isProjection ()) {
239
+ return doReadProjection (context , source , typeDescriptor );
240
+ }
241
+
242
+ return readAggregate (context , source , typeHint );
243
+ }
244
+
245
+ static class MapPersistentPropertyAccessor implements PersistentPropertyAccessor <Map <String , Object >> {
246
+
247
+ Map <String , Object > map = new LinkedHashMap <>();
248
+
249
+ @ Override
250
+ public void setProperty (PersistentProperty <?> persistentProperty , Object o ) {
251
+ map .put (persistentProperty .getName (), o );
252
+ }
253
+
254
+ @ Override
255
+ public Object getProperty (PersistentProperty <?> persistentProperty ) {
256
+ return map .get (persistentProperty .getName ());
257
+ }
258
+
259
+ @ Override
260
+ public Map <String , Object > getBean () {
261
+ return map ;
262
+ }
263
+ }
264
+
103
265
/**
104
266
* Read a {@link RowDocument} into the requested {@link Class aggregate type}.
105
267
*
@@ -295,15 +457,14 @@ private <S> S populateProperties(ConversionContext context, RelationalPersistent
295
457
evaluator , spELContext );
296
458
297
459
Predicate <RelationalPersistentProperty > propertyFilter = isConstructorArgument (entity ).negate ();
298
- readProperties (contextToUse , entity , accessor , documentAccessor , valueProvider , evaluator , propertyFilter );
460
+ readProperties (contextToUse , entity , accessor , documentAccessor , valueProvider , propertyFilter );
299
461
300
462
return accessor .getBean ();
301
463
}
302
464
303
465
private void readProperties (ConversionContext context , RelationalPersistentEntity <?> entity ,
304
466
PersistentPropertyAccessor <?> accessor , RowDocumentAccessor documentAccessor ,
305
- RelationalPropertyValueProvider valueProvider , SpELExpressionEvaluator evaluator ,
306
- Predicate <RelationalPersistentProperty > propertyFilter ) {
467
+ RelationalPropertyValueProvider valueProvider , Predicate <RelationalPersistentProperty > propertyFilter ) {
307
468
308
469
for (RelationalPersistentProperty prop : entity ) {
309
470
@@ -475,6 +636,44 @@ interface ContainerValueConverter<T> {
475
636
476
637
}
477
638
639
+ /**
640
+ * @since 3.4.3
641
+ */
642
+ class ProjectingConversionContext extends DefaultConversionContext {
643
+
644
+ private final EntityProjection <?, ?> returnedTypeDescriptor ;
645
+
646
+ ProjectingConversionContext (RelationalConverter sourceConverter , CustomConversions customConversions ,
647
+ ObjectPath path , ContainerValueConverter <Collection <?>> collectionConverter ,
648
+ ContainerValueConverter <Map <?, ?>> mapConverter , ValueConverter <Object > elementConverter ,
649
+ EntityProjection <?, ?> projection ) {
650
+ super (sourceConverter , customConversions , path ,
651
+ (context , source , typeHint ) -> doReadOrProject (context , source , typeHint , projection ),
652
+
653
+ collectionConverter , mapConverter , elementConverter );
654
+ this .returnedTypeDescriptor = projection ;
655
+ }
656
+
657
+ @ Override
658
+ public ConversionContext forProperty (String name ) {
659
+
660
+ EntityProjection <?, ?> property = returnedTypeDescriptor .findProperty (name );
661
+ if (property == null ) {
662
+ return new DefaultConversionContext (sourceConverter , conversions , objectPath ,
663
+ MappingRelationalConverter .this ::readAggregate , collectionConverter , mapConverter , elementConverter );
664
+ }
665
+
666
+ return new ProjectingConversionContext (sourceConverter , conversions , objectPath , collectionConverter ,
667
+ mapConverter , elementConverter , property );
668
+ }
669
+
670
+ @ Override
671
+ public ConversionContext withPath (ObjectPath currentPath ) {
672
+ return new ProjectingConversionContext (sourceConverter , conversions , currentPath , collectionConverter ,
673
+ mapConverter , elementConverter , returnedTypeDescriptor );
674
+ }
675
+ }
676
+
478
677
/**
479
678
* Conversion context defining an interface for graph-traversal-based conversion of row documents. Entrypoint for
480
679
* recursive conversion of {@link RowDocument} and other types.
@@ -633,4 +832,32 @@ protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, Relation
633
832
}
634
833
}
635
834
835
+ private record PropertyTranslatingPropertyAccessor <T > (PersistentPropertyAccessor <T > delegate ,
836
+ PersistentPropertyTranslator propertyTranslator ) implements PersistentPropertyAccessor <T > {
837
+
838
+ static <T > PersistentPropertyAccessor <T > create (PersistentPropertyAccessor <T > delegate ,
839
+ PersistentPropertyTranslator propertyTranslator ) {
840
+ return new PropertyTranslatingPropertyAccessor <>(delegate , propertyTranslator );
841
+ }
842
+
843
+ @ Override
844
+ public void setProperty (PersistentProperty <?> property , @ Nullable Object value ) {
845
+ delegate .setProperty (translate (property ), value );
846
+ }
847
+
848
+ @ Override
849
+ public Object getProperty (PersistentProperty <?> property ) {
850
+ return delegate .getProperty (translate (property ));
851
+ }
852
+
853
+ @ Override
854
+ public T getBean () {
855
+ return delegate .getBean ();
856
+ }
857
+
858
+ private RelationalPersistentProperty translate (PersistentProperty <?> property ) {
859
+ return propertyTranslator .translate ((RelationalPersistentProperty ) property );
860
+ }
861
+ }
862
+
636
863
}
0 commit comments