Spring Data JPA et graphiques d'entités nommées

1. Vue d'ensemble

En termes simples, les graphiques d'entité sont une autre façon de décrire une requête dans JPA 2.1. Nous pouvons les utiliser pour formuler des requêtes plus performantes.

Dans ce didacticiel, nous allons apprendre à implémenter des graphiques d'entité avec Spring Data JPA à travers un exemple simple.

2. Les entités

Tout d'abord, créons un modèle appelé Item qui a plusieurs caractéristiques:

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

Définissons maintenant l' entité C haracteristic :

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

Comme nous pouvons le voir dans le code, le champ de caractéristiques dans l' entité Item et le champ item dans l' entité Characteristic sont chargés paresseusement à l'aide du paramètre fetch . Donc, notre objectif ici est de les charger avec impatience lors de l'exécution.

3. Les graphiques d'entité

Dans Spring Data JPA, nous pouvons définir un graphique d'entité à l'aide d'une combinaison d' annotations @NamedEntityGraph et @EntityGraph . Ou, nous pouvons également définir des graphes d'entités ad hoc avec uniquement l' argument attributePaths de l' annotation @EntityGraph .

Voyons comment cela peut être fait.

3.1. Avec @NamedEntityGraph

Tout d'abord, nous pouvons utiliser l' annotation @NamedEntityGraph de JPA directement sur notre entité Item :

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

Et puis, nous pouvons attacher l' annotation @EntityGraph à l'une de nos méthodes de référentiel:

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

Comme le montre le code, nous avons transmis le nom du graphique d'entité, que nous avons créé précédemment sur l' entité Item , à l' annotation @EntityGraph . Lorsque nous appelons la méthode, c'est la requête que Spring Data utilisera.

La valeur par défaut de l'argument type de @EntityGraph annotation est EntityGraphType.FETCH . Lorsque nous l'utilisons, le module Spring Data appliquera la stratégie FetchType.EAGER sur les nœuds d'attributs spécifiés. Et pour d'autres, la stratégie FetchType.LAZY sera appliquée.

Dans notre cas, les caractéristiques propriété sera chargé avec impatience, même si la valeur par défaut la stratégie de l'extraction @OneToMany annotation est paresseux.

Un problème ici est que si la stratégie de récupération définie est EAGER, nous ne pouvons pas changer son comportement en LAZY . C'est par conception car les opérations suivantes peuvent nécessiter les données extraites avec impatience à un stade ultérieur de l'exécution.

3.2. Sans @NamedEntityGraph

Ou, nous pouvons également définir un graphe d'entités ad hoc, avec attributePaths.

Ajoutons un graphique d'entité ad hoc à notre CharacteristicsRepository qui charge avec impatience son parent Item :

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

Cela chargera la propriété item de l' entité Characteristic avec empressement, même si notre entité déclare une stratégie de chargement différé pour cette propriété.

Ceci est pratique car nous pouvons définir le graphe d'entité en ligne au lieu de faire référence à un graphe d'entité nommé existant.

4. Cas de test

Maintenant que nous avons défini nos graphiques d'entités, créons un cas de test pour le vérifier:

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

Le premier test utilisera le graphe d'entité défini à l'aide de l' annotation @NamedEntityGraph .

Voyons le SQL généré par 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=?

À titre de comparaison, supprimons l' annotation @EntityGraph du référentiel et inspectons la requête:

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

À partir de ces requêtes, nous pouvons clairement observer que la requête générée sans l' annotation @EntityGraph ne charge aucune propriété de l' entité Characteristic . En conséquence, il charge uniquement l' entité Item .

Enfin, comparons les requêtes Hibernate du deuxième test avec l' annotation @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=?

Et la requête sans l' annotation @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. Conclusion

Dans ce didacticiel, nous avons appris à utiliser les graphiques d'entité JPA dans Spring Data. Avec Spring Data, nous pouvons créer plusieurs méthodes de référentiel liées à différents graphiques d'entités .

Les exemples de cet article sont disponibles à l'adresse over sur GitHub.