1717
1818import java .util .ArrayList ;
1919import java .util .Collection ;
20+ import java .util .LinkedHashMap ;
2021import java .util .List ;
2122import java .util .Map ;
2223import java .util .function .Predicate ;
2324
25+ import org .springframework .beans .BeansException ;
26+ import org .springframework .context .ApplicationContext ;
27+ import org .springframework .context .ApplicationContextAware ;
2428import org .springframework .core .CollectionFactory ;
2529import org .springframework .core .convert .ConversionService ;
2630import org .springframework .data .convert .CustomConversions ;
2731import org .springframework .data .mapping .InstanceCreatorMetadata ;
2832import org .springframework .data .mapping .MappingException ;
2933import org .springframework .data .mapping .Parameter ;
3034import org .springframework .data .mapping .PersistentEntity ;
35+ import org .springframework .data .mapping .PersistentProperty ;
3136import org .springframework .data .mapping .PersistentPropertyAccessor ;
3237import org .springframework .data .mapping .context .MappingContext ;
3338import org .springframework .data .mapping .model .ConvertingPropertyAccessor ;
3944import org .springframework .data .mapping .model .SpELContext ;
4045import org .springframework .data .mapping .model .SpELExpressionEvaluator ;
4146import 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 ;
4252import org .springframework .data .relational .core .mapping .Embedded ;
4353import org .springframework .data .relational .core .mapping .Embedded .OnEmpty ;
54+ import org .springframework .data .relational .core .mapping .PersistentPropertyTranslator ;
4455import org .springframework .data .relational .core .mapping .RelationalMappingContext ;
4556import org .springframework .data .relational .core .mapping .RelationalPersistentEntity ;
4657import org .springframework .data .relational .core .mapping .RelationalPersistentProperty ;
4758import org .springframework .data .relational .domain .RowDocument ;
59+ import org .springframework .data .util .Predicates ;
4860import org .springframework .data .util .TypeInformation ;
4961import org .springframework .lang .Nullable ;
5062import org .springframework .util .Assert ;
5769 * @author Mark Paluch
5870 * @since 3.2
5971 */
60- public class MappingRelationalConverter extends BasicRelationalConverter {
72+ public class MappingRelationalConverter extends BasicRelationalConverter implements ApplicationContextAware {
6173
6274 private SpELContext spELContext ;
6375
76+ private final SpelAwareProxyProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory ();
77+
78+ private final EntityProjectionIntrospector introspector ;
79+
6480 /**
6581 * Creates a new {@link MappingRelationalConverter} given the new {@link RelationalMappingContext}.
6682 *
@@ -71,6 +87,7 @@ public MappingRelationalConverter(RelationalMappingContext context) {
7187 super (context );
7288
7389 this .spELContext = new SpELContext (DocumentPropertyAccessor .INSTANCE );
90+ this .introspector = createIntrospector (projectionFactory , getConversions (), getMappingContext ());
7491 }
7592
7693 /**
@@ -85,6 +102,29 @@ public MappingRelationalConverter(RelationalMappingContext context, CustomConver
85102 super (context , conversions );
86103
87104 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 ;
88128 }
89129
90130 /**
@@ -100,6 +140,128 @@ protected ConversionContext getConversionContext(ObjectPath path) {
100140 this ::readMap , this ::getPotentiallyConvertedSimpleRead );
101141 }
102142
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+
103265 /**
104266 * Read a {@link RowDocument} into the requested {@link Class aggregate type}.
105267 *
@@ -295,15 +457,14 @@ private <S> S populateProperties(ConversionContext context, RelationalPersistent
295457 evaluator , spELContext );
296458
297459 Predicate <RelationalPersistentProperty > propertyFilter = isConstructorArgument (entity ).negate ();
298- readProperties (contextToUse , entity , accessor , documentAccessor , valueProvider , evaluator , propertyFilter );
460+ readProperties (contextToUse , entity , accessor , documentAccessor , valueProvider , propertyFilter );
299461
300462 return accessor .getBean ();
301463 }
302464
303465 private void readProperties (ConversionContext context , RelationalPersistentEntity <?> entity ,
304466 PersistentPropertyAccessor <?> accessor , RowDocumentAccessor documentAccessor ,
305- RelationalPropertyValueProvider valueProvider , SpELExpressionEvaluator evaluator ,
306- Predicate <RelationalPersistentProperty > propertyFilter ) {
467+ RelationalPropertyValueProvider valueProvider , Predicate <RelationalPersistentProperty > propertyFilter ) {
307468
308469 for (RelationalPersistentProperty prop : entity ) {
309470
@@ -475,6 +636,44 @@ interface ContainerValueConverter<T> {
475636
476637 }
477638
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+
478677 /**
479678 * Conversion context defining an interface for graph-traversal-based conversion of row documents. Entrypoint for
480679 * recursive conversion of {@link RowDocument} and other types.
@@ -633,4 +832,32 @@ protected <T> T potentiallyConvertSpelValue(Object object, Parameter<T, Relation
633832 }
634833 }
635834
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+
636863}
0 commit comments