Java >> Java Tutorial >  >> Tag >> Spring

Spring Data JPA-Lernprogramm:Prüfung, Teil Eins

Wenn wir das Wort Audit hören, fällt uns als Erstes ein Audit-Protokoll ein, das jede Version der geprüften Entität enthält. Die Implementierung eines Audit-Logs ist eine komplexe Aufgabe, die viel Zeit in Anspruch nimmt. Glücklicherweise müssen wir dies meistens nicht tun.

Es kommt jedoch häufig vor, dass wir in der Lage sein müssen, die folgenden Fragen zu beantworten:

  • Wann wurde die Entität X erstellt und/oder geändert?
  • Wer hat die Entität X erstellt und/oder modifiziert?

Die Auditing-Infrastruktur von Spring Data JPA hilft uns bei der Beantwortung dieser Fragen. Dieser Blogbeitrag beschreibt, wie wir die Erstellungs- und Änderungszeitfelder zu unseren Entitäten hinzufügen und sie aktualisieren können, indem wir die Überwachungsinfrastruktur von Spring Data JPA verwenden.

Beginnen wir damit, einen Dienst zu erstellen, der das aktuelle Datum und die aktuelle Uhrzeit zurückgibt.

Aktuelles Datum und Uhrzeit abrufen

Es gibt zwei Gründe, warum wir eine Schnittstelle erstellen sollten, die zum Abrufen des aktuellen Datums und der Uhrzeit verwendet werden kann. Diese Gründe sind:

  1. Wir wollen zwei verschiedene Implementierungen für diese Schnittstelle erstellen:
    • Die erste Implementierung wird von unserer Anwendung verwendet und gibt das aktuelle Datum und die aktuelle Uhrzeit zurück.
    • Die zweite Implementierung wird von unseren Integrationstests verwendet und gibt immer dasselbe Datum und dieselbe Uhrzeit zurück.
  2. Wenn wir eine reale Anwendung implementieren, müssen unsere anderen Komponenten wahrscheinlich auch das aktuelle Datum und die aktuelle Uhrzeit abrufen.

Der DateTimeService Schnittstelle deklariert nur eine Methode:

  • Das getCurrentDateAndTime() -Methode gibt eine ZonedDateTime zurück Objekt.

Der Quellcode des DateTimeService Die Benutzeroberfläche sieht wie folgt aus:

import java.time.ZonedDateTime;

public interface DateTimeService {

    ZonedDateTime getCurrentDateAndTime();
}

Der CurrentTimeDateTimeService Klasse implementiert den DateTimeService Schnittstelle. Sein getCurrentDateAndTime() -Methode gibt einfach das aktuelle Datum und die Uhrzeit zurück.

Der Quellcode des CurrentTimeDateTimeService sieht wie folgt aus:

import java.time.ZonedDateTime;

public class CurrentTimeDateTimeService implements DateTimeService {

    @Override
    public ZonedDateTime getCurrentDateAndTime() {
        return ZonedDateTime.now();
    }
}

Lassen Sie uns weitermachen und herausfinden, wie wir unseren Service in die Auditing-Infrastruktur von Spring Data JPA integrieren können.

Integration unseres Dienstes in die Prüfungsinfrastruktur von Spring Data JPA

Die Überwachungsinfrastruktur von Spring Data JPA verwendet den DateTimeProvider Schnittstelle, wenn es das aktuelle Datum und die aktuelle Uhrzeit abrufen muss. Das heißt, wenn wir unseren DateTimeService integrieren wollen mit Spring Data JPA müssen wir diese Schnittstelle implementieren. Wir können dies tun, indem wir diesen Schritten folgen:

  1. Erstellen Sie einen AuditingDateTimeProvider Klasse und implementieren Sie den DateTimeProvider Schnittstelle.
  2. Fügen Sie einen endgültigen DateTimeService hinzu -Feld in die erstellte Klasse und fügen Sie es mithilfe der Konstruktorinjektion ein.
  3. Implementieren Sie getNow() Methode. Wir müssen das aktuelle Datum und die Uhrzeit abrufen, indem wir das DateTimeService-Objekt verwenden und einen neuen GregorianCalendar zurückgeben Objekt.

Der Quellcode des AuditingDateTimeProvider Klasse sieht wie folgt aus:

import org.springframework.data.auditing.DateTimeProvider;

import java.util.Calendar;
import java.util.GregorianCalendar;

public class AuditingDateTimeProvider implements DateTimeProvider {

    private final DateTimeService dateTimeService;

    public AuditingDateTimeProvider(DateTimeService dateTimeService) {
        this.dateTimeService = dateTimeService;
    }

    @Override
    public Calendar getNow() {
        return GregorianCalendar.from(dateTimeService.getCurrentDateAndTime());
    }
}

Unser nächster Schritt besteht darin, den Anwendungskontext unserer Anwendung zu konfigurieren. Lassen Sie uns herausfinden, wie wir es tun können.

Konfigurieren des Anwendungskontexts

Zuerst , müssen wir einen DateTimeService erstellen Bean, die verwendet wird, wenn wir unsere Anwendung ausführen. Wir sollten diese Bean in der Root-Anwendungskontext-Konfigurationsklasse (oder XML-Konfigurationsdatei) deklarieren, da sie wahrscheinlich von mehr als einer Komponente verwendet wird, und ich denke, dass die Root-Anwendungskontext-Konfigurationsklasse (oder XML-Konfigurationsdatei) ein natürlicher Ort dafür ist diese Art von Bohnen.

Wir können diese Bean erstellen, indem wir diesen Schritten folgen:

  1. Erstellen Sie den currentTimeDateTimeService() -Methode und implementieren Sie sie, indem Sie einen neuen CurrentTimeDateTimeService zurückgeben Objekt.
  2. Kommentieren Sie die Methode mit @Bean Anmerkung.
  3. Kommentieren Sie die Methode mit @Profile -Anmerkung und setzen Sie ihren Wert auf Profiles.APPLICATION . Dadurch wird sichergestellt, dass diese Bean nur erstellt wird, wenn unsere Anwendung gestartet wird.

Der relevante Teil des ExampleApplicationContext Klasse sieht wie folgt aus:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;

@Configuration
@ComponentScan("net.petrikainulainen.springdata.jpa")
@Import({WebMvcContext.class, PersistenceContext.class})
public class ExampleApplicationContext {

    @Profile(Profiles.APPLICATION)
    @Bean
    DateTimeService currentTimeDateTimeService() {
        return new CurrentTimeDateTimeService();
    }
}

Zweiter , müssen wir den DateTimeProvider erstellen Bean und aktivieren Sie die Auditing-Unterstützung von Spring Data. Wir können dies tun, indem wir die folgenden Änderungen an der Konfigurationsklasse vornehmen, die die Persistenzschicht unserer Beispielanwendung konfiguriert:

  1. Erstellen Sie einen dateTimeProvider() Methode, die einen DateTimeProvider zurückgibt Objekt und nimmt einen DateTimeService Objekt als Methodenparameter.
  2. Implementieren Sie die Methode, indem Sie einen neuen AuditingAwareDateTimeProvider erstellen Objekt.
  3. Kommentieren Sie die erstellte Methode mit @Bean Anmerkung.
  4. Kommentieren Sie die Konfigurationsklasse mit @EnableJpaAuditing -Anmerkung und legen Sie den Namen des DateTimeProvider fest Bean (dateTimeProvider ) als Wert seiner dataTimeProviderRef Attribut.

Der relevante Teil des PersistenceContext Klasse sieht wie folgt aus:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.auditing.DateTimeProvider;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;


@Configuration
@EnableJpaAuditing(dateTimeProviderRef = "dateTimeProvider")
@EnableJpaRepositories(basePackages = {
        "net.petrikainulainen.springdata.jpa.todo"
})
@EnableTransactionManagement
class PersistenceContext {

    @Bean
    DateTimeProvider dateTimeProvider(DateTimeService dateTimeService) {
        return new AuditingDateTimeProvider(dateTimeService);
    }
}

Fahren wir fort und nehmen die erforderlichen Änderungen an unserer Entitätsklasse vor.

Ändern unserer Entitätsklasse

Wir müssen die folgenden Änderungen an unserer Entitätsklasse vornehmen (Todo ):

  1. Wir müssen sicherstellen, dass der Wert der creationTime Das Feld wird gesetzt, wenn unsere Entität zum ersten Mal persistiert wird.
  2. Wir müssen sicherstellen, dass der Wert der modificationTime Das Feld wird gesetzt, wenn unsere Entität zum ersten Mal beibehalten wird, und aktualisiert, wenn die Informationen unserer Entität aktualisiert werden.

Wir können diese Änderungen vornehmen, indem wir diesen Schritten folgen:

  1. Kommentieren Sie die creationTime Feld mit dem @CreatedDate Anmerkung. Dies identifiziert das Feld, dessen Wert festgelegt wird, wenn die Entität zum ersten Mal in der Datenbank gespeichert wird.
  2. Kommentieren Sie die modificationTime Feld mit dem @LastModifiedDate Anmerkung. Dies identifiziert das Feld, dessen Wert gesetzt wird, wenn die Entität zum ersten Mal gespeichert wird, und aktualisiert wird, wenn die Informationen der Entität aktualisiert werden.
  3. Annotieren Sie die Entity-Klasse mit den @EntityListeners -Anmerkung und setzen Sie ihren Wert auf AuditingEntityListener.class . Der AuditingEntityListener class ist ein JPA-Entity-Listener, der die Audit-Informationen einer Entity aktualisiert, wenn sie beibehalten und aktualisiert wird.

Der relevante Teil der Todo Klasse sieht wie folgt aus:

import org.hibernate.annotations.Type;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Version;
import java.time.ZonedDateTime;

@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "todos")
final class Todo {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "creation_time", nullable = false)
    @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
    @CreatedDate
    private ZonedDateTime creationTime;

    @Column(name = "description", length = 500)
    private String description;

    @Column(name = "modification_time")
    @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
    @LastModifiedDate
    private ZonedDateTime modificationTime;

    @Column(name = "title", nullable = false, length = 100)
    private String title;

    @Version
    private long version;
}

Normalerweise ist es eine gute Idee, die Audit-Felder einer abstrakten Basisklasse hinzuzufügen und sie mit @EntityListener zu kommentieren Anmerkung. Der Grund, warum ich es hier nicht getan habe, ist, dass unsere Beispielanwendung nur eine Entität hat und ich die Dinge so einfach wie möglich halten wollte.

Wenn wir diese Informationen in die abstrakte Basisklasse verschieben würden, würde ihr Quellcode wie folgt aussehen:

import org.hibernate.annotations.Type;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperClass
import java.time.ZonedDateTime;

@EntityListeners(AuditingEntityListener.class)
@MappedSuperClass
public abstract class BaseEntity {

    @Column(name = "creation_time", nullable = false)
    @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
    @CreatedDate
    private ZonedDateTime creationTime;

    @Column(name = "modification_time")
    @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
    @LastModifiedDate
    private ZonedDateTime modificationTime;
}

Lassen Sie uns herausfinden, warum wir die Auditing-Unterstützung von Spring Data JPA anstelle der in der Java Persistence API angegebenen Callback-Methoden verwenden sollten.

Warum sollten wir die Prüfungsunterstützung von Spring Data JPA nutzen?

Wenn wir die Erstellungs- und Änderungszeitfelder zu unseren Entitäten hinzufügen müssen, müssen wir Spring Data JPA nicht verwenden. Wir können die Feldwerte dieser Felder festlegen, indem wir Callback-Methoden erstellen, die an die Lebenszyklusereignisse der Entität angehängt werden.

Die Quelle einer abstrakten Basisklasse, die diese Methode verwendet, sieht wie folgt aus:

import org.hibernate.annotations.Type;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.MappedSuperClass;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import java.time.ZonedDateTime;

@MappedSuperClass
public abstract class BaseEntity {

    @Column(name = "creation_time", nullable = false)
    @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
    private ZonedDateTime creationTime;

    @Column(name = "modification_time")
    @Type(type = "org.jadira.usertype.dateandtime.threeten.PersistentZonedDateTime")
    private ZonedDateTime modificationTime;
	
	@PrePersist
	public void prePersist() {
		ZonedDateTime now = ZonedDateTime.now();
		this.creationTime = now;
		this.modificationTime = now;
	}
	
	@PreUpdate
	public void preUpdate() {
		this.modificationTime = ZonedDateTime.now();
	}
}

Diese Methode ist viel einfacher als die Lösung, die die Überwachungsinfrastruktur von Spring Data JPA verwendet. Die offensichtliche Frage ist:

Ist es sinnvoll, eine komplexere Lösung zu verwenden?

Es hängt davon ab, ob. Es gibt zwei Gründe, warum es sinnvoll ist:

  • Wenn wir Tests schreiben müssen, die sicherstellen, dass die Erstellungs- und Änderungszeiten korrekt sind, müssen wir die Auditing-Infrastruktur von Spring Data JPA verwenden, da sie uns die Möglichkeit gibt, einen DateTimeProvider zu verwenden die immer dasselbe Datum und dieselbe Uhrzeit zurückgibt.
  • Wenn wir die Informationen des Benutzers speichern müssen, der eine Entität erstellt und/oder geändert hat, sollten wir Spring Data auch zum Festlegen der Erstellungs- und Änderungszeiten verwenden. Es macht einfach keinen Sinn, die Audit-Informationen einer Entität mit zwei verschiedenen Mechanismen festzulegen.

Fahren wir fort und fassen zusammen, was wir aus diesem Blogpost gelernt haben.

Zusammenfassung

Dieser Blogbeitrag hat uns drei Dinge gelehrt:

  • Wir können unseren eigenen Datums- und Uhrzeitanbieter erstellen, indem wir den DateTimeProvider implementieren Schnittstelle. Dies ist sinnvoll, da es uns die Möglichkeit gibt, zu Testzwecken einen anderen Anbieter zu verwenden.
  • Wir können die Erstellungs- und Änderungszeitfelder mithilfe von Anmerkungen identifizieren und die Werte durch Implementieren von Auditable festlegen Schnittstelle oder erweitern Sie das AbstractAuditable Klasse.
  • Es ist einfacher, die Werte der Erstellungs- und Änderungszeitfelder mithilfe von Entitätslebenszyklusereignissen und Rückrufmethoden festzulegen, aber es gibt Situationen, in denen wir die Überwachungsinfrastruktur von Spring Data JPA verwenden sollten (auch wenn es sich um eine komplexere Lösung handelt ).

Der nächste Teil dieses Tutorials beschreibt, wie wir die Antwort auf die Frage erhalten können:Wer hat die Entität X erstellt und/oder geändert.

P.S. Sie können die Beispielanwendung dieses Blogbeitrags von Github herunterladen.


Java-Tag