kzharas210
kzharas210

Reputation: 500

Hibernate Envers: How to capture who deleted an entity in audit table

I am using hibernate-envers with spring. Everything works just fine, except when I delete an entity, it does not change the values of updated_by and updated_date inside audit table, instead it saves an entity exactly as it was before (just copy) after spring.jpa.properties.org.hibernate.envers.store_data_at_delete=true.

I have already tried to register listener EventType.PRE_DELETE, but it didn't help.

Here is my UpdateEntity:

@LastModifiedBy
@Column(nullable = false)
private Long updatedBy;

@LastModifiedDate
@Column(nullable = false)
private Date updatedDate;

How can I capture who deleted and when was deleted inside audit table by modifying columns updated_by and updated_date?

Upvotes: 11

Views: 6695

Answers (3)

feodal007
feodal007

Reputation: 11

Thanks to ŁukaszW, this answer was very useful for me. I have added AuditorAwareUserName : AuditorAware to the implementation, and it works perfectly. The "User-Name" is the name of a request header from RequestContextHolder, and the naming is dynamic.

internal class CustomAuditDeleteStrategy : DefaultAuditStrategy() {
    companion object {
        private const val REV_TYPE = "REVTYPE"
        private const val UPDATED_BY = "updatedBy"
        private const val UPDATED_AT = "updatedAt"
        private val ZONE_ID = ZoneId.of("Europe/Berlin")
    }

    private var auditorAwareUserName = AuditorAwareUserName("User-Name")

    override fun perform(
        session: Session?,
        entityName: String?,
        configuration: Configuration?,
        id: Any?,
        data: Any?,
        revision: Any?
    ) {
        if (data is Map<*, *>) {
            val dataToUpdate = @Suppress("UNCHECKED_CAST") (data as MutableMap<String, Any?>)
            if (dataToUpdate[REV_TYPE] == DEL) {
                dataToUpdate[UPDATED_BY] = auditorAwareUserName.currentAuditor.get()
                dataToUpdate[UPDATED_AT] = ZonedDateTime.now(ZONE_ID).toLocalDateTime()
            }
        }
        super.perform(session, entityName, configuration, id, data, revision)
    }
}

Upvotes: 0

ŁukaszW
ŁukaszW

Reputation: 21

You can use custom audit strategy:

public class CustomAuditStrategy extends DefaultAuditStrategy {

  @Override
  public void perform(final Session s, final String e, final AuditEntitiesConfiguration cfg, final Serializable id, final Object data, final Object revision) {
    if (data instanceof Map) {
      final Map dataToUpdate = (Map)data;
      if (dataToUpdate.get("REVTYPE").equals(RevisionType.DEL)) {
        dataToUpdate.put("modifiedBy", "test");
        dataToUpdate.put("modifiedDate", LocalDateTime.now());
      }
    }
    super.perform(s, e, cfg, id, data, revision);
  }
}

And then you should modify hibernate properties:

spring.jpa.properties.org.hibernate.envers.audit_strategy: x.your.CustomAuditStrategy  
org.hibernate.envers.store_data_at_delete: true

Upvotes: 2

Jens Schauder
Jens Schauder

Reputation: 81907

Looks like you are combining to features: Auditing support and Envers.

Envers records every change to your entity including the delete if configured as you did.

Auditing support traces update and insert timestamps and users. It doesn't work for deletes since there would be nowhere to store that information.

If you want to keep the combination and still make it work you'd need to modify the entity (e.g. setting an extra flag deleting to true), flush it, maybe even commit it (I'm not sure if that is necessary) and then delete it.

Probably a better approach is using a custom revision object and store the required data there as described in this question and answer: Ways to pass additional data to Custom RevisionEntity in Hibernate Envers?

Upvotes: 7

Related Questions