43
43
import software .amazon .awssdk .enhanced .dynamodb .TableSchema ;
44
44
import software .amazon .awssdk .enhanced .dynamodb .internal .mapper .BeanAttributeGetter ;
45
45
import software .amazon .awssdk .enhanced .dynamodb .internal .mapper .BeanAttributeSetter ;
46
+ import software .amazon .awssdk .enhanced .dynamodb .internal .mapper .MetaTableSchema ;
47
+ import software .amazon .awssdk .enhanced .dynamodb .internal .mapper .MetaTableSchemaCache ;
46
48
import software .amazon .awssdk .enhanced .dynamodb .internal .mapper .ObjectConstructor ;
47
49
import software .amazon .awssdk .enhanced .dynamodb .mapper .annotations .BeanTableSchemaAttributeTag ;
48
50
import software .amazon .awssdk .enhanced .dynamodb .mapper .annotations .DynamoDbAttribute ;
@@ -104,10 +106,43 @@ private BeanTableSchema(StaticTableSchema<T> staticTableSchema) {
104
106
* @return An initialized {@link BeanTableSchema}
105
107
*/
106
108
public static <T > BeanTableSchema <T > create (Class <T > beanClass ) {
107
- return new BeanTableSchema <>( createStaticTableSchema ( beanClass ));
109
+ return create ( beanClass , new MetaTableSchemaCache ( ));
108
110
}
109
111
110
- private static <T > StaticTableSchema <T > createStaticTableSchema (Class <T > beanClass ) {
112
+ private static <T > BeanTableSchema <T > create (Class <T > beanClass , MetaTableSchemaCache metaTableSchemaCache ) {
113
+ // Fetch or create a new reference to this yet-to-be-created TableSchema in the cache
114
+ MetaTableSchema <T > metaTableSchema = metaTableSchemaCache .getOrCreate (beanClass );
115
+
116
+ BeanTableSchema <T > newTableSchema =
117
+ new BeanTableSchema <>(createStaticTableSchema (beanClass , metaTableSchemaCache ));
118
+ metaTableSchema .initialize (newTableSchema );
119
+ return newTableSchema ;
120
+ }
121
+
122
+ // Called when creating an immutable TableSchema recursively. Utilizes the MetaTableSchema cache to stop infinite
123
+ // recursion
124
+ static <T > TableSchema <T > recursiveCreate (Class <T > beanClass , MetaTableSchemaCache metaTableSchemaCache ) {
125
+ Optional <MetaTableSchema <T >> metaTableSchema = metaTableSchemaCache .get (beanClass );
126
+
127
+ // If we get a cache hit...
128
+ if (metaTableSchema .isPresent ()) {
129
+ // Either: use the cached concrete TableSchema if we have one
130
+ if (metaTableSchema .get ().isInitialized ()) {
131
+ return metaTableSchema .get ().concreteTableSchema ();
132
+ }
133
+
134
+ // Or: return the uninitialized MetaTableSchema as this must be a recursive reference and it will be
135
+ // initialized later as the chain completes
136
+ return metaTableSchema .get ();
137
+ }
138
+
139
+ // Otherwise: cache doesn't know about this class; create a new one from scratch
140
+ return create (beanClass );
141
+
142
+ }
143
+
144
+ private static <T > StaticTableSchema <T > createStaticTableSchema (Class <T > beanClass ,
145
+ MetaTableSchemaCache metaTableSchemaCache ) {
111
146
DynamoDbBean dynamoDbBean = beanClass .getAnnotation (DynamoDbBean .class );
112
147
113
148
if (dynamoDbBean == null ) {
@@ -142,7 +177,7 @@ private static <T> StaticTableSchema<T> createStaticTableSchema(Class<T> beanCla
142
177
setterForProperty (propertyDescriptor , beanClass ));
143
178
} else {
144
179
StaticAttribute .Builder <T , ?> attributeBuilder =
145
- staticAttributeBuilder (propertyDescriptor , beanClass );
180
+ staticAttributeBuilder (propertyDescriptor , beanClass , metaTableSchemaCache );
146
181
147
182
Optional <AttributeConverter > attributeConverter =
148
183
createAttributeConverterFromAnnotation (propertyDescriptor );
@@ -167,10 +202,11 @@ private static List<AttributeConverterProvider> createConverterProvidersFromAnno
167
202
}
168
203
169
204
private static <T > StaticAttribute .Builder <T , ?> staticAttributeBuilder (PropertyDescriptor propertyDescriptor ,
170
- Class <T > beanClass ) {
205
+ Class <T > beanClass ,
206
+ MetaTableSchemaCache metaTableSchemaCache ) {
171
207
172
208
Type propertyType = propertyDescriptor .getReadMethod ().getGenericReturnType ();
173
- EnhancedType <?> propertyTypeToken = convertTypeToEnhancedType (propertyType );
209
+ EnhancedType <?> propertyTypeToken = convertTypeToEnhancedType (propertyType , metaTableSchemaCache );
174
210
return StaticAttribute .builder (beanClass , propertyTypeToken )
175
211
.name (attributeNameForProperty (propertyDescriptor ))
176
212
.getter (getterForProperty (propertyDescriptor , beanClass ))
@@ -185,20 +221,22 @@ private static List<AttributeConverterProvider> createConverterProvidersFromAnno
185
221
* EnhancedClient otherwise does all by itself.
186
222
*/
187
223
@ SuppressWarnings ("unchecked" )
188
- private static EnhancedType <?> convertTypeToEnhancedType (Type type ) {
224
+ private static EnhancedType <?> convertTypeToEnhancedType (Type type , MetaTableSchemaCache metaTableSchemaCache ) {
189
225
Class <?> clazz = null ;
190
226
191
227
if (type instanceof ParameterizedType ) {
192
228
ParameterizedType parameterizedType = (ParameterizedType ) type ;
193
229
Type rawType = parameterizedType .getRawType ();
194
230
195
231
if (List .class .equals (rawType )) {
196
- return EnhancedType .listOf (convertTypeToEnhancedType (parameterizedType .getActualTypeArguments ()[0 ]));
232
+ return EnhancedType .listOf (convertTypeToEnhancedType (parameterizedType .getActualTypeArguments ()[0 ],
233
+ metaTableSchemaCache ));
197
234
}
198
235
199
236
if (Map .class .equals (rawType )) {
200
237
return EnhancedType .mapOf (EnhancedType .of (parameterizedType .getActualTypeArguments ()[0 ]),
201
- convertTypeToEnhancedType (parameterizedType .getActualTypeArguments ()[1 ]));
238
+ convertTypeToEnhancedType (parameterizedType .getActualTypeArguments ()[1 ],
239
+ metaTableSchemaCache ));
202
240
}
203
241
204
242
if (rawType instanceof Class ) {
@@ -209,10 +247,14 @@ private static EnhancedType<?> convertTypeToEnhancedType(Type type) {
209
247
}
210
248
211
249
if (clazz != null ) {
212
- if (clazz .getAnnotation (DynamoDbImmutable .class ) != null
213
- || clazz .getAnnotation (DynamoDbBean .class ) != null ) {
214
- return EnhancedType .documentOf ((Class <Object >) clazz ,
215
- (TableSchema <Object >) TableSchema .fromClass (clazz ));
250
+ if (clazz .getAnnotation (DynamoDbImmutable .class ) != null ) {
251
+ return EnhancedType .documentOf (
252
+ (Class <Object >) clazz ,
253
+ (TableSchema <Object >) ImmutableTableSchema .recursiveCreate (clazz , metaTableSchemaCache ));
254
+ } else if (clazz .getAnnotation (DynamoDbBean .class ) != null ) {
255
+ return EnhancedType .documentOf (
256
+ (Class <Object >) clazz ,
257
+ (TableSchema <Object >) BeanTableSchema .recursiveCreate (clazz , metaTableSchemaCache ));
216
258
}
217
259
}
218
260
0 commit comments