1515 */
1616package org .springframework .data .redis .support .collections ;
1717
18+ import java .util .function .Supplier ;
19+
1820import org .springframework .beans .factory .BeanNameAware ;
1921import org .springframework .beans .factory .InitializingBean ;
2022import org .springframework .beans .factory .SmartFactoryBean ;
2123import org .springframework .data .redis .connection .DataType ;
24+ import org .springframework .data .redis .core .RedisOperations ;
2225import org .springframework .data .redis .core .RedisTemplate ;
2326import org .springframework .data .util .Lazy ;
2427import org .springframework .lang .Nullable ;
2730
2831/**
2932 * Factory bean that facilitates creation of Redis-based collections. Supports list, set, zset (or sortedSet), map (or
30- * hash) and properties. Will use the key type if it exists or to create a dedicated collection (Properties vs Map).
31- * Otherwise uses the provided type (default is list) .
33+ * hash) and properties. Uses the key and {@link CollectionType} to determine what collection type to use. The factory
34+ * verifies the key type if a {@link CollectionType} is specified. Defaults to {@link CollectionType#LIST} .
3235 *
3336 * @author Costin Leau
3437 * @author Christoph Strobl
38+ * @author Mark Paluch
39+ * @see RedisStore
3540 */
3641public class RedisCollectionFactoryBean implements SmartFactoryBean <RedisStore >, BeanNameAware , InitializingBean {
3742
3843 /**
3944 * Collection types supported by this factory.
4045 *
4146 * @author Costin Leau
47+ * @author Mark Paluch
4248 */
4349 public enum CollectionType {
4450 LIST {
4551
52+ @ Override
4653 public DataType dataType () {
4754 return DataType .LIST ;
4855 }
4956 },
5057 SET {
5158
59+ @ Override
5260 public DataType dataType () {
5361 return DataType .SET ;
5462 }
5563 },
5664 ZSET {
5765
66+ @ Override
5867 public DataType dataType () {
5968 return DataType .ZSET ;
6069 }
6170 },
6271 MAP {
6372
73+ @ Override
6474 public DataType dataType () {
6575 return DataType .HASH ;
6676 }
6777 },
6878 PROPERTIES {
6979
80+ @ Override
7081 public DataType dataType () {
7182 return DataType .HASH ;
7283 }
7384 };
7485
7586 abstract DataType dataType ();
87+
88+ /**
89+ * Attempt to find a {@link CollectionType} by {@link DataType}. Defaults to {@link Supplier ifNotFound} when
90+ * {@code dataType} is {@literal null} or the collection type cannot be determined.
91+ *
92+ * @param dataType the {@link DataType} to look up.
93+ * @param ifNotFound supplier for a default value.
94+ * @since 3.2
95+ */
96+ static CollectionType findCollectionType (@ Nullable DataType dataType , Supplier <CollectionType > ifNotFound ) {
97+
98+ for (CollectionType collectionType : values ()) {
99+ if (collectionType .dataType () == dataType ) {
100+ return collectionType ;
101+ }
102+ }
103+
104+ return ifNotFound .get ();
105+ }
76106 }
77107
78- private @ Nullable Lazy <RedisStore > store ;
79- private @ Nullable CollectionType type = null ;
108+ private @ Nullable CollectionType type ;
80109 private @ Nullable RedisTemplate <String , ?> template ;
81110 private @ Nullable String key ;
82111 private @ Nullable String beanName ;
83112
113+ private @ Nullable Lazy <RedisStore > store ;
114+
84115 @ Override
85116 public void afterPropertiesSet () {
86117
@@ -93,46 +124,40 @@ public void afterPropertiesSet() {
93124
94125 store = Lazy .of (() -> {
95126
96- DataType dt = template .type (key );
127+ DataType keyType = template .type (key );
97128
98129 // can't create store
99- Assert .isTrue (!DataType .STRING .equals (dt ), "Cannot create store on keys of type 'string '" );
130+ Assert .isTrue (!DataType .STREAM .equals (keyType ), "Cannot create store on keys of type 'STREAM '" );
100131
101- RedisStore tmp = createStore (dt );
132+ if (this .type == null ) {
133+ this .type = CollectionType .findCollectionType (keyType , () -> CollectionType .LIST );
134+ }
102135
103- if (tmp == null ) {
104- if (type == null ) {
105- type = CollectionType .LIST ;
106- }
107- tmp = createStore (type .dataType ());
136+ if (keyType != null && DataType .NONE != keyType && this .type .dataType () != keyType ) {
137+ throw new IllegalArgumentException (
138+ String .format ("Cannot create collection type '%s' for a key containing '%s'" , this .type , keyType ));
108139 }
109- return tmp ;
140+
141+ return createStore (this .type , key , template );
110142 });
111143 }
112144
113- @ SuppressWarnings ("unchecked" )
114- private RedisStore createStore (DataType dt ) {
115- switch (dt ) {
116- case LIST :
117- return RedisList .create (key , template );
118-
119- case SET :
120- return new DefaultRedisSet (key , template );
121-
122- case ZSET :
123- return RedisZSet .create (key , template );
145+ private RedisStore createStore (CollectionType collectionType , String key , RedisOperations <String , ?> operations ) {
124146
125- case HASH :
126- if ( CollectionType . PROPERTIES . equals ( type )) {
127- return new RedisProperties (key , template );
128- }
129- return new DefaultRedisMap (key , template );
130- }
131- return null ;
147+ return switch ( collectionType ) {
148+ case LIST -> RedisList . create ( key , operations );
149+ case SET -> new DefaultRedisSet <> (key , operations );
150+ case ZSET -> RedisZSet . create ( key , operations );
151+ case PROPERTIES -> new RedisProperties (key , operations );
152+ case MAP -> new DefaultRedisMap <>( key , operations );
153+ } ;
132154 }
133155
134156 @ Override
135157 public RedisStore getObject () {
158+
159+ Assert .state (store != null ,
160+ "RedisCollectionFactoryBean is not initialized. Ensure to initialize this factory by calling afterPropertiesSet() before obtaining the factory object." );
136161 return store .get ();
137162 }
138163
0 commit comments