繁体   English   中英

如何将 Java 枚举与 Amazon DynamoDB 和 AWS SDK v2 一起使用?

[英]How can I use Java Enums with Amazon DynamoDB and AWS SDK v2?

我正在尝试为 AWS 实现一个简单的 java 事件处理程序 lambda。 它接收 sqs 事件并且应该对 dynamoDB 表进行适当的更新。

此表中的属性之一是具有 4 个已定义状态的状态字段; 因此我想在 java 和 map 中使用枚举 class 到此属性。

在 AWS SDK v1 下,我可以使用 @DynamoDBTypeConvertedEnum 注释。 但它在 v2 中不再存在。 相反,有一个 @DynamoDbConvertedBy() 接收转换器 class 引用。 还有一个 EnumAttributeConverter class 应该可以很好地配合它。

但由于某种原因,它不起作用。 以下是我当前代码的片段:

@Data
@DynamoDbBean
@NoArgsConstructor
public class Task{

@Getter(onMethod_ = {@DynamoDbPartitionKey})  
    String id; 

...

@Getter(onMethod_ = {@DynamoDbConvertedBy(EnumAttributeConverter.class)})
    ExportTaskStatus status;
}

枚举如下所示:

@RequiredArgsConstructor
public enum TaskStatus {
    @JsonProperty("running") PROCESSING(1),
    @JsonProperty("succeeded") COMPLETED(2),
    @JsonProperty("cancelled") CANCELED(3),
    @JsonProperty("failed") FAILED(4);

    private final int order;
}

有了这个,我在启动应用程序时得到以下异常:

Class 'class software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.EnumAttributeConverter' appears to have no default constructor thus cannot be used with the BeanTableSchema

dynamodb-enhanced SDK 开箱即用。

当您声明@DynamoDbBean时, DefaultAttributeConverterProvider会提供一长串在 java 类型之间转换属性的可能方法,包括在EnumAttributeConverter type.rawClass().isEnum()为真时使用的 EnumAttributeConverter。 所以你不必担心。

如果您想扩展转换器的数量,则需要添加converterProviders注释参数,并声明默认的(或省略它)以及您想要的任何其他提供程序。

示例: @DynamoDbBean(converterProviders = { DefaultAttributeConverterProvider.class, MyCustomAttributeConverterProvider.class });

我认为您的注释实际上可能是这里的问题。 我会删除所有提到构造函数的注释,而是写出你自己的构造函数。 对于TaskTaskStatus

如何将 Java 枚举与 Amazon DynamoDB 和 AWS SDK v2 结合使用?

尽管文档没有说明,但DynamoDbConvertedBy注释要求您提供的任何AttriuteConverter包含参数默认构造函数

不幸的是,对于你和我来说,编写许多内置AttributeConverter类的人决定使用静态create()方法来实例化它们而不是构造函数(也许它们是幕后的单例?我不知道)。 这意味着任何想要使用这些有用的无构造函数类(如InstantAsStringAttributeConverterEnumAttributeConverter需要将它们包装在自定义包装类中,以便简单地EnumAttributeConverter我们使用create实例化的转换器。 对于像InstantAsStringAttributeConverter这样的非泛型类型类,这很容易。 只需创建一个包装类,它会模仿您使用create()新建的实例,并改为引用它:

public class InstantAsStringAttributeConverterWithConstructor implements AttributeConverter<Instant> {
    private final static InstantAsStringAttributeConverter CONVERTER = InstantAsStringAttributeConverter.create();

    @Override
    public AttributeValue transformFrom(Instant instant) {
        return CONVERTER.transformFrom(instant);
    }

    @Override
    public Instant transformTo(AttributeValue attributeValue) {
        return CONVERTER.transformTo(attributeValue);
    }

    @Override
    public EnhancedType<Instant> type() {
        return CONVERTER.type();
    }

    @Override
    public AttributeValueType attributeValueType() {
        return CONVERTER.attributeValueType();
    }
}

然后更新注释以指向该类而不是实际的底层库类。

但是等等, EnumAttributeConverter是一个泛型类型类,这意味着您需要更进一步。 首先,您需要创建一个包装官方版本的转换器版本,但依赖于接受类型而不是静态实例化的构造函数:

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.enhanced.dynamodb.internal.converter.attribute.EnumAttributeConverter;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;

public class EnumAttributeConverterWithConstructor<T extends Enum<T>> implements AttributeConverter<T> {
    private final EnumAttributeConverter<T> converter;

    public CustomEnumAttributeConverter(final Class<T> enumClass) {
        this.converter = EnumAttributeConverter.create(enumClass);
    }

    @Override
    public AttributeValue transformFrom(T t) {
        return this.converter.transformFrom(t);
    }

    @Override
    public T transformTo(AttributeValue attributeValue) {
        return this.converter.transformTo(attributeValue);
    }

    @Override
    public EnhancedType<T> type() {
        return this.converter.type();
    }

    @Override
    public AttributeValueType attributeValueType() {
        return this.converter.attributeValueType();
    }
}

但这只会让我们走到一半——现在我们需要为我们想要转换的每个枚举类型生成一个版本,这些类型是我们自定义类的子类:

public class ExportTaskStatusAttributeConverter extends EnumAttributeConverterWithConstructor<ExportTaskStatus> {
    public ExportTaskStatusAttributeConverter() {
        super(ExportTaskStatus.class);
    }
}
@DynamoDbConvertedBy(ExportTaskStatusAttributeConverter.class)
public ExportTaskStatus getStatus() { return this.status; }

或 Lombok-y 方式:

@Getter(onMethod_ = {@DynamoDbConvertedBy(ExportTaskStatusAttributeConverter.class)})
ExportTaskStatus status;

这是一种痛苦。 这是一个痛苦,可以通过在 AWS 开发工具包中进行一点调整和一点点反射来解决,但这就是我们现在所处的位置。

基于watkinsmatthewp<\/a>的解决方案

public class TaskStatusConverter implements AttributeConverter<TaskStatus> {
    @Delegate
    private final EnumAttributeConverter<TaskStatus> converter;

    public TaskStatusConverter() {
        converter = EnumAttributeConverter.create(TaskStatus.class);
    }
}

对于来到这里的其他人来说,我看起来就像只是从枚举中删除注释完全可以正常工作,即 SDK 隐式应用提供的属性转换器。 这个Github issue<\/a>中也提到了这一点。 我自己的类看起来像这样(Brand 在这里是一个枚举),并且枚举在获取项目时转换没有任何问题。

@Value
@Builder(toBuilder = true)
@DynamoDbImmutable(builder = User.UserBuilder.class)
public class User {

    @Getter(onMethod = @__({@DynamoDbPartitionKey}))
    String id;

    Brand brand;
    ...
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM