48
48
import org .springframework .data .mapping .PersistentPropertyAccessor ;
49
49
import org .springframework .data .mapping .PreferredConstructor ;
50
50
import org .springframework .data .mapping .PropertyHandler ;
51
+ import org .springframework .data .mapping .model .MappingException ;
51
52
import org .springframework .data .mapping .model .PersistentEntityParameterValueProvider ;
52
53
import org .springframework .data .mapping .model .PropertyValueProvider ;
53
54
import org .springframework .data .redis .core .index .Indexed ;
107
108
*/
108
109
public class MappingRedisConverter implements RedisConverter , InitializingBean {
109
110
111
+ private static final String TYPE_HINT_ALIAS = "_class" ;
112
+
110
113
private final RedisMappingContext mappingContext ;
111
114
private final GenericConversionService conversionService ;
112
115
private final EntityInstantiators entityInstantiators ;
@@ -231,41 +234,9 @@ public void doWithPersistentProperty(KeyValuePersistentProperty persistentProper
231
234
232
235
else if (persistentProperty .isCollectionLike ()) {
233
236
234
- if (conversionService .canConvert (byte [].class , persistentProperty .getComponentType ())) {
235
-
236
- Object targetValue = null ;
237
- if (persistentProperty .getType ().isArray ()) {
238
-
239
- List <Object > list = (List <Object >) readCollectionOfSimpleTypes (currentPath , ArrayList .class ,
240
- persistentProperty .getTypeInformation ().getComponentType ().getActualType ().getType (), source );
241
-
242
- targetValue = list .toArray ((Object []) Array .newInstance (
243
- persistentProperty .getTypeInformation ().getComponentType ().getActualType ().getType (), list .size ()));
244
- } else {
245
- targetValue = readCollectionOfSimpleTypes (currentPath , persistentProperty .getType (),
246
- persistentProperty .getTypeInformation ().getComponentType ().getActualType ().getType (), source );
247
- }
248
-
249
- accessor .setProperty (persistentProperty , targetValue );
250
- } else {
251
-
252
- Object targetValue = null ;
253
- if (persistentProperty .getType ().isArray ()) {
254
-
255
- List <Object > list = (List <Object >) readCollectionOfComplexTypes (currentPath , ArrayList .class ,
256
- persistentProperty .getTypeInformation ().getComponentType ().getActualType ().getType (),
257
- source .getBucket ());
258
-
259
- targetValue = list .toArray ((Object []) Array .newInstance (
260
- persistentProperty .getTypeInformation ().getComponentType ().getActualType ().getType (), list .size ()));
261
- } else {
262
-
263
- targetValue = readCollectionOfComplexTypes (currentPath , persistentProperty .getType (),
264
- persistentProperty .getTypeInformation ().getComponentType ().getActualType ().getType (),
265
- source .getBucket ());
266
- }
267
- accessor .setProperty (persistentProperty , targetValue );
268
- }
237
+ Object targetValue = readCollectionOrArray (currentPath , persistentProperty .getType (),
238
+ persistentProperty .getTypeInformation ().getComponentType ().getActualType ().getType (), source .getBucket ());
239
+ accessor .setProperty (persistentProperty , targetValue );
269
240
270
241
} else if (persistentProperty .isEntity () && !conversionService .canConvert (byte [].class ,
271
242
persistentProperty .getTypeInformation ().getActualType ().getType ())) {
@@ -276,9 +247,9 @@ else if (persistentProperty.isCollectionLike()) {
276
247
277
248
RedisData source = new RedisData (bucket );
278
249
279
- byte [] type = bucket .get (currentPath + "._class" );
250
+ byte [] type = bucket .get (currentPath + "." + TYPE_HINT_ALIAS );
280
251
if (type != null && type .length > 0 ) {
281
- source .getBucket ().put ("_class" , type );
252
+ source .getBucket ().put (TYPE_HINT_ALIAS , type );
282
253
}
283
254
284
255
accessor .setProperty (persistentProperty , readInternal (currentPath , targetType , source ));
@@ -294,8 +265,8 @@ else if (persistentProperty.isCollectionLike()) {
294
265
}
295
266
}
296
267
297
- accessor . setProperty ( persistentProperty ,
298
- fromBytes (source .getBucket ().get (currentPath ), persistentProperty . getActualType () ));
268
+ Class <?> typeToUse = getTypeHint ( currentPath , source . getBucket (), persistentProperty . getActualType ());
269
+ accessor . setProperty ( persistentProperty , fromBytes (source .getBucket ().get (currentPath ), typeToUse ));
299
270
}
300
271
}
301
272
@@ -405,16 +376,17 @@ private void writeInternal(final String keyspace, final String path, final Objec
405
376
406
377
if (customConversions .hasCustomWriteTarget (value .getClass ())) {
407
378
408
- if (customConversions .getCustomWriteTarget (value .getClass ()).equals (byte [].class )) {
379
+ if (! StringUtils . hasText ( path ) && customConversions .getCustomWriteTarget (value .getClass ()).equals (byte [].class )) {
409
380
sink .getBucket ().put (StringUtils .hasText (path ) ? path : "_raw" , conversionService .convert (value , byte [].class ));
410
381
} else {
411
- writeToBucket (path , value , sink );
382
+ writeToBucket (path , value , sink , typeHint . getType () );
412
383
}
413
384
return ;
414
385
}
415
386
416
387
if (value .getClass () != typeHint .getType ()) {
417
- sink .getBucket ().put ((!path .isEmpty () ? path + "._class" : "_class" ), toBytes (value .getClass ().getName ()));
388
+ sink .getBucket ().put ((!path .isEmpty () ? path + "." + TYPE_HINT_ALIAS : TYPE_HINT_ALIAS ),
389
+ toBytes (value .getClass ().getName ()));
418
390
}
419
391
420
392
final KeyValuePersistentEntity <?> entity = mappingContext .getPersistentEntity (value .getClass ());
@@ -459,7 +431,7 @@ public void doWithPersistentProperty(KeyValuePersistentProperty persistentProper
459
431
} else {
460
432
461
433
Object propertyValue = accessor .getProperty (persistentProperty );
462
- sink . getBucket (). put ( propertyStringPath , toBytes ( propertyValue ));
434
+ writeToBucket ( propertyStringPath , propertyValue , sink , persistentProperty . getType ( ));
463
435
}
464
436
}
465
437
});
@@ -541,15 +513,15 @@ private void writeCollection(String keyspace, String path, Iterable<?> values, T
541
513
String currentPath = path + ".[" + i + "]" ;
542
514
543
515
if (customConversions .hasCustomWriteTarget (value .getClass ())) {
544
- writeToBucket (currentPath , value , sink );
516
+ writeToBucket (currentPath , value , sink , typeHint . getType () );
545
517
} else {
546
518
writeInternal (keyspace , currentPath , value , typeHint , sink );
547
519
}
548
520
i ++;
549
521
}
550
522
}
551
523
552
- private void writeToBucket (String path , Object value , RedisData sink ) {
524
+ private void writeToBucket (String path , Object value , RedisData sink , Class <?> propertyType ) {
553
525
554
526
if (value == null ) {
555
527
return ;
@@ -559,6 +531,12 @@ private void writeToBucket(String path, Object value, RedisData sink) {
559
531
560
532
Class <?> targetType = customConversions .getCustomWriteTarget (value .getClass ());
561
533
534
+ if (!ClassUtils .isAssignable (Map .class , targetType ) && customConversions .isSimpleType (value .getClass ())
535
+ && value .getClass () != propertyType ) {
536
+ sink .getBucket ().put ((!path .isEmpty () ? path + "." + TYPE_HINT_ALIAS : TYPE_HINT_ALIAS ),
537
+ toBytes (value .getClass ().getName ()));
538
+ }
539
+
562
540
if (ClassUtils .isAssignable (Map .class , targetType )) {
563
541
564
542
Map <?, ?> map = (Map <?, ?>) conversionService .convert (value , targetType );
@@ -576,59 +554,37 @@ private void writeToBucket(String path, Object value, RedisData sink) {
576
554
577
555
}
578
556
579
- /**
580
- * @param path
581
- * @param collectionType
582
- * @param valueType
583
- * @param source
584
- * @return
585
- */
586
- private Collection <?> readCollectionOfSimpleTypes (String path , Class <?> collectionType , Class <?> valueType ,
587
- RedisData source ) {
588
-
589
- Bucket partial = source .getBucket ().extract (path + ".[" );
557
+ private Object readCollectionOrArray (String path , Class <?> collectionType , Class <?> valueType , Bucket bucket ) {
590
558
591
- List <String > keys = new ArrayList <String >(partial . keySet ( ));
559
+ List <String > keys = new ArrayList <String >(bucket . extractAllKeysFor ( path ));
592
560
Collections .sort (keys , listKeyComparator );
593
561
594
- Collection <Object > target = CollectionFactory .createCollection (collectionType , valueType , partial .size ());
562
+ boolean isArray = collectionType .isArray ();
563
+ Class <?> collectionTypeToUse = isArray ? ArrayList .class : collectionType ;
564
+ Collection <Object > target = CollectionFactory .createCollection (collectionTypeToUse , valueType , keys .size ());
595
565
596
566
for (String key : keys ) {
597
- target .add (fromBytes (partial .get (key ), valueType ));
598
- }
599
567
600
- return target ;
601
- }
602
-
603
- /**
604
- * @param path
605
- * @param collectionType
606
- * @param valueType
607
- * @param source
608
- * @return
609
- */
610
- private Collection <?> readCollectionOfComplexTypes (String path , Class <?> collectionType , Class <?> valueType ,
611
- Bucket source ) {
612
-
613
- List <String > keys = new ArrayList <String >(source .extractAllKeysFor (path ));
614
- Collections .sort (keys , listKeyComparator );
615
-
616
- Collection <Object > target = CollectionFactory .createCollection (collectionType , valueType , keys .size ());
617
-
618
- for (String key : keys ) {
568
+ if (key .endsWith (TYPE_HINT_ALIAS )) {
569
+ continue ;
570
+ }
619
571
620
- Bucket elementData = source .extract (key );
572
+ Bucket elementData = bucket .extract (key );
621
573
622
- byte [] typeInfo = elementData .get (key + "._class" );
574
+ byte [] typeInfo = elementData .get (key + "." + TYPE_HINT_ALIAS );
623
575
if (typeInfo != null && typeInfo .length > 0 ) {
624
- elementData .put ("_class" , typeInfo );
576
+ elementData .put (TYPE_HINT_ALIAS , typeInfo );
625
577
}
626
578
627
- Object o = readInternal (key , valueType , new RedisData (elementData ));
628
- target .add (o );
579
+ Class <?> typeToUse = getTypeHint (key , elementData , valueType );
580
+ if (conversionService .canConvert (byte [].class , typeToUse )) {
581
+ target .add (fromBytes (elementData .get (key ), typeToUse ));
582
+ } else {
583
+ target .add (readInternal (key , valueType , new RedisData (elementData )));
584
+ }
629
585
}
630
586
631
- return target ;
587
+ return isArray ? target . toArray (( Object []) Array . newInstance ( valueType , target . size ())) : target ;
632
588
}
633
589
634
590
/**
@@ -653,7 +609,7 @@ private void writeMap(String keyspace, String path, Class<?> mapValueType, Map<?
653
609
String currentPath = path + ".[" + entry .getKey () + "]" ;
654
610
655
611
if (customConversions .hasCustomWriteTarget (entry .getValue ().getClass ())) {
656
- writeToBucket (currentPath , entry .getValue (), sink );
612
+ writeToBucket (currentPath , entry .getValue (), sink , mapValueType );
657
613
} else {
658
614
writeInternal (keyspace , currentPath , entry .getValue (), ClassTypeInformation .from (mapValueType ), sink );
659
615
}
@@ -677,6 +633,10 @@ private void writeMap(String keyspace, String path, Class<?> mapValueType, Map<?
677
633
678
634
for (Entry <String , byte []> entry : partial .entrySet ()) {
679
635
636
+ if (entry .getKey ().endsWith (TYPE_HINT_ALIAS )) {
637
+ continue ;
638
+ }
639
+
680
640
String regex = "^(" + Pattern .quote (path ) + "\\ .\\ [)(.*?)(\\ ])" ;
681
641
Pattern pattern = Pattern .compile (regex );
682
642
@@ -686,7 +646,9 @@ private void writeMap(String keyspace, String path, Class<?> mapValueType, Map<?
686
646
String .format ("Cannot extract map value for key '%s' in path '%s'." , entry .getKey (), path ));
687
647
}
688
648
String key = matcher .group (2 );
689
- target .put (key , fromBytes (entry .getValue (), valueType ));
649
+
650
+ Class <?> typeToUse = getTypeHint (path + ".[" + key + "]" , source .getBucket (), valueType );
651
+ target .put (key , fromBytes (entry .getValue (), typeToUse ));
690
652
}
691
653
692
654
return target ;
@@ -721,9 +683,9 @@ private void writeMap(String keyspace, String path, Class<?> mapValueType, Map<?
721
683
722
684
Bucket partial = source .getBucket ().extract (key );
723
685
724
- byte [] typeInfo = partial .get (key + "._class" );
686
+ byte [] typeInfo = partial .get (key + "." + TYPE_HINT_ALIAS );
725
687
if (typeInfo != null && typeInfo .length > 0 ) {
726
- partial .put ("_class" , typeInfo );
688
+ partial .put (TYPE_HINT_ALIAS , typeInfo );
727
689
}
728
690
729
691
Object o = readInternal (key , valueType , new RedisData (partial ));
@@ -733,6 +695,24 @@ private void writeMap(String keyspace, String path, Class<?> mapValueType, Map<?
733
695
return target ;
734
696
}
735
697
698
+ private Class <?> getTypeHint (String path , Bucket bucket , Class <?> fallback ) {
699
+
700
+ byte [] typeInfo = bucket .get (path + "." + TYPE_HINT_ALIAS );
701
+
702
+ if (typeInfo == null || typeInfo .length < 1 ) {
703
+ return fallback ;
704
+ }
705
+
706
+ String typeName = fromBytes (typeInfo , String .class );
707
+ try {
708
+ return ClassUtils .forName (typeName , this .getClass ().getClassLoader ());
709
+ } catch (ClassNotFoundException e ) {
710
+ throw new MappingException (String .format ("Cannot find class for type %s. " , typeName ), e );
711
+ } catch (LinkageError e ) {
712
+ throw new MappingException (String .format ("Cannot find class for type %s. " , typeName ), e );
713
+ }
714
+ }
715
+
736
716
/**
737
717
* Convert given source to binary representation using the underlying {@link ConversionService}.
738
718
*
@@ -836,7 +816,7 @@ private static class RedisTypeAliasAccessor implements TypeAliasAccessor<RedisDa
836
816
private final ConversionService conversionService ;
837
817
838
818
RedisTypeAliasAccessor (ConversionService conversionService ) {
839
- this (conversionService , "_class" );
819
+ this (conversionService , TYPE_HINT_ALIAS );
840
820
}
841
821
842
822
RedisTypeAliasAccessor (ConversionService conversionService , String typeKey ) {
0 commit comments