Spring Boot 中 JPA 未自动填充的审计字段

Auditing fields not automatically filled by JPA in Spring Boot

我有以下实体,它反映了某种后台任务并将被更新。我认为使用 JPA 审计会很有用,所以我像这样包含它:

@Entity
@RequiredArgsConstructor
@Getter
@Setter
@Table(name = "FOO")
public class FooJob{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "ID", unique = true, nullable = false)
    private long id;

    @LastModifiedDate
    @Column(name = "FINISHED", nullable = false)
    private LocalDateTime finished;

    ...

}

@Repository
public interface FooJobRepository
    extends JpaRepository<FooJob, Long>,
        JpaSpecificationExecutor<FooJob> {}

并且我在 spring-boot 应用程序

上启用了 @EnableJpaAuditing

spring版本:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.0</version>
    <relativePath />
</parent>

但是当运行代码是这样的:

@Service
@Transactional
/*package*/ class FooJobService{
   public void run() {
     FooJob job = createJob();
     ....
   }

   private FooJob createJob(){
      FooJob job = new FooJob();
      job.setSomeValue("123");
      //Do not set "finished" date here
      repo.saveAndFlush(job); //THIS LINE THROWS EXCEPTION
   }
}

我会得到以下异常:

org.springframework.dao.DataIntegrityViolationException: not-null property references a null or transient value : my.package.entity.FooJob.finished; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value : my.package.entity.FooJob.finished

为什么这不起作用?

您需要为您的实体添加更多注释以便对其进行审核(至少 @Audited@AuditTable@EntityListeners

示例:

@Entity
@Table(name = "my_table")
@AuditTable("my_table_audit")
@Audited
@EntityListeners(AuditingEntityListener.class)
public class MyEntity{
  @Column(name = "created_date", nullable = false)
  @CreatedDate
  private Date createdDate;
  @Column(name = "modified_date")
  @LastModifiedDate
  private Date modifiedDate;
  @Column(name = "created_by")
  @CreatedBy
  private String createdBy;
  @Column(name = "modified_by")
  @LastModifiedBy
  private String modifiedBy;
  @Version
  private Long version;

我也不确定 @LastModifiedDate 是否可以与类型 LocalDateTime 一起使用。

您还需要一个审计框架,例如 Hibernate Envers

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-envers</artifactId>
</dependency>

如果您使用的是 Hibernate EnversMySQL,审计应该是这样的:

CREATE TABLE revinfo
(
    revision_number integer AUTO_INCREMENT,
    REVTSTMP bigint,
    CONSTRAINT revinfo_pkey PRIMARY KEY (revision_number)
);

CREATE TABLE IF NOT EXISTS my_table
(
    ...
    created_date                datetime DEFAULT now(),
    modified_date               datetime DEFAULT now(),
    created_by                  VARCHAR(100)  NULL,
    modified_by                 VARCHAR(100)  NULL,
    version                     INT           NULL
);

CREATE TABLE IF NOT EXISTS my_table_audit
(
    ...
    created_date                datetime,
    modified_date               datetime,
    created_by                  VARCHAR(100),
    modified_by                 VARCHAR(100),
    version                     INT,
    revision_number             integer,
    revision_type               smallint,
    FOREIGN KEY fk_my_table_audit_rev_num (revision_number) REFERENCES REVINFO (revision_number)
);

您需要在主 class 或配置 class 中注释 @EnableJpaAuditing(class 和 @Configuration

并且必须用 @EntityListeners(AuditingEntityListener.class).

注释实体 class

如果您只更新完成日期,请将此添加到实体:

@EntityListeners(AuditingEntityListener.class)