Skip to content

Version diff on the audit trail

Status: ✅ Implemented

“Who changed this, when, and what exactly?” An audit trail answers it only if the reader can see the changes as versions — one save, its author, and every field it touched with old → new values — instead of a flat stream of rows.

Implement Auditable on the CRUD entity or view (as before) and return one AuditEntry per field change, all changes of one save sharing the same when:

@Override
public ListingData<AuditEntry> history(String searchText, AuditFilters filters,
Pageable pageable, HttpRequest httpRequest) {
return ListingData.from(auditStore.entriesFor(recordId)); // when, who, action, field, old, new
}

The History dialog now renders the trail grouped by save moment (AuditEntry.when is the listing’s @GroupBy column): each group subtotal row is one version — timestamp and number of fields changed — and its rows are the per-field diff (field, oldValuenewValue, who, action). Searching and filtering (by author, by field…) recompute the grouping over the filtered trail.

Restoring is a write decision the framework won’t take for you (it can’t know whether reverting a price also requires reverting the invoice it produced). The recipe: give the audited entity’s crud a bulk or row action that reads the audit rows and writes the old values back through your repository — the optimistic-locking @Version bump keeps the restore itself auditable.