Suppression et relations de Spring Data JPA

1. Vue d'ensemble

Dans ce didacticiel, nous verrons comment la suppression est effectuée dans Spring Data JPA.

2. Exemple d'entité

Comme nous le savons dans la documentation de référence Spring Data JPA, les interfaces de référentiel nous fournissent un support de base pour les entités.

Si nous avons une entité, comme un livre :

@Entity public class Book { @Id @GeneratedValue private Long id; private String title; // standard constructors // standard getters and setters }

Ensuite, nous pouvons étendre CrudRepository de Spring Data JPA pour nous donner accès aux opérations CRUD sur Book :

@Repository public interface BookRepository extends CrudRepository {}

3. Supprimer du référentiel

Entre autres, CrudRepository contient deux méthodes: deleteById et deleteAll .

Testons ces méthodes directement depuis notre BookRepository :

@RunWith(SpringRunner.class) @SpringBootTest(classes = {Application.class}) public class DeleteFromRepositoryUnitTest { @Autowired private BookRepository repository; Book book1; Book book2; List books; // data initialization @Test public void whenDeleteByIdFromRepository_thenDeletingShouldBeSuccessful() { repository.deleteById(book1.getId()); assertThat(repository.count()).isEqualTo(1); } @Test public void whenDeleteAllFromRepository_thenRepositoryShouldBeEmpty() { repository.deleteAll(); assertThat(repository.count()).isEqualTo(0); } }

Et même si nous utilisons CrudRepository , notez que ces mêmes méthodes existent pour d'autres interfaces Spring Data JPA comme JpaRepository ou PagingAndSortingRepository.

4. Requête de suppression dérivée

Nous pouvons également dériver des méthodes de requête pour supprimer des entités. Il existe un ensemble de règles pour les écrire, mais concentrons-nous simplement sur l'exemple le plus simple.

Une requête de suppression dérivée doit commencer par deleteBy , suivi du nom des critères de sélection. Ces critères doivent être fournis dans l'appel de méthode.

Disons que nous voulons supprimer les livres par titre . En utilisant la convention de dénomination, nous commencerions par deleteBy et listerions le titre comme critères:

@Repository public interface BookRepository extends CrudRepository { long deleteByTitle(String title); }

La valeur de retour, de type long , indique le nombre d'enregistrements supprimés par la méthode.

Écrivons un test et assurons-nous qu'il est correct:

@Test @Transactional public void whenDeleteFromDerivedQuery_thenDeletingShouldBeSuccessful() { long deletedRecords = repository.deleteByTitle("The Hobbit"); assertThat(deletedRecords).isEqualTo(1); }

La persistance et la suppression d'objets dans JPA nécessitent une transaction, c'est pourquoi nous devrions utiliser une annotation @Transactional lors de l'utilisation de ces requêtes de suppression dérivées, pour nous assurer qu'une transaction est en cours d'exécution. Ceci est expliqué en détail dans la documentation ORM avec Spring.

5. Requête de suppression personnalisée

Les noms de méthode pour les requêtes dérivées peuvent être assez longs et ils sont limités à une seule table.

Lorsque nous avons besoin de quelque chose de plus complexe, nous pouvons écrire une requête personnalisée en utilisant @Query et @Modifying ensemble.

Vérifions le code équivalent pour notre méthode dérivée de plus tôt:

@Modifying @Query("delete from Book b where b.title=:title") void deleteBooks(@Param("title") String title);

Encore une fois, nous pouvons vérifier que cela fonctionne avec un simple test:

@Test @Transactional public void whenDeleteFromCustomQuery_thenDeletingShouldBeSuccessful() { repository.deleteBooks("The Hobbit"); assertThat(repository.count()).isEqualTo(1); }

Les deux solutions présentées ci-dessus sont similaires et aboutissent au même résultat. Cependant, ils adoptent une approche légèrement différente.

La méthode @Query crée une seule requête JPQL sur la base de données. Par comparaison, les méthodes deleteBy exécutent une requête de lecture, puis suppriment chacun des éléments un par un.

6. Supprimer dans les relations

Voyons maintenant ce qui se passe lorsque nous avons des relations avec d'autres entités.

Supposons que nous ayons une entité Category , qui a une association OneToMany avec l' entité Book :

@Entity public class Category { @Id @GeneratedValue private Long id; private String name; @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true) private List books; // standard constructors // standard getters and setters }

Le CategoryRepository peut simplement être une interface vide qui étend CrudRepository :

@Repository public interface CategoryRepository extends CrudRepository {}

Nous devons également modifier l' entité Book pour refléter cette association:

@ManyToOne private Category category;

Ajoutons maintenant deux catégories et associons-les aux livres que nous avons actuellement. Maintenant, si nous essayons de supprimer les catégories, les livres seront également supprimés:

@Test public void whenDeletingCategories_thenBooksShouldAlsoBeDeleted() { categoryRepository.deleteAll(); assertThat(bookRepository.count()).isEqualTo(0); assertThat(categoryRepository.count()).isEqualTo(0); }

Ce n'est cependant pas bidirectionnel. Cela signifie que si nous supprimons les livres, les catégories sont toujours là:

@Test public void whenDeletingBooks_thenCategoriesShouldAlsoBeDeleted() { bookRepository.deleteAll(); assertThat(bookRepository.count()).isEqualTo(0); assertThat(categoryRepository.count()).isEqualTo(2); }

Nous pouvons modifier ce comportement en modifiant les propriétés de la relation, telles que CascadeType .

7. Conclusion

Dans cet article, nous avons examiné différentes façons de supprimer des entités dans Spring Data JPA. Nous avons examiné les méthodes de suppression fournies de CrudRepository , ainsi que nos requêtes dérivées ou personnalisées à l'aide de l' annotation @Query .

Nous avons également examiné comment la suppression est effectuée dans les relations. Comme toujours, tous les extraits de code mentionnés dans cet article se trouvent sur notre référentiel GitHub.