将自定义字段添加到 Hibernate Envers 修订 table (revinfo),如 operation_id。哪个是Operation实体的PK

Add custom field to Hibernate Envers revision table (revinfo), like operation_id. Which is a PK of Operation entity

您好,我想知道是否可以将 Operation_id 之类的自定义字段添加到 revinfo table,它将具有自定义操作,例如 ADD_TRAVEL_OP。这些操作应该在每个端点静态或动态设置。

@Entity
@Table(name = "revinfo")
@RevisionEntity(AuditRevisionListener.class)
public class AuditRevisionEntity extends DefaultRevisionEntity implements Serializable {

    @Column(name = "rev")
    private int revision;

    private String username;

    private String operationId;

    public int getRevision() {
        return revision;
    }

    public void setRevision(final int revision) {
        this.revision = revision;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(final String userName) {
        this.username = userName;
    }

    public String getOperationId() {
        return operationId;
    }

    public void setOperationId(final String operationId) {
        this.operationId = operationId;
    }
@Configuration
public class AuditRevisionListener implements RevisionListener {

    @Override
    public void newRevision(Object revisionEntity) {
        final AuditRevisionEntity are = (AuditRevisionEntity) revisionEntity;
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            are.setUsername(userDetails.getUsername());
        } else {
            are.setUsername(authentication.getName());
        }

    }

}

当然,您可以向 AuditRevisionEntity 添加字段。诀窍是您必须能够访问 AuditRevisionListener 中的数据。我要求某些操作需要在审计跟踪 (AuditRevisionEntity) 中记录更改原因。我最终使用本地线程来存储更改原因,使用本地线程在 AuditRevisionListener 中设置更改原因,并在事务开始或完成时清除本地线程。

JpaTransactionManager 实现:

public class AuditingJpaTransactionManager extends JpaTransactionManager {
  private final AuditContextHolder auditContextHolder = new AuditContextHolder();
  
  public AuditingJpaTransactionManager() {
    super();
  }

  public AuditingJpaTransactionManager(EntityManagerFactory emf) {
    super(emf);
  }
  
  @Override
  protected void doBegin(Object transaction, TransactionDefinition definition) {
    this.auditContextHolder.clearContext();
    super.doBegin(transaction, definition);
  }

  @Override
  protected void doCleanupAfterCompletion(Object transaction) {
    this.auditContextHolder.clearContext();
    super.doCleanupAfterCompletion(transaction);
  }
}

线程本地实现:

public class AuditContextHolder {
  private static final ThreadLocal<AuditContext> contextHolder = new ThreadLocal<>();
  
  public void clearContext() {
    contextHolder.remove();
  }
  
  public AuditContext getContext() {
    AuditContext ctx = contextHolder.get();
    
    if (ctx == null) {
      ctx = new AuditContext();
      contextHolder.set(ctx);
    }
    
    return ctx;
  }
  
  @NoArgsConstructor
  public static class AuditContext {
    @Getter
    @Setter
    private String reasonForChange;
  }
}

所以在 Lee Greiner 的帮助下,我实现了我的目标。我会把剩下的留在这里,这是显而易见的,但仍然可以帮助其他人。

//update to what i already had
public class AuditRevisionListener implements RevisionListener {

    @Override
    public void newRevision(Object revisionEntity) {

        final AuditRevisionEntity are = (AuditRevisionEntity) revisionEntity;
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) authentication.getPrincipal();
            are.setUsername(userDetails.getUsername());
        } else {
            are.setUsername(authentication.getName());
        }
        String operationId = new AuditContextHolder().getContext().getOperationId();
        if(operationId == null) operationId = Operation.NOT_DEFINED.name();
        are.setOperationId(operationId);
    }

}
//Set the desired Operation before the save point
new AuditContextHolder()
.getContext()
.setOperationId(Operation.LOGIN_OP.name());