Dość częstym zadaniem jakie musimy wykonać zapisując dane naszej aplikacji jest również dołączenie informacji o tym kto i kiedy stworzył, a później aktualizował dany rekord. Zazwyczaj informacje takie trafiają do czterech kolumn: data stworzenia, data aktualizacji, użytkownik, który stworzył oraz zaktualizował rekord. Są to tzw. kolumny audytowe. W momencie, gdy wykorzystujemy do zapisu naszych danych implementację JPA, mamy sprawę bardzo ułatwioną.

Stwórzmy interfejs Auditable, który powinna implementować każda „audytowalna” klasa encji.

public interface Auditable {

    Date getCreationDate();

    void setCreationDate(Date date);

    Date getModificationDate();

    void setModificationDate(Date date);

    T getCreationUser();

    void setCreationUser(T user);

    T getModificationUser();

    void setModificationUser(T user);

    T getCurrentUser();

}

Interfejs Auditable jest parametryzowany, tak aby można było określić jakiego typu obiekty reprezentują użytkownika w naszej aplikacji. Wspólna implementacja tego interfejsu została umieszczona w klasie abstrakcyjnej AuditEntity. To tutaj zdefiniowana jest cała logika związana z aktualizowaniem danych audytowych przed zapisem.

@MappedSuperclass
public abstract class AuditEntity implements Auditable<User> {

    protected Date creationDate;
    protected Date modificationDate;

    @ManyToOne
    @JoinColumn(name = "creation_user_id")
    protected User creationUser;

    @ManyToOne
    @JoinColumn(name = "modification_user_id")
    protected User modificationUser;

    //tutaj gettery i settery dla creationDate,
    //modificationDate, creationUser, modificationUser

    @Override
    public User getCurrentUser() {
        return Application.getCurrentUser();
    }

    @PrePersist
    public void prePersist() {
        setCreationDate(new Date());
        setCreationUser(getCurrentUser());
        preUpdate();
    }

    @PreUpdate
    public void preUpdate() {
        setModificationDate(new Date());
        setModificationUser(getCurrentUser());
    }
}

Klasa bazowa została oznaczona adnotacją @MappedSuperclass. Dzięki temu dziedziczone atrybuty są trwałe wg. mapowania zdefiniowanego w tej klasie (istnieje możliwość nadpisania tych mapowań poprzez użycie adnotacji @AttributeOverride(s)). Najważniejszą częścią tej klasy są metody prePersist() oraz preUpdate(). Dzięki oznaczeniu metody prePersist() odpowiedną adnotacją @PrePersist wykonuje się ona przed utrwaleniem danej encji. Podobnie wykonywana jest metoda preUpdate() (oznaczona adnotacją @PreUpdate) przed każdą aktualizacją do bazy danych.

Teraz wystarczy tylko dodać dziedziczenie z AuditEntity dla każdej encji, dla której chcemy zapisywać dane audytowe np.:

@Entity
@Table(name = "customers")
public class Customer extends AuditEntity

Jak widać dość szybko i prosto można osiągnąć funkcjonalność „audytowalnych” encji.