我正在使用 DynamoDB 表,其中每个分区键都与多个排序键关联。我需要获取一个分区键中的所有项目。我知道我需要获取具有相同分区键和不同排序键以及各自属性的这些项目的列表。然后根据需要将它们映射到单个 DTO 模型。我设置了一个带有分区 (
ProfileModel
) 和排序 (pk
) 键的 sk
,以及用于保存各种属性的 Map<String, AttributeValue>
。但是,应用程序在启动过程中会抛出 IllegalStateException
,表明无法找到 EnhancedType<Map<String, AttributeValue>>
的转换器。我怀疑这可能与 MapAttributeConverter
的集成方式或可能缺少的配置有关。以下是相关代码片段和错误消息:
ProfileDynamoDb.java:
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import software.amazon.awssdk.core.pagination.sync.SdkIterable;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
@Repository
public class ProfileDynamoDb {
private final DynamoDbTable<ProfileModel> table;
private final DynamoDbEnhancedClient enhancedClient;
@Autowired
public ProfileDynamoDb(DynamoDbEnhancedClient enhancedClient) {
this.enhancedClient = enhancedClient;
table = enhancedClient.table("profiles", TableSchema.fromBean(ProfileModel.class));
}
public List<ProfileModel> fetchByPartitionKey(String profileName) {
try {
QueryConditional queryConditional = QueryConditional.keyEqualTo(k -> k.partitionValue("PROFILE#" + profileName));
SdkIterable<ProfileModel> results = table.query(r -> r.queryConditional(queryConditional)).items();
return results.stream().toList();
} catch (DynamoDbException e) {
log.error("Error while getting profile by name: {}", profileName, e);
throw e;
}
}
ProfileModel.java:
import lombok.Data;
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.MapAttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import java.util.HashMap;
import java.util.Map;
@Data
@DynamoDbBean
public class ProfileModel {
private String pk;
private String sk;
private Map<String, AttributeValue> attributes = new HashMap<>();
public void addAttribute(String key, AttributeValue value) {
attributes.put(key, value);
}
@DynamoDbAttribute("Attributes")
@DynamoDbConvertedBy(MapAttributeConverter.class)
public Object getAttribute(String key) {
return attributes.get(key);
}
@DynamoDbPartitionKey
public String getPk() {
return pk;
}
@DynamoDbSortKey
public String getSk() {
return sk;
}
}
错误信息:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.booking.profile.infrastructure.dynamodb.ProfileDynamoDb]: Constructor threw exception
Caused by: java.lang.IllegalStateException: Converter not found for EnhancedType(java.util.Map<java.lang.String, software.amazon.awssdk.services.dynamodb.model.AttributeValue>)
有人可以帮助确定可能导致转换器问题的原因以及如何解决它吗?是否有特定的方法来使用 Spring Boot 和 AWS SDK for Java 为 DynamoDB 实体中的
Map
属性定义模型和 DAO?或者有没有办法对具有相同分区键和不同软键的许多属性执行相同的操作,例如当您需要获取单个项目时,DynamoDB 库本身会映射所有属性,
您的模型定义了一个
Map<String, AttributeValue>
来存储各种属性。@DynamoDbConvertedBy(MapAttributeConverter.class)
不是 SDK 中针对此类类型提供的标准转换器,您可以考虑实现一个实现 AttributeConverter<Map<String, AttributeValue>>
接口的自定义属性转换器。
import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.AttributeConverterContext;
import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import java.util.HashMap;
import java.util.Map;
public class MapAttributeValueConverter implements AttributeConverter<Map<String, AttributeValue>> {
@Override
public AttributeValue transformFrom(Map<String, AttributeValue> input, AttributeConverterContext context) {
return AttributeValue.builder().m(input).build();
}
@Override
public Map<String, AttributeValue> transformTo(AttributeValue input, AttributeConverterContext context) {
return input.m();
}
@Override
public EnhancedType<Map<String, AttributeValue>> type() {
return EnhancedType.mapOf(String.class, AttributeValue.class);
}
@Override
public AttributeValueType attributeValueType() {
return AttributeValueType.M;
}
}
然后您需要更新您的
ProfileModel
才能使用这个新转换器:
@DynamoDbConvertedBy(MapAttributeValueConverter.class)
public Map<String, AttributeValue> getAttributes() {
return attributes;
}
自定义属性转换器
MapAttributeValueConverter
解决了在 DynamoDB 项目中处理 Map<String, AttributeValue>
的具体问题。
更直接地解决您的问题:
获取具有相同分区键和不同排序键的项目:这是 DynamoDB 中的常见访问模式,并通过查询操作原生支持。
当您执行仅指定分区键的查询操作时,DynamoDB 会检索共享该分区键的所有项目,无论其排序键是什么。这就是您如何获取按分区键逻辑分组在一起但按排序键区分的多个项目。
请参阅“复合主键”。
映射属性:获取项目时,DynamoDB 增强型客户端会根据您在模型类中提供的注释,自动将从 DynamoDB 获取的项目的属性映射到模型类的字段。
如果您的模型中有一个字段被注释为处理
Map<String, AttributeValue>
,则自定义属性转换器 (MapAttributeValueConverter
) 将发挥作用,将 DynamoDB AttributeValue
类型转换为 Map<String, AttributeValue>
类型,反之亦然。
关注
Map<String, AttributeValue>
字段:如果您的目的是动态处理单个项目中的一组可变属性,则使用Map<String, AttributeValue>
是一种有效的方法。自定义转换器使您能够存储和检索这些动态属性作为模型的一部分。我应该使用哪个版本的 Dynamodb?有
课。此外,没有AttributeConverterContext
和transformFrom
接受两个参数。To
我用的是版本。implementation("software.amazon.awssdk:dynamodb-enhanced:2.17.128")
在这种情况下,那就是:
import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import java.util.Map;
public class MapAttributeValueConverter implements AttributeConverter<Map<String, AttributeValue>> {
@Override
public AttributeValue transformFrom(Map<String, AttributeValue> input) {
return AttributeValue.builder().m(input).build();
}
@Override
public Map<String, AttributeValue> transformTo(AttributeValue input) {
return input.m();
}
@Override
public EnhancedType<Map<String, AttributeValue>> type() {
return EnhancedType.of(new TypeToken<Map<String, AttributeValue>>() {});
}
@Override
public AttributeValueType attributeValueType() {
return AttributeValueType.M;
}
}
这将包括以下更改:
transformFrom
和 transformTo
方法现在仅正确接受相关输入(对于 Map<String, AttributeValue>
为 transformFrom
,对于 AttributeValue
为 transformTo
),并返回不带 AttributeConverterContext
的适当类型。EnhancedType.of
与 new TypeToken<Map<String, AttributeValue>>(){}
一起使用可能需要根据您的具体实现或 JDK 版本进行调整。如果 TypeToken
不直接可用或导致问题,您可以简单地使用 EnhancedType.mapOf(String.class, AttributeValue.class)
作为更直接的方法来为 EnhancedType
指定 Map<String, AttributeValue>
。