16
16
package org .springframework .data .jdbc .core ;
17
17
18
18
import lombok .NonNull ;
19
- import lombok .RequiredArgsConstructor ;
20
19
21
- import java .sql .Connection ;
22
20
import java .sql .JDBCType ;
21
+ import java .util .ArrayList ;
22
+ import java .util .Arrays ;
23
23
import java .util .HashMap ;
24
- import java .util .LinkedHashMap ;
24
+ import java .util .HashSet ;
25
+ import java .util .List ;
25
26
import java .util .Map ;
26
- import java .util .stream .Collectors ;
27
- import java .util .stream .StreamSupport ;
27
+ import java .util .function .Predicate ;
28
28
29
29
import org .springframework .dao .DataRetrievalFailureException ;
30
30
import org .springframework .dao .EmptyResultDataAccessException ;
31
31
import org .springframework .dao .InvalidDataAccessApiUsageException ;
32
+ import org .springframework .data .jdbc .core .convert .JdbcConverter ;
33
+ import org .springframework .data .jdbc .core .convert .JdbcValue ;
32
34
import org .springframework .data .jdbc .support .JdbcUtil ;
35
+ import org .springframework .data .mapping .PersistentProperty ;
33
36
import org .springframework .data .mapping .PersistentPropertyAccessor ;
34
37
import org .springframework .data .mapping .PersistentPropertyPath ;
35
38
import org .springframework .data .mapping .PropertyHandler ;
36
- import org .springframework .data .relational .core .conversion .RelationalConverter ;
37
39
import org .springframework .data .relational .core .mapping .RelationalMappingContext ;
38
40
import org .springframework .data .relational .core .mapping .RelationalPersistentEntity ;
39
41
import org .springframework .data .relational .core .mapping .RelationalPersistentProperty ;
40
42
import org .springframework .data .relational .domain .Identifier ;
41
- import org .springframework .data .util .ClassTypeInformation ;
42
43
import org .springframework .jdbc .core .RowMapper ;
43
44
import org .springframework .jdbc .core .namedparam .MapSqlParameterSource ;
44
45
import org .springframework .jdbc .core .namedparam .NamedParameterJdbcOperations ;
45
46
import org .springframework .jdbc .support .GeneratedKeyHolder ;
47
+ import org .springframework .jdbc .support .JdbcUtils ;
46
48
import org .springframework .jdbc .support .KeyHolder ;
47
49
import org .springframework .lang .Nullable ;
48
50
import org .springframework .util .Assert ;
55
57
* @author Thomas Lang
56
58
* @author Bastian Wilhelm
57
59
*/
58
- @ RequiredArgsConstructor
59
60
public class DefaultDataAccessStrategy implements DataAccessStrategy {
60
61
61
62
private final @ NonNull SqlGeneratorSource sqlGeneratorSource ;
62
63
private final @ NonNull RelationalMappingContext context ;
63
- private final @ NonNull RelationalConverter converter ;
64
+ private final @ NonNull JdbcConverter converter ;
64
65
private final @ NonNull NamedParameterJdbcOperations operations ;
65
66
private final @ NonNull DataAccessStrategy accessStrategy ;
66
67
68
+ public DefaultDataAccessStrategy (SqlGeneratorSource sqlGeneratorSource , RelationalMappingContext context ,
69
+ JdbcConverter converter , NamedParameterJdbcOperations operations , @ Nullable DataAccessStrategy accessStrategy ) {
70
+
71
+ this .sqlGeneratorSource = sqlGeneratorSource ;
72
+ this .context = context ;
73
+ this .converter = converter ;
74
+ this .operations = operations ;
75
+ this .accessStrategy = accessStrategy == null ? this : accessStrategy ;
76
+ }
77
+
67
78
/**
68
79
* Creates a {@link DefaultDataAccessStrategy} which references it self for resolution of recursive data accesses.
69
80
* Only suitable if this is the only access strategy in use.
70
81
*/
71
82
public DefaultDataAccessStrategy (SqlGeneratorSource sqlGeneratorSource , RelationalMappingContext context ,
72
- RelationalConverter converter , NamedParameterJdbcOperations operations ) {
83
+ JdbcConverter converter , NamedParameterJdbcOperations operations ) {
73
84
74
- this .sqlGeneratorSource = sqlGeneratorSource ;
75
- this .operations = operations ;
76
- this .context = context ;
77
- this .converter = converter ;
78
- this .accessStrategy = this ;
85
+ this (sqlGeneratorSource , context , converter , operations , null );
79
86
}
80
87
81
88
/*
@@ -97,28 +104,19 @@ public <T> Object insert(T instance, Class<T> domainType, Identifier identifier)
97
104
KeyHolder holder = new GeneratedKeyHolder ();
98
105
RelationalPersistentEntity <T > persistentEntity = getRequiredPersistentEntity (domainType );
99
106
100
- Map <String , Object > parameters = new LinkedHashMap <>(identifier .size ());
101
- identifier .forEach ((name , value , type ) -> {
102
- parameters .put (name , converter .writeValue (value , ClassTypeInformation .from (type )));
103
- });
107
+ MapSqlParameterSource parameterSource = getParameterSource (instance , persistentEntity , "" , PersistentProperty ::isIdProperty );
104
108
105
- MapSqlParameterSource parameterSource = getPropertyMap ( instance , persistentEntity , "" );
109
+ identifier . forEach (( name , value , type ) -> addConvertedPropertyValue ( parameterSource , name , value , type ) );
106
110
107
111
Object idValue = getIdValueOrNull (instance , persistentEntity );
108
- RelationalPersistentProperty idProperty = persistentEntity .getIdProperty ();
109
-
110
112
if (idValue != null ) {
111
113
112
- Assert .notNull (idProperty , "Since we have a non-null idValue, we must have an idProperty as well." );
113
-
114
- parameters .put (idProperty .getColumnName (),
115
- converter .writeValue (idValue , ClassTypeInformation .from (idProperty .getColumnType ())));
114
+ RelationalPersistentProperty idProperty = persistentEntity .getRequiredIdProperty ();
115
+ addConvertedPropertyValue (parameterSource , idProperty , idValue , idProperty .getColumnName ());
116
116
}
117
117
118
- parameters .forEach (parameterSource ::addValue );
119
-
120
118
operations .update ( //
121
- sql (domainType ).getInsert (parameters . keySet ( )), //
119
+ sql (domainType ).getInsert (new HashSet <>( Arrays . asList ( parameterSource . getParameterNames ()) )), //
122
120
parameterSource , //
123
121
holder //
124
122
);
@@ -135,7 +133,8 @@ public <S> boolean update(S instance, Class<S> domainType) {
135
133
136
134
RelationalPersistentEntity <S > persistentEntity = getRequiredPersistentEntity (domainType );
137
135
138
- return operations .update (sql (domainType ).getUpdate (), getPropertyMap (instance , persistentEntity , "" )) != 0 ;
136
+ return operations .update (sql (domainType ).getUpdate (),
137
+ getParameterSource (instance , persistentEntity , "" , property -> false )) != 0 ;
139
138
}
140
139
141
140
/*
@@ -240,17 +239,14 @@ public <T> Iterable<T> findAll(Class<T> domainType) {
240
239
@ Override
241
240
public <T > Iterable <T > findAllById (Iterable <?> ids , Class <T > domainType ) {
242
241
243
- String findAllInListSql = sql (domainType ).getFindAllInList ();
244
- Class <?> targetType = getRequiredPersistentEntity ( domainType ). getRequiredIdProperty (). getColumnType ();
242
+ RelationalPersistentProperty idProperty = getRequiredPersistentEntity (domainType ).getRequiredIdProperty ();
243
+ MapSqlParameterSource parameterSource = new MapSqlParameterSource ();
245
244
246
- MapSqlParameterSource parameter = new MapSqlParameterSource ( //
247
- "ids" , //
248
- StreamSupport .stream (ids .spliterator (), false ) //
249
- .map (id -> converter .writeValue (id , ClassTypeInformation .from (targetType ))) //
250
- .collect (Collectors .toList ()) //
251
- );
245
+ addConvertedPropertyValuesAsList (parameterSource , idProperty , ids , "ids" );
246
+
247
+ String findAllInListSql = sql (domainType ).getFindAllInList ();
252
248
253
- return operations .query (findAllInListSql , parameter , (RowMapper <T >) getEntityRowMapper (domainType ));
249
+ return operations .query (findAllInListSql , parameterSource , (RowMapper <T >) getEntityRowMapper (domainType ));
254
250
}
255
251
256
252
/*
@@ -292,15 +288,18 @@ public <T> boolean existsById(Object id, Class<T> domainType) {
292
288
return result ;
293
289
}
294
290
295
- private <S , T > MapSqlParameterSource getPropertyMap (S instance , RelationalPersistentEntity <S > persistentEntity ,
296
- String prefix ) {
291
+ private <S , T > MapSqlParameterSource getParameterSource (S instance , RelationalPersistentEntity <S > persistentEntity ,
292
+ String prefix , Predicate < RelationalPersistentProperty > skipProperty ) {
297
293
298
294
MapSqlParameterSource parameters = new MapSqlParameterSource ();
299
295
300
296
PersistentPropertyAccessor <S > propertyAccessor = persistentEntity .getPropertyAccessor (instance );
301
297
302
298
persistentEntity .doWithProperties ((PropertyHandler <RelationalPersistentProperty >) property -> {
303
299
300
+ if (skipProperty .test (property )) {
301
+ return ;
302
+ }
304
303
if (property .isEntity () && !property .isEmbedded ()) {
305
304
return ;
306
305
}
@@ -309,42 +308,21 @@ private <S, T> MapSqlParameterSource getPropertyMap(S instance, RelationalPersis
309
308
310
309
Object value = propertyAccessor .getProperty (property );
311
310
RelationalPersistentEntity <?> embeddedEntity = context .getPersistentEntity (property .getType ());
312
- MapSqlParameterSource additionalParameters = getPropertyMap ((T ) value ,
313
- (RelationalPersistentEntity <T >) embeddedEntity , prefix + property .getEmbeddedPrefix ());
311
+ MapSqlParameterSource additionalParameters = getParameterSource ((T ) value ,
312
+ (RelationalPersistentEntity <T >) embeddedEntity , prefix + property .getEmbeddedPrefix (), skipProperty );
314
313
parameters .addValues (additionalParameters .getValues ());
315
314
} else {
316
315
317
316
Object value = propertyAccessor .getProperty (property );
318
- Object convertedValue = convertForWrite ( property , value );
317
+ String paramName = prefix + property . getColumnName ( );
319
318
320
- parameters .addValue (prefix + property .getColumnName (), convertedValue ,
321
- JdbcUtil .sqlTypeFor (property .getColumnType ()));
319
+ addConvertedPropertyValue (parameters , property , value , paramName );
322
320
}
323
321
});
324
322
325
323
return parameters ;
326
324
}
327
325
328
- @ Nullable
329
- private Object convertForWrite (RelationalPersistentProperty property , @ Nullable Object value ) {
330
-
331
- Object convertedValue = converter .writeValue (value , ClassTypeInformation .from (property .getColumnType ()));
332
-
333
- if (convertedValue == null || !convertedValue .getClass ().isArray ()) {
334
- return convertedValue ;
335
- }
336
-
337
- Class <?> componentType = convertedValue .getClass ();
338
- while (componentType .isArray ()) {
339
- componentType = componentType .getComponentType ();
340
- }
341
-
342
- String typeName = JDBCType .valueOf (JdbcUtil .sqlTypeFor (componentType )).getName ();
343
-
344
- return operations .getJdbcOperations ()
345
- .execute ((Connection c ) -> c .createArrayOf (typeName , (Object []) convertedValue ));
346
- }
347
-
348
326
@ SuppressWarnings ("unchecked" )
349
327
@ Nullable
350
328
private <S , ID > ID getIdValueOrNull (S instance , RelationalPersistentEntity <S > persistentEntity ) {
@@ -398,8 +376,65 @@ private RowMapper<?> getMapEntityRowMapper(RelationalPersistentProperty property
398
376
399
377
private <T > MapSqlParameterSource createIdParameterSource (Object id , Class <T > domainType ) {
400
378
401
- Class <?> columnType = getRequiredPersistentEntity (domainType ).getRequiredIdProperty ().getColumnType ();
402
- return new MapSqlParameterSource ("id" , converter .writeValue (id , ClassTypeInformation .from (columnType )));
379
+ MapSqlParameterSource parameterSource = new MapSqlParameterSource ();
380
+
381
+ addConvertedPropertyValue ( //
382
+ parameterSource , //
383
+ getRequiredPersistentEntity (domainType ).getRequiredIdProperty (), //
384
+ id , //
385
+ "id" //
386
+ );
387
+ return parameterSource ;
388
+ }
389
+
390
+ private void addConvertedPropertyValue (MapSqlParameterSource parameterSource , RelationalPersistentProperty property ,
391
+ Object value , String paramName ) {
392
+
393
+ JdbcValue jdbcValue = converter .writeJdbcValue ( //
394
+ value , //
395
+ property .getColumnType (), //
396
+ property .getSqlType () //
397
+ );
398
+
399
+ parameterSource .addValue (paramName , jdbcValue .getValue (), JdbcUtil .sqlTypeFor (jdbcValue .getJdbcType ()));
400
+ }
401
+
402
+ private void addConvertedPropertyValue (MapSqlParameterSource parameterSource , String name , Object value ,
403
+ Class <?> type ) {
404
+
405
+ JdbcValue jdbcValue = converter .writeJdbcValue ( //
406
+ value , //
407
+ type , //
408
+ JdbcUtil .sqlTypeFor (type ) //
409
+ );
410
+
411
+ parameterSource .addValue ( //
412
+ name , //
413
+ jdbcValue .getValue (), //
414
+ JdbcUtil .sqlTypeFor (jdbcValue .getJdbcType ()) //
415
+ );
416
+ }
417
+
418
+ private void addConvertedPropertyValuesAsList (MapSqlParameterSource parameterSource ,
419
+ RelationalPersistentProperty property , Iterable <?> values , String paramName ) {
420
+
421
+ List <Object > convertedIds = new ArrayList <>();
422
+ JdbcValue jdbcValue = null ;
423
+ for (Object id : values ) {
424
+
425
+ Class <?> columnType = property .getColumnType ();
426
+ int sqlType = property .getSqlType ();
427
+
428
+ jdbcValue = converter .writeJdbcValue (id , columnType , sqlType );
429
+ convertedIds .add (jdbcValue .getValue ());
430
+ }
431
+
432
+ Assert .notNull (jdbcValue , "JdbcValue must be not null at this point. Please report this as a bug." );
433
+
434
+ JDBCType jdbcType = jdbcValue .getJdbcType ();
435
+ int typeNumber = jdbcType == null ? JdbcUtils .TYPE_UNKNOWN : jdbcType .getVendorTypeNumber ();
436
+
437
+ parameterSource .addValue (paramName , convertedIds , typeNumber );
403
438
}
404
439
405
440
@ SuppressWarnings ("unchecked" )
0 commit comments