[英]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 });
我认为您的注释实际上可能是这里的问题。 我会删除所有提到构造函数的注释,而是写出你自己的构造函数。 对于Task
和TaskStatus
。
如何将 Java 枚举与 Amazon DynamoDB 和 AWS SDK v2 结合使用?
尽管文档没有说明,但DynamoDbConvertedBy
注释要求您提供的任何AttriuteConverter
包含参数默认构造函数
不幸的是,对于你和我来说,编写许多内置AttributeConverter
类的人决定使用静态create()
方法来实例化它们而不是构造函数(也许它们是幕后的单例?我不知道)。 这意味着任何想要使用这些有用的无构造函数类(如InstantAsStringAttributeConverter
和EnumAttributeConverter
需要将它们包装在自定义包装类中,以便简单地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 开发工具包中进行一点调整和一点点反射来解决,但这就是我们现在所处的位置。
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.