@CreatedDate 在更新时设置为 null (spring-data-jdbc)

Nan*_*lla 5 java spring-data spring-boot spring-data-jdbc

我尝试@CreatedDate与 spring-data-jdbc 和 PostgreSQL 数据库结合使用来跟踪首次创建表条目的时间。这在插入新行时有效(它正确地将其设置为当前日期),但在更新条目时,我收到异常,因为它被设置为空,这是我的配置不允许的。

这是我的实体(省略了其他不相关的字段):

@Table("my_entity")
public class MyEntity {

    @Id
    private final Long id;

    @Column("created_at")
    @CreatedDate
    private Instant createdAt;

    public MyEntity (Long id) {
        this.id = id;
    }

    public Long getId() {
        return id;
    }

    public Instant getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Instant createdAt) {
        this.createdAt = createdAt;
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的存储库:

public interface MyRepository extends CrudRepository<MyEntity, Long> {}
Run Code Online (Sandbox Code Playgroud)

这是相关配置:

@Configuration
@EnableJdbcRepositories("com.example")
@EnableJdbcAuditing
public class JdbcConfig extends AbstractJdbcConfiguration {
}
Run Code Online (Sandbox Code Playgroud)

使用该repository.saveAll()方法,createdAt第一次就可以正确设置该字段。当使用相同的方法更新现有实体时,当我希望跳过该字段时,它被设置为 null。

我已经尝试过以下操作:

  • 按照 spring-data-rest 的建议删除公共 getter 和 setter(请参阅此处) - 没有改变任何内容
  • @Column(name="created_at", updatable="false")按照 JPA 的建议进行设置(请参阅此处) - 此字段不适用于我正在使用的注释 ( org.springframework.data.relational.core.mapping.Column)

到目前为止,我发现的两个解决方案是使用数据库触发器或在更新实体之前手动从数据库中检索实体并手动设置创建日期(此处也提到)。还有其他选项还是我配置错误?这是预期的行为吗?

小智 5

正如您所发现的,解决方案是:

  1. 完全更新createdAt时手动设置。(意味着使用现有的id创建一个新实例)full update
  2. 部分更新partial update意味着您将记录获取到持久性上下文,例如使用 findById,然后更新它并将其保存回来)

当您启用 时@EnableXXXAuditing,它会判断您的repo.save()insertupdate,然后分别更新您的@CreatedDate@LastModifiedDate

在你的情况下: full update没有createdAt设置。Spring Data JDBC 会根据您的记录 ID 来确定您的记录是否应该插入或更新实体状态检测策略。如果它发现 ID 存在,它将执行update. @EnableXXXAuditingfor the的行为update仅被设置@LastModifiedDate,因此 yourcreatedAt仍然为null。这就是你陷入困境的原因。

仅供参考:这里有一个简短的解释@EnableXXXAuditing