Spring Data Composable Repositories

1. Einleitung

Bei der Modellierung eines realen Systems oder Prozesses sind Repositorys im DDD-Stil (Domain Driven Design) eine gute Option. Zu diesem Zweck können wir Spring Data JPA als Abstraktionsschicht für den Datenzugriff verwenden.

Wenn Sie mit diesem Konzept noch nicht vertraut sind, lesen Sie dieses Einführungs-Tutorial, um sich auf dem Laufenden zu halten.

In diesem Tutorial konzentrieren wir uns auf das Konzept des Erstellens von benutzerdefinierten und zusammensetzbaren Repositorys, die mit kleineren Repositorys, so genannten Fragmenten, erstellt werden.

2. Maven-Abhängigkeiten

Die Option zum Erstellen zusammensetzbarer Repositorys ist ab Spring 5 verfügbar.

Fügen wir die erforderliche Abhängigkeit für Spring Data JPA hinzu:

 org.springframework.data spring-data-jpa 2.2.2.RELEASE 

Wir müssten auch eine Datenquelle einrichten, damit unsere Datenzugriffsschicht funktioniert. Es ist eine gute Idee, eine In-Memory-Datenbank wie H2 für die Entwicklung und schnelle Tests einzurichten.

3. Hintergrund

3.1. Ruhezustand als JPA-Implementierung

Spring Data JPA verwendet standardmäßig Hibernate als JPA-Implementierung. Wir können leicht miteinander verwechseln oder vergleichen, aber sie dienen unterschiedlichen Zwecken.

Spring Data JPA ist die Abstraktionsschicht für den Datenzugriff, unter der wir jede Implementierung verwenden können. Wir könnten zum Beispiel den Ruhezustand zugunsten von EclipseLink ausschalten.

3.2. Standard-Repositorys

In vielen Fällen müssten wir selbst keine Abfragen schreiben.

Stattdessen müssen wir nur Schnittstellen erstellen, die wiederum die generischen Spring-Datenrepository-Schnittstellen erweitern:

public interface LocationRepository extends JpaRepository { }

Und dies an sich würde es uns ermöglichen, allgemeine Operationen - CRUD, Paging und Sortieren - für das Location- Objekt auszuführen, das einen Primärschlüssel vom Typ Long hat .

Darüber hinaus ist Spring Data JPA mit einem Mechanismus zur Erstellung von Abfragen ausgestattet, mit dem Abfragen in unserem Namen mithilfe von Methodenkonventionen generiert werden können:

public interface StoreRepository extends JpaRepository { List findStoreByLocationId(Long locationId); }

3.3. Benutzerdefinierte Repositorys

Bei Bedarf können wir unser Modell-Repository erweitern, indem wir eine Fragmentschnittstelle schreiben und die gewünschte Funktionalität implementieren. Dies kann dann in unser eigenes JPA-Repository eingefügt werden.

Hier erweitern wir beispielsweise unser ItemTypeRepository, indem wir ein Fragment-Repository erweitern:

public interface ItemTypeRepository extends JpaRepository, CustomItemTypeRepository { }

Hier ist CustomItemTypeRepository eine weitere Schnittstelle:

public interface CustomItemTypeRepository { void deleteCustomById(ItemType entity); }

Die Implementierung kann ein Repository jeglicher Art sein, nicht nur JPA:

public class CustomItemTypeRepositoryImpl implements CustomItemTypeRepository { @Autowired private EntityManager entityManager; @Override public void deleteCustomById(ItemType itemType) { entityManager.remove(itemType); } }

Wir müssen nur sicherstellen, dass es das Postfix Impl hat . Wir können jedoch einen benutzerdefinierten Postfix mithilfe der folgenden XML-Konfiguration festlegen:

oder mithilfe dieser Anmerkung:

@EnableJpaRepositories( basePackages = "com.baeldung.repository", repositoryImplementationPostfix = "CustomImpl")

4. Erstellen von Repositorys mit mehreren Fragmenten

Bis vor einigen Releases konnten wir unsere Repository-Schnittstellen nur mit einer einzigen benutzerdefinierten Implementierung erweitern. Dies war eine Einschränkung, aufgrund derer wir alle zugehörigen Funktionen in einem einzigen Objekt zusammenfassen mussten.

Bei größeren Projekten mit komplexen Domänenmodellen führt dies natürlich zu aufgeblähten Klassen.

Mit Spring 5 haben wir jetzt die Möglichkeit, unser JPA-Repository mit mehreren Fragment-Repositorys anzureichern . Auch hier bleibt die Anforderung, dass wir diese Fragmente als Schnittstellen-Implementierungs-Paare haben.

Um dies zu demonstrieren, erstellen wir zwei Fragmente:

public interface CustomItemTypeRepository { void deleteCustom(ItemType entity); void findThenDelete(Long id); } public interface CustomItemRepository { Item findItemById(Long id); void deleteCustom(Item entity); void findThenDelete(Long id); }

Natürlich müssten wir ihre Implementierungen schreiben. Anstatt diese benutzerdefinierten Repositorys - mit zugehörigen Funktionen - in ihre eigenen JPA-Repositorys einzufügen, können wir die Funktionalität eines einzelnen JPA-Repositorys erweitern:

public interface ItemTypeRepository extends JpaRepository, CustomItemTypeRepository, CustomItemRepository { }

Jetzt hätten wir alle verknüpften Funktionen in einem einzigen Repository.

5. Umgang mit Mehrdeutigkeiten

Da wir von mehreren Repositorys erben, können wir möglicherweise nicht herausfinden, welche unserer Implementierungen im Falle eines Konflikts verwendet werden. In unserem Beispiel haben beispielsweise beide Fragment-Repositorys die Methode findThenDelete () mit derselben Signatur.

In diesem Szenario wird die Reihenfolge der Deklaration der Schnittstellen verwendet, um die Mehrdeutigkeit aufzulösen . In unserem Fall wird daher die Methode in CustomItemTypeRepository verwendet, da sie zuerst deklariert wurde.

Wir können dies anhand dieses Testfalls testen:

@Test public void givenItemAndItemTypeWhenDeleteThenItemTypeDeleted() { Optional itemType = composedRepository.findById(1L); assertTrue(itemType.isPresent()); Item item = composedRepository.findItemById(2L); assertNotNull(item); composedRepository.findThenDelete(1L); Optional sameItemType = composedRepository.findById(1L); assertFalse(sameItemType.isPresent()); Item sameItem = composedRepository.findItemById(2L); assertNotNull(sameItem); }

6. Fazit

In diesem Artikel haben wir uns die verschiedenen Möglichkeiten angesehen, wie wir Spring Data JPA-Repositorys verwenden können. Wir haben gesehen, dass Spring es einfach macht, Datenbankoperationen für unsere Domänenobjekte durchzuführen, ohne viel Code oder sogar SQL-Abfragen zu schreiben.

Diese Unterstützung kann durch die Verwendung zusammensetzbarer Repositorys erheblich angepasst werden.

Die Codefragmente aus diesem Artikel sind hier auf GitHub als Maven-Projekt verfügbar.