如何将Java Enums与Amazon DynamoDB和AWS SDK v2配合使用?

zsohkypk  于 2023-04-04  发布在  Java
关注(0)|答案(5)|浏览(152)

我正在尝试为AWS实现一个简单的java事件处理器lambda。它接收sqs事件,并对dynamoDB表进行适当的更新。
此表中的属性之一是具有4个已定义状态的状态字段;因此,我想在java中使用一个枚举类,并将其Map到这个属性。
在AWS SDK v1中,我可以使用@DynamoDBTypeConvertedEnum注解。但在v2中它不再存在。取而代之的是@DynamoDbConvertedBy(),它接收转换器类引用。还有一个EnumAttributeConverter类,它应该可以很好地与它一起工作。
但由于某种原因,它不起作用。下面是我当前代码的一个片段:

@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
2mbi3lxu

2mbi3lxu1#

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

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

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

    Brand brand;
    ...
}
h43kikqp

h43kikqp2#

如何将Java Enums与Amazon DynamoDB和AWS SDK v2配合使用?
尽管documentation没有说明,但DynamoDbConvertedBy注解要求您提供的任何AttriuteConverter包含parameterles默认构造函数
不幸的是编写许多内置AttributeConverter类的人决定使用静态create()方法而不是构造函数来示例化它们(也许它们是隐藏的单例?我不知道)。这意味着任何想要使用这些有用的构造函数的人-较少的类,如InstantAsStringAttributeConverterEnumAttributeConverter需要将它们 Package 在自定义 Package 器类中,这些 Package 器类简单地模仿我们使用create示例化的转换器。对于非泛型类型类,如InstantAsStringAttributeConverter,这很简单。只需创建一个 Package 器类,它会模仿你用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是一个泛型类型类,这意味着你需要更进一步。首先,你需要创建一个转换器的版本,它 Package 了官方版本,但依赖于一个接受类型的构造函数,而不是静态示例化:

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();
    }
}

但这只完成了一半--现在我们需要为每个想要转换的枚举类型生成一个版本,该类型子类化我们的自定义类:
一个二个一个一个
或者Lombok岛的方式:

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

这是一个痛苦。这是一个痛苦,可以通过一点调整和AWS SDK中的一点反射来解决,但这就是我们现在所处的位置。

xurqigkl

xurqigkl3#

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

qnyhuwrf

qnyhuwrf4#

dynamodb增强的SDK开箱即用。
当你声明一个@DynamoDbBean时,DefaultAttributeConverterProvider提供了一长串在java类型之间转换属性的可能方法,包括一个EnumAttributeConverter,如果type.rawClass().isEnum()为true,则使用它。
如果您想要扩展转换器的数量,您需要添加converterProviders注解参数,并声明默认值(或省略它),以及您想要的任何其他提供程序。
示例:@DynamoDbBean(converterProviders = { DefaultAttributeConverterProvider.class, MyCustomAttributeConverterProvider.class });

iyr7buue

iyr7buue5#

基于watkinsmatthewp的解决方案答案:

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

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

任务状态属性如下所示:

@Getter(onMethod_ = {@DynamoDbConvertedBy(TaskStatusConverter.class)})
TaskStatus status;

相关问题