W ostatnim poście opisywałem jak poradzić sobie z zapisywaniem danych audytowych encji przy wykorzystaniu JPA. W komentarzu pod postem padło pytanie ze strony Piotrka:

Ciekaw jestem jednak jakiegoś sposobu w czystym JPA, który pozwoli na wydelegowanie zbierania audytowych informacji do osobnego „listenera”.

Otóż postanowiłem zgłębić temat 🙂 i faktycznie jest zgrabniejszy sposób w jaki można wykonać to zadanie. Należy wykorzystać adnotację @EntityListeners. Na początek stwórzmy klasę listenera:

public class AuditListener {

    @PrePersist
    void onPrePersist(Object o) {
        if (o instanceof Auditable) {
            ((Auditable<User>)o).setCreationDate(new Date());
            ((Auditable<User>)o).setCreationUser(getCurrentUser());
            onPreUpdate(o);
        }
    }

    @PreUpdate
    void onPreUpdate(Object o) {
        if (o instanceof Auditable) {
            ((Auditable<User>)o).setModificationDate(new Date());
            ((Auditable<User>)o).setModificationUser(getCurrentUser());
        }
    }

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

Nie musi ona implementować żadnego interfejsu, posługujemy się podobnie jak w poprzednim przykładzie adnotacjami @PrePersist oraz @PreUpdate. Tak oznaczone metody wykonają się odpowiednio przez zapisem i aktualizacją encji. Ważne jest aby posiadały odpowiednią sygnaturę, to znaczy muszą zwracać void oraz przyjmować jeden argument (może to być typ bardziej specyficzny niż Object). W implementacji tych metod wiele się nie zmieniło, korzystam tutaj ze znanego wcześniej interfejsu Auditable.

Teraz możemy zmodyfikować klasę naszej encji:

@Entity
@Table(name = "customers")
@EntityListeners(AuditListener.class)
public class Customer implements Auditable

Jak widać powyżej nie musimy już dziedziczyć po klasie AuditEntity (coś można pokusić się o pozostawienie tam pól audytowych wraz z mapowaniem oraz getterami i setterami), ważne aby implementować interfejs Auditable. Pokazany wcześniej listener AuditListener dodajemy do encji poprzez adnotację @EntityListeners. W ten sposób można zarejestrować kilka listenerów nasłuchujących na operacje bazodanowe wykonywane na naszej encji.

Istnieje możliwość pozbycia się takiego listenera dla encji dziedziczącej z encji, dla której został dołączony już konkretny listener. Służy do tego adnotacja @ExcludeSuperclassListeners.

@Entity
@ExcludeSuperclassListeners
public class SpecificEntity extends BaseEntity {
}