Java Spring JPA环境下如何将Date转换为Epoch时间戳并存入DynamoDB表?
我看了你的配置,发现核心问题是DynamoDB表结构和实体类的类型不匹配——你的CloudFormation把createdAt定义为数字类型(N,存Epoch时间戳),但实体类里直接用了Date类型作为范围键,而AWS SDK的DynamoDB映射默认不会自动把Date转成数字时间戳,所以得做类型转换处理。下面给你两种可行的解决办法,你可以按需选择:
方案1:自定义类型转换器(推荐)
这种方式能让业务层依然用Date类型,底层转换逻辑完全封装在转换器里,代码更干净。
步骤1:写一个Date转Long的类型转换器
创建一个实现DynamoDBTypeConverter的类,负责在Date和Epoch时间戳(Long)之间互相转换:
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverter; import java.util.Date; public class DateToEpochTimestampConverter implements DynamoDBTypeConverter<Long, Date> { // 把Date转成Long时间戳存到DynamoDB @Override public Long convert(Date date) { return date != null ? date.getTime() : null; } // 把DynamoDB里的Long时间戳转成Date返回给业务层 @Override public Date unconvert(Long timestamp) { return timestamp != null ? new Date(timestamp) : null; } }
步骤2:在实体类的范围键上绑定转换器
修改你实体类中getCreatedAt方法的注解,加上@DynamoDBTypeConverted指定转换器:
@DynamoDBRangeKey @DynamoDBAttribute(attributeName = "createdAt") @DynamoDBTypeConverted(converter = DateToEpochTimestampConverter.class) public Date getCreatedAt() { return pk != null ? pk.getCreatedAt() : null; }
同时,PrimaryKey内部类里的createdAt字段不用改,Lombok的@Data会自动生成正确的getter,转换器会自动处理和DynamoDB字段的映射逻辑。
方案2:直接用Long类型存储,维护Date的便捷方法
如果不想写转换器,也可以直接把实体里的范围键改成Long类型,然后加个忽略DynamoDB映射的Date属性,方便业务层使用:
步骤1:修改PrimaryKey内部类
把createdAt字段改成Long类型,再加两个忽略映射的方法来处理Date和时间戳的转换:
@Data @Builder @DynamoDBDocument @NoArgsConstructor @AllArgsConstructor public static class PrimaryKey implements Serializable { @DynamoDBHashKey private String bookId; @DynamoDBRangeKey private Long createdAtTimestamp; // 对应DynamoDB的N类型 // 业务层用的Date属性,不映射到DynamoDB @DynamoDBIgnore public Date getCreatedAt() { return createdAtTimestamp != null ? new Date(createdAtTimestamp) : null; } @DynamoDBIgnore public void setCreatedAt(Date createdAt) { this.createdAtTimestamp = createdAt != null ? createdAt.getTime() : null; } // 重构of方法,支持传入Date自动转时间戳 @DynamoDBIgnore public static Book.PrimaryKey of(String bookId, Date createdAt) { return Book.PrimaryKey.builder() .bookId(bookId) .createdAtTimestamp(createdAt != null ? createdAt.getTime() : null) .build(); } }
步骤2:修改实体类的getCreatedAt方法
让它返回Long类型的时间戳,对应DynamoDB的N类型:
@DynamoDBRangeKey @DynamoDBAttribute(attributeName = "createdAt") public Long getCreatedAt() { return pk != null ? pk.getCreatedAtTimestamp() : null; } // 保留set方法,业务层依然可以传Date public Book setCreatedAt(final Date createdAt) { if (pk == null) { pk = new Book.PrimaryKey(); } pk.setCreatedAt(createdAt); return this; }
注意一个潜在的小坑!
我看到你实体类里的getbookId()方法名是小写b开头:
@DynamoDBHashKey @DynamoDBAttribute(attributeName = "bookId") public String getbookId() { return pk != null ? pk.getbookId() : null; }
但Lombok的@Data注解会给bookId字段生成getBookId()(大写B开头)的getter,这会导致AWS SDK的DynamoDB映射找错方法,出现主键映射失败的问题。你应该把这个方法名改成getBookId(),和Lombok生成的保持一致:
@DynamoDBHashKey @DynamoDBAttribute(attributeName = "bookId") public String getBookId() { return pk != null ? pk.getBookId() : null; } public Book setBookId(final String bookId) { if (pk == null) { pk = new Book.PrimaryKey(); } pk.setBookId(bookId); return this; }
这样修改后,你的实体类就能正确把Date转成Epoch时间戳存到DynamoDB的createdAt字段里,也能正常从数据库读取转成Date返回给业务层啦。
内容来源于stack exchange

