diff --git a/.changes/next-release/bugfix-AmazonDynamoDB-c60c54b.json b/.changes/next-release/bugfix-AmazonDynamoDB-c60c54b.json new file mode 100644 index 000000000000..9fa332dcbc38 --- /dev/null +++ b/.changes/next-release/bugfix-AmazonDynamoDB-c60c54b.json @@ -0,0 +1,6 @@ +{ + "type": "bugfix", + "category": "Amazon DynamoDB", + "description": "Fix `TableSchema.fromBean` for mutually recursive `@DynamoDbBean` classes. ([#6110](https://github.com/aws/aws-sdk-java-v2/issues/6110))", + "contributor": "afarber" +} diff --git a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchema.java b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchema.java index 90f8231cd26c..92865a359435 100644 --- a/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchema.java +++ b/services-custom/dynamodb-enhanced/src/main/java/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchema.java @@ -192,8 +192,8 @@ private static BeanTableSchema create(BeanTableSchemaParams params, Me return newTableSchema; } - // Called when creating an immutable TableSchema recursively. Utilizes the MetaTableSchema cache to stop infinite - // recursion + // Called when creating a bean TableSchema recursively. Utilizes the MetaTableSchema cache to stop infinite + // recursion for self-referencing and mutually recursive schemas. static TableSchema recursiveCreate(Class beanClass, MethodHandles.Lookup lookup, MetaTableSchemaCache metaTableSchemaCache) { Optional> metaTableSchema = metaTableSchemaCache.get(beanClass); @@ -211,7 +211,8 @@ static TableSchema recursiveCreate(Class beanClass, MethodHandles.Look } // Otherwise: cache doesn't know about this class; create a new one from scratch - return create(BeanTableSchemaParams.builder(beanClass).lookup(lookup).build()); + return create(BeanTableSchemaParams.builder(beanClass).lookup(lookup).build(), metaTableSchemaCache, + ExecutionContext.ROOT); } @@ -603,4 +604,3 @@ static void clearSchemaCache() { BEAN_TABLE_SCHEMA_CACHE.clear(); } } - diff --git a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/BeanTableSchemaRecursiveTest.java b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/BeanTableSchemaRecursiveTest.java index e2919f9769c3..1d39945691c9 100644 --- a/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/BeanTableSchemaRecursiveTest.java +++ b/services-custom/dynamodb-enhanced/src/test/java/software/amazon/awssdk/enhanced/dynamodb/functionaltests/BeanTableSchemaRecursiveTest.java @@ -23,9 +23,28 @@ import software.amazon.awssdk.enhanced.dynamodb.TableSchema; import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.RecursiveRecordBean; import software.amazon.awssdk.enhanced.dynamodb.functionaltests.models.RecursiveRecordImmutable; +import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean; import software.amazon.awssdk.services.dynamodb.model.AttributeValue; public class BeanTableSchemaRecursiveTest { + @Test + public void transitiveRecursiveBean_document() { + TableSchema tableSchema = TableSchema.fromBean(TransitiveRecursiveParent.class); + + TransitiveRecursiveChild child = new TransitiveRecursiveChild(); + child.setName("child"); + + TransitiveRecursiveParent parent = new TransitiveRecursiveParent(); + parent.setName("parent"); + parent.setChild(child); + + TransitiveRecursiveParent roundTrip = tableSchema.mapToItem(tableSchema.itemToMap(parent, true)); + + assertThat(roundTrip.getName()).isEqualTo("parent"); + assertThat(roundTrip.getChild().getName()).isEqualTo("child"); + assertThat(roundTrip.getChild().getParent()).isNull(); + } + @Test public void recursiveRecord_document() { TableSchema tableSchema = TableSchema.fromClass(RecursiveRecordBean.class); @@ -89,4 +108,48 @@ public void recursiveRecord_list() { }); }); } + + @DynamoDbBean + public static class TransitiveRecursiveParent { + private String name; + private TransitiveRecursiveChild child; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public TransitiveRecursiveChild getChild() { + return child; + } + + public void setChild(TransitiveRecursiveChild child) { + this.child = child; + } + } + + @DynamoDbBean + public static class TransitiveRecursiveChild { + private String name; + private TransitiveRecursiveParent parent; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public TransitiveRecursiveParent getParent() { + return parent; + } + + public void setParent(TransitiveRecursiveParent parent) { + this.parent = parent; + } + } }