DynamoDB 表模型代码支持具有多个排序键的设计

问题描述 投票:0回答:1

我正在使用 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 库本身会映射所有属性,

java amazon-dynamodb nosql key-value-store
1个回答
1
投票

您的模型定义了一个

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 处理查询和映射的基本方式;它只是允许您的应用程序在模型结构的限制内灵活处理动态属性。


我应该使用哪个版本的 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>
© www.soinside.com 2019 - 2024. All rights reserved.