45
45
import org .springframework .data .mapping .PersistentPropertyAccessor ;
46
46
import org .springframework .data .mapping .callback .ReactiveEntityCallbacks ;
47
47
import org .springframework .data .mapping .context .MappingContext ;
48
+ import org .springframework .data .projection .EntityProjection ;
48
49
import org .springframework .data .projection .ProjectionInformation ;
49
50
import org .springframework .data .projection .SpelAwareProxyProjectionFactory ;
50
51
import org .springframework .data .r2dbc .convert .R2dbcConverter ;
66
67
import org .springframework .data .relational .core .sql .Functions ;
67
68
import org .springframework .data .relational .core .sql .SqlIdentifier ;
68
69
import org .springframework .data .relational .core .sql .Table ;
70
+ import org .springframework .data .relational .domain .RowDocument ;
69
71
import org .springframework .data .util .ProxyUtils ;
70
72
import org .springframework .lang .Nullable ;
71
73
import org .springframework .r2dbc .core .DatabaseClient ;
@@ -95,6 +97,8 @@ public class R2dbcEntityTemplate implements R2dbcEntityOperations, BeanFactoryAw
95
97
96
98
private final ReactiveDataAccessStrategy dataAccessStrategy ;
97
99
100
+ private final R2dbcConverter converter ;
101
+
98
102
private final MappingContext <? extends RelationalPersistentEntity <?>, ? extends RelationalPersistentProperty > mappingContext ;
99
103
100
104
private final SpelAwareProxyProjectionFactory projectionFactory ;
@@ -116,7 +120,8 @@ public R2dbcEntityTemplate(ConnectionFactory connectionFactory) {
116
120
this .databaseClient = DatabaseClient .builder ().connectionFactory (connectionFactory )
117
121
.bindMarkers (dialect .getBindMarkersFactory ()).build ();
118
122
this .dataAccessStrategy = new DefaultReactiveDataAccessStrategy (dialect );
119
- this .mappingContext = dataAccessStrategy .getConverter ().getMappingContext ();
123
+ this .converter = dataAccessStrategy .getConverter ();
124
+ this .mappingContext = converter .getMappingContext ();
120
125
this .projectionFactory = new SpelAwareProxyProjectionFactory ();
121
126
}
122
127
@@ -157,6 +162,7 @@ public R2dbcEntityTemplate(DatabaseClient databaseClient, ReactiveDataAccessStra
157
162
158
163
this .databaseClient = databaseClient ;
159
164
this .dataAccessStrategy = strategy ;
165
+ this .converter = dataAccessStrategy .getConverter ();
160
166
this .mappingContext = strategy .getConverter ().getMappingContext ();
161
167
this .projectionFactory = new SpelAwareProxyProjectionFactory ();
162
168
}
@@ -173,7 +179,7 @@ public ReactiveDataAccessStrategy getDataAccessStrategy() {
173
179
174
180
@ Override
175
181
public R2dbcConverter getConverter () {
176
- return this .dataAccessStrategy . getConverter () ;
182
+ return this .converter ;
177
183
}
178
184
179
185
@ Override
@@ -334,10 +340,10 @@ <T, P extends Publisher<T>> P doSelect(Query query, Class<?> entityClass, SqlIde
334
340
return (P ) ((Flux <?>) result ).concatMap (it -> maybeCallAfterConvert (it , tableName ));
335
341
}
336
342
337
- private <T > RowsFetchSpec <T > doSelect (Query query , Class <?> entityClass , SqlIdentifier tableName ,
343
+ private <T > RowsFetchSpec <T > doSelect (Query query , Class <?> entityType , SqlIdentifier tableName ,
338
344
Class <T > returnType ) {
339
345
340
- StatementMapper statementMapper = dataAccessStrategy .getStatementMapper ().forType (entityClass );
346
+ StatementMapper statementMapper = dataAccessStrategy .getStatementMapper ().forType (entityType );
341
347
342
348
StatementMapper .SelectSpec selectSpec = statementMapper //
343
349
.createSelect (tableName ) //
@@ -362,7 +368,7 @@ private <T> RowsFetchSpec<T> doSelect(Query query, Class<?> entityClass, SqlIden
362
368
363
369
PreparedOperation <?> operation = statementMapper .getMappedObject (selectSpec );
364
370
365
- return getRowsFetchSpec (databaseClient .sql (operation ), entityClass , returnType );
371
+ return getRowsFetchSpec (databaseClient .sql (operation ), entityType , returnType );
366
372
}
367
373
368
374
@ Override
@@ -783,19 +789,26 @@ private <T> List<Expression> getSelectProjection(Table table, Query query, Class
783
789
return query .getColumns ().stream ().map (table ::column ).collect (Collectors .toList ());
784
790
}
785
791
786
- private <T > RowsFetchSpec <T > getRowsFetchSpec (DatabaseClient .GenericExecuteSpec executeSpec , Class <?> entityClass ,
787
- Class <T > returnType ) {
792
+ private <T > RowsFetchSpec <T > getRowsFetchSpec (DatabaseClient .GenericExecuteSpec executeSpec , Class <?> entityType ,
793
+ Class <T > resultType ) {
788
794
789
- boolean simpleType ;
795
+ boolean simpleType = getConverter (). isSimpleType ( resultType ) ;
790
796
791
797
BiFunction <Row , RowMetadata , T > rowMapper ;
792
- if (returnType .isInterface ()) {
793
- simpleType = getConverter ().isSimpleType (entityClass );
794
- rowMapper = dataAccessStrategy .getRowMapper (entityClass )
795
- .andThen (o -> projectionFactory .createProjection (returnType , o ));
798
+
799
+ if (simpleType ) {
800
+ rowMapper = dataAccessStrategy .getRowMapper (resultType );
796
801
} else {
797
- simpleType = getConverter ().isSimpleType (returnType );
798
- rowMapper = dataAccessStrategy .getRowMapper (returnType );
802
+
803
+ EntityProjection <T , ?> projection = converter .introspectProjection (resultType , entityType );
804
+
805
+ rowMapper = (row , rowMetadata ) -> {
806
+
807
+ RowDocument document = dataAccessStrategy .toRowDocument (resultType , row , rowMetadata .getColumnMetadatas ());
808
+
809
+ return projection .isProjection () ? converter .project (projection , document )
810
+ : converter .read (resultType , document );
811
+ };
799
812
}
800
813
801
814
// avoid top-level null values if the read type is a simple one (e.g. SELECT MAX(age) via Integer.class)
0 commit comments