Spring Data Composable Repositories

Zusammenstellbare Spring Data-Repositorys

1. Einführung

Bei der Modellierung eines realen Systems oder Prozesses sind DDD-Repositorys (Domain-Driven Design) eine gute Option. Zu diesem Zweck können wir Spring Data JPA als unsere Datenzugriffsabstraktionsschicht verwenden.

Wenn Sie mit diesem Konzept noch nicht vertraut sind, lesen Siethis introductory 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.0.9.RELEASE

Wir müssten auch eine Datenquelle einrichten, damit unsere Datenzugriffsschicht funktioniert. Es ist eine gute Idee,set up an in-memory database like H2 für die Entwicklung und schnelle Tests zu verwenden.

3. Hintergrund

3.1. Ruhezustand als JPA-Implementierung

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

Spring Data JPA ist die Datenzugriffsabstraktionsschicht, unter der wir eine beliebige Implementierung verwenden können. Wir könnten zum Beispielswitch out Hibernate in favor of EclipseLink.

3.2. Standard-Repositorys

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

Stattdessen müssen nur Schnittstellen erstellt werden, 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 dasLocation-Objekt durchzuführen, das einen Primärschlüssel vom TypLong hat.

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

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

3.3. Benutzerdefinierte Repositorys

Bei Bedarf können wirenrich our model repository schreiben, indem wir eine Fragmentschnittstelle schreiben und die gewünschte Funktionalität implementieren. Dies kann dann in unser eigenes JPA-Repository injiziert werden.

Zum Beispiel bereichern wir hier unsereItemTypeRepository, indem wir ein Fragment-Repository erweitern:

public interface ItemTypeRepository
  extends JpaRepository, CustomItemTypeRepository {
}

Hier istCustomItemTypeRepository eine andere Schnittstelle:

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

Die Implementierung kann ein beliebiges Repository 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 PostfixImpl hat. Mithilfe der folgenden XML-Konfiguration können wir jedoch ein benutzerdefiniertes Postfix festlegen:

oder mit dieser Anmerkung:

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

4. Erstellen von Repositorys mit mehreren Fragmenten

Bis vor wenigen 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 Option aufenrich our JPA repository with multiple fragment repositories. Auch hier bleibt die Anforderung bestehen, 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üssen wir ihre Implementierungen schreiben. Anstatt diese benutzerdefinierten Repositorys - mit verwandten 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 haben wir alle verknüpften Funktionen in einem einzigen Repository.

5. Der Umgang mit Mehrdeutigkeit

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 MethodefindThenDelete() mit derselben Signatur.

In diesem Szenariothe order of the declaration of the interfaces is used to resolve the ambiguity. Folglich wird in unserem Fall die Methode inCustomItemTypeRepository verwendet, da sie zuerst deklariert wurde.

Wir können dies mit diesem Testfall 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, mit denen wir JPA-Repositorys von Spring Data verwenden können. Wir haben gesehen, dass Spring es einfach macht, Datenbankoperationen an unseren Domänenobjekten durchzuführen, ohne viel Code oder sogar SQL-Abfragen zu schreiben.

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

Die Codefragmente aus diesem Artikel sind alsMaven project here on Github verfügbar.