Spring Data JPA e grafici di entità denominate

1. Panoramica

In poche parole, i grafici delle entità sono un altro modo per descrivere una query in JPA 2.1. Possiamo usarli per formulare query con prestazioni migliori.

In questo tutorial, impareremo come implementare Entity Graphs con Spring Data JPA attraverso un semplice esempio.

2. Le entità

Per prima cosa, creiamo un modello chiamato Item che ha più caratteristiche:

@Entity public class Item { @Id private Long id; private String name; @OneToMany(mappedBy = "item") private List characteristics = new ArrayList(); // getters and setters }

Ora definiamo l' entità caratteristica C :

@Entity public class Characteristic { @Id private Long id; private String type; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn private Item item; //Getters and Setters }

Come possiamo vedere nel codice, sia il campo delle caratteristiche nell'entità Articolo che il campo dell'elemento nell'entità Caratteristica vengono caricati pigramente usando il parametro fetch . Quindi, il nostro obiettivo qui è caricarli con entusiasmo in fase di esecuzione.

3. I grafici delle entità

In Spring Data JPA, possiamo definire un grafico di entità utilizzando una combinazione di annotazioni @NamedEntityGraph e @EntityGraph . Oppure, possiamo anche definire grafici di entità ad-hoc con solo l' argomento attributePaths dell'annotazione @EntityGraph .

Vediamo come si può fare.

3.1. Con @NamedEntityGraph

Innanzitutto, possiamo utilizzare l' annotazione @NamedEntityGraph di JPA direttamente sulla nostra entità Item :

@Entity @NamedEntityGraph(name = "Item.characteristics", attributeNodes = @NamedAttributeNode("characteristics") ) public class Item { //... }

Quindi, possiamo allegare l' annotazione @EntityGraph a uno dei nostri metodi di repository:

public interface ItemRepository extends JpaRepository { @EntityGraph(value = "Item.characteristics") Item findByName(String name); }

Come mostra il codice, abbiamo passato il nome dell'entità grafico, che abbiamo creato in precedenza sull'entità Item , all'annotazione @EntityGraph . Quando chiamiamo il metodo, questa è la query che verrà utilizzata da Spring Data.

Il valore predefinito dell'argomento type dell'annotazione @EntityGraph è EntityGraphType.FETCH . Quando lo usiamo, il modulo Spring Data applicherà la strategia FetchType.EAGER sui nodi degli attributi specificati. E per altri, verrà applicata la strategia FetchType.LAZY .

Quindi nel nostro caso, la proprietà delle caratteristiche verrà caricata con entusiasmo, anche se la strategia di recupero predefinita dell'annotazione @OneToMany è pigra.

Un problema qui è che se la strategia di recupero definita è EAGER, non possiamo cambiare il suo comportamento in LAZY . Ciò è di progettazione poiché le operazioni successive potrebbero richiedere i dati recuperati con impazienza in un momento successivo durante l'esecuzione.

3.2. Senza @NamedEntityGraph

Oppure possiamo definire anche un grafico di entità ad-hoc con attributePaths.

Aggiungiamo un grafico di entità ad-hoc al nostro CharacteristicsRepository che carica con entusiasmo il suo elemento genitore:

public interface CharacteristicsRepository extends JpaRepository { @EntityGraph(attributePaths = {"item"}) Characteristic findByType(String type); }

Questo caricherà con entusiasmo la proprietà dell'elemento dell'entità Characteristic , anche se la nostra entità dichiara una strategia di caricamento lento per questa proprietà.

Ciò è utile poiché possiamo definire il grafico di entità in linea invece di fare riferimento a un grafico di entità con nome esistente.

4. Scenario di prova

Ora che abbiamo definito i nostri grafici di entità, creiamo un test case per verificarlo:

@DataJpaTest @RunWith(SpringRunner.class) @Sql(scripts = "/entitygraph-data.sql") public class EntityGraphIntegrationTest { @Autowired private ItemRepository itemRepo; @Autowired private CharacteristicsRepository characteristicsRepo; @Test public void givenEntityGraph_whenCalled_shouldRetrunDefinedFields() { Item item = itemRepo.findByName("Table"); assertThat(item.getId()).isEqualTo(1L); } @Test public void givenAdhocEntityGraph_whenCalled_shouldRetrunDefinedFields() { Characteristic characteristic = characteristicsRepo.findByType("Rigid"); assertThat(characteristic.getId()).isEqualTo(1L); } }

Il primo test utilizzerà il grafico di entità definito utilizzando l' annotazione @NamedEntityGraph .

Vediamo l'SQL generato da Hibernate:

select item0_.id as id1_10_0_, characteri1_.id as id1_4_1_, item0_.name as name2_10_0_, characteri1_.item_id as item_id3_4_1_, characteri1_.type as type2_4_1_, characteri1_.item_id as item_id3_4_0__, characteri1_.id as id1_4_0__ from item item0_ left outer join characteristic characteri1_ on item0_.id=characteri1_.item_id where item0_.name=?

Per confronto, rimuoviamo l' annotazione @EntityGraph dal repository e controlliamo la query:

select item0_.id as id1_10_, item0_.name as name2_10_ from item item0_ where item0_.name=?

Da queste query, possiamo chiaramente osservare che la query generata senza l' annotazione @EntityGraph non carica alcuna proprietà dell'entità Characteristic . Di conseguenza, carica solo l' entità Articolo .

Infine, confrontiamo le query Hibernate del secondo test con l' annotazione @EntityGraph :

select characteri0_.id as id1_4_0_, item1_.id as id1_10_1_, characteri0_.item_id as item_id3_4_0_, characteri0_.type as type2_4_0_, item1_.name as name2_10_1_ from characteristic characteri0_ left outer join item item1_ on characteri0_.item_id=item1_.id where characteri0_.type=?

E la query senza l' annotazione @EntityGraph :

select characteri0_.id as id1_4_, characteri0_.item_id as item_id3_4_, characteri0_.type as type2_4_ from characteristic characteri0_ where characteri0_.type=?

5. conclusione

In questo tutorial, abbiamo imparato come utilizzare i grafici delle entità JPA in Spring Data. Con Spring Data, possiamo creare più metodi di repository che sono collegati a diversi grafici di entità .

Gli esempi per questo articolo sono disponibili su GitHub.