无注解DynamoDBTypeConverter适配不可变FooBar类问题
我刚好碰到过类似的不可变类与DynamoDB映射的场景,你的需求完全可以通过指定Map<String, AttributeValue>作为转换器的目标类型来解决——这正是DynamoDB底层用来表示属性结构的原生类型,完美匹配你存储的格式。
第一步:实现正确的DynamoDBTypeConverter
我们需要把FooBar和DynamoDB原生的AttributeValue结构做双向转换,转换器的类型参数应该定义为DynamoDBTypeConverter<Map<String, AttributeValue>, FooBar>。具体实现如下:
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverter; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import java.util.HashMap; import java.util.Map; public class FooBarConverter implements DynamoDBTypeConverter<Map<String, AttributeValue>, FooBar> { @Override public Map<String, AttributeValue> convert(FooBar fooBar) { Map<String, AttributeValue> attributeMap = new HashMap<>(); // 将int值转换为DynamoDB要求的数字类型(N类型) attributeMap.put("foo", new AttributeValue().withN(String.valueOf(fooBar.getFoo()))); attributeMap.put("bar", new AttributeValue().withN(String.valueOf(fooBar.getBar()))); return attributeMap; } @Override public FooBar unconvert(Map<String, AttributeValue> attributeMap) { // 从AttributeValue中提取数字字符串,转成int后构造不可变的FooBar实例 int foo = Integer.parseInt(attributeMap.get("foo").getN()); int bar = Integer.parseInt(attributeMap.get("bar").getN()); return new FooBar(foo, bar); } }
第二步:在表实体类中关联转换器
因为不能给FooBar本身添加注解,我们需要在包含FooBar字段的表实体类上,给对应的getter方法添加@DynamoDBTypeConverted注解来指定转换器。如果你的实体类也是不可变的,记得给构造方法加上@DynamoDBConstructor注解,让DynamoDBMapper能正确实例化它:
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBConstructor; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverted; @DynamoDBTable(tableName = "YourTargetTable") public class TableEntity { private final String id; private final FooBar fooBar; @DynamoDBConstructor public TableEntity(String id, FooBar fooBar) { this.id = id; this.fooBar = fooBar; } @DynamoDBHashKey public String getId() { return id; } @DynamoDBTypeConverted(converter = FooBarConverter.class) public FooBar getFooBar() { return fooBar; } }
第三步:使用DynamoDBMapper读写数据
现在就可以正常用映射器进行读写操作了,完全不需要修改FooBar的不可变结构:
// 初始化客户端和映射器 AmazonDynamoDB dynamoDBClient = AmazonDynamoDBClientBuilder.defaultClient(); DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient); // 写入数据 FooBar fooBar = new FooBar(45, 12); TableEntity entity = new TableEntity("item-001", fooBar); mapper.save(entity); // 读取数据 TableEntity retrievedEntity = mapper.load(TableEntity.class, "item-001"); System.out.println(retrievedEntity.getFooBar().getFoo()); // 输出45 System.out.println(retrievedEntity.getFooBar().getBar()); // 输出12
为什么之前的尝试失败?
- 普通Map类型:你用的应该是
Map<String, Object>,DynamoDB无法自动将这种通用Map转换成它的原生属性结构,必须明确使用Map<String, AttributeValue>才能匹配底层存储格式。 - String类型:DynamoDB会把字符串当成单一属性值存储,而你的数据是两个独立的属性(foo和bar),所以读取时自然拿不到正确的内容。
- Object类型:DynamoDB无法解析无明确类型信息的Object转换目标,所以会抛出类型解析异常。
内容的提问来源于stack exchange,提问作者Paul Davies
相关产品推荐
相关产品推荐

