Repository Spring Data Composable

1. Introduzione

Quando si modella un sistema o processo del mondo reale, i repository di stile DDD (Domain-Driven Design) sono una buona opzione. A tale scopo, possiamo utilizzare Spring Data JPA come livello di astrazione dell'accesso ai dati.

Se non conosci questo concetto, dai un'occhiata a questo tutorial introduttivo per aiutarti a metterti al passo.

In questo tutorial, ci concentreremo sul concetto di creazione di repository personalizzati e componibili che vengono creati utilizzando repository più piccoli chiamati frammenti.

2. Dipendenze di Maven

L'opzione per creare repository componibili è disponibile a partire da Spring 5.

Aggiungiamo la dipendenza richiesta per Spring Data JPA:

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

Avremmo anche bisogno di configurare un'origine dati affinché il nostro livello di accesso ai dati funzioni. È una buona idea impostare un database in memoria come H2 per lo sviluppo e il test rapido.

3. Sfondo

3.1. Iberna come implementazione JPA

Spring Data JPA, per impostazione predefinita, utilizza Hibernate come implementazione JPA. Possiamo facilmente confondere l'uno con l'altro o confrontarli, ma servono a scopi diversi.

Spring Data JPA è il livello di astrazione dell'accesso ai dati al di sotto del quale possiamo utilizzare qualsiasi implementazione. Potremmo, ad esempio, disattivare Hibernate a favore di EclipseLink.

3.2. Repository predefiniti

In molti casi, non avremmo bisogno di scrivere alcuna domanda da soli.

Invece, abbiamo solo bisogno di creare interfacce che a loro volta estendano le interfacce generiche del repository di dati Spring:

public interface LocationRepository extends JpaRepository { }

E questo, di per sé, ci consentirebbe di eseguire operazioni comuni - CRUD, paging e ordinamento - sull'oggetto Location che ha una chiave primaria di tipo Long .

Inoltre, Spring Data JPA è dotato di un meccanismo di creazione di query che fornisce la possibilità di generare query per nostro conto utilizzando le convenzioni del nome del metodo:

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

3.3. Repository personalizzati

Se necessario, possiamo arricchire il nostro repository di modelli scrivendo un'interfaccia di frammento e implementando la funzionalità desiderata. Questo può quindi essere inserito nel nostro repository JPA.

Ad esempio, qui stiamo arricchendo il nostro ItemTypeRepository estendendo un repository di frammenti:

public interface ItemTypeRepository extends JpaRepository, CustomItemTypeRepository { }

Qui CustomItemTypeRepository è un'altra interfaccia:

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

La sua implementazione può essere un repository di qualsiasi tipo, non solo JPA:

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

Dobbiamo solo assicurarci che abbia il suffisso Impl . Tuttavia, possiamo impostare un suffisso personalizzato utilizzando la seguente configurazione XML:

o utilizzando questa annotazione:

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

4. Composizione di archivi utilizzando più frammenti

Fino a poche versioni fa, potevamo estendere le nostre interfacce di repository solo utilizzando una singola implementazione personalizzata. Questa era una limitazione a causa della quale avremmo dovuto portare tutte le funzionalità correlate in un unico oggetto.

Inutile dire che, per progetti più grandi con modelli di dominio complessi, questo porta a classi gonfie.

Ora con Spring 5, abbiamo la possibilità di arricchire il nostro repository JPA con più repository di frammenti . Ancora una volta, resta il requisito che abbiamo questi frammenti come coppie di implementazione dell'interfaccia.

Per dimostrarlo, creiamo due frammenti:

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); }

Ovviamente avremmo bisogno di scrivere le loro implementazioni. Ma invece di collegare questi repository personalizzati, con le relative funzionalità, nei loro repository JPA, possiamo estendere la funzionalità di un singolo repository JPA:

public interface ItemTypeRepository extends JpaRepository, CustomItemTypeRepository, CustomItemRepository { }

Ora avremmo tutte le funzionalità collegate in un unico repository.

5. Trattare con ambiguità

Dato che ereditiamo da più repository, potremmo avere problemi a capire quale delle nostre implementazioni verrebbe utilizzata in caso di conflitto. Ad esempio, nel nostro esempio, entrambi i repository di frammenti hanno un metodo, findThenDelete () , con la stessa firma.

In questo scenario, l'ordine della dichiarazione delle interfacce viene utilizzato per risolvere l'ambiguità . Di conseguenza, nel nostro caso, verrà utilizzato il metodo all'interno di CustomItemTypeRepository poiché è stato dichiarato per primo.

Possiamo testarlo usando questo test case:

@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. Conclusione

In questo articolo, abbiamo esaminato i diversi modi in cui possiamo utilizzare i repository JPA Spring Data. Abbiamo visto che Spring rende semplice eseguire operazioni di database sui nostri oggetti di dominio senza scrivere molto codice o persino query SQL.

Questo supporto è notevolmente personalizzabile attraverso l'uso di repository componibili.

Gli snippet di codice di questo articolo sono disponibili come progetto Maven qui su GitHub.