Neil John
Neil John

Reputation: 37

How to add fields in audit table using Envers in Spring Boot

Good morning, how can I add fields in my audit table?

I need to audit some tables, but I need to get the user who did the operation. My entity who will be audited is:

@Entity
@Table(name ="TableName")
@Audited
@AuditTable("TableNameAuditedLog")
public class MyEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "myId")
    private Long id;

    @Column(name = "myName")
    private String name;
}

Looking the docs, I saw an example a custom class to be my audit and a listener, so I made like this:

@Data
@RevisionEntity(AuditListener.class)
@MappedSuperclass
public class Audit {

    @Id
    @GeneratedValue
    @RevisionNumber
    private Long id;

    @RevisionTimestamp
    private Long timestamp;

    @Column(name = "user")
    private String user;
}

public class AuditListener implements RevisionListener {

    @Override
    public void newRevision(Object revisionEntity) {
        Audit audit = (Audit) revisionEntity;
        audit.setUsuario("user");
    }
}

I've tried to extends my Audit class in my Entity class, but I'd trouble with JPA, the trouble is:

Caused by: org.hibernate.MappingException: Unable to find column with logical name: myId in org.hibernate.mapping.Table(TableNameAuditedLog) and its related supertables and secondary tables

How can I do this?

Thank you all.

Upvotes: 1

Views: 3072

Answers (1)

Lee Greiner
Lee Greiner

Reputation: 1092

Remove the MappedSuperClass from your Audit class. You could also have Audit extend DefaultRevisionEntity. All you would have in Audit class is your custom field.

@Column(name = "user")
private String user;

A custom audit revision entity:

@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@RevisionEntity(UserRevisionListener.class)
public class AuditRevisionEntity extends DefaultTrackingModifiedEntitiesRevisionEntity {
  private static final long serialVersionUID = 1L;
  
  private Long userId;
  
  @Column(length = 100, nullable = false)
  private String initiator;
}

And revision listener

public class UserRevisionListener implements RevisionListener {
  private static final String SYSTEM_USER = "System";
  private transient final SecurityUtils securityUtils;
  
  public UserRevisionListener(final SecurityUtils securityUtils) {
    this.securityUtils = securityUtils;
  }
  
  @Override
  public void newRevision(Object revisionEntity) {
    final AuditRevisionEntity are = (AuditRevisionEntity) revisionEntity;
    
    securityUtils.getPrincipal().ifPresentOrElse((appPrincipal) -> {
      are.setUserId(appPrincipal.getUserId());
      are.setInitiator(appPrincipal.getDisplayName());
    }, () -> are.setInitiator(SYSTEM_USER));
  }
}

In my case I am getting the current principal(I am using a custom principal that has the extra fields) using a SecurityUtils helper and setting the AuditRevisionEntity as needed. Some changes are made by Quartz jobs so there is no principal in which case only the initiator is set.

Upvotes: 3

Related Questions