Eliminazione e relazioni di Spring Data JPA

1. Panoramica

In questo tutorial, daremo un'occhiata a come viene eseguita l'eliminazione in Spring Data JPA.

2. Entità campione

Come sappiamo dalla documentazione di riferimento JPA Spring Data, le interfacce del repository ci forniscono un supporto di base per le entità.

Se abbiamo un'entità, come un libro :

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

Quindi, possiamo estendere CrudRepository di Spring Data JPA per darci accesso alle operazioni CRUD su Book :

@Repository public interface BookRepository extends CrudRepository {}

3. Elimina dal repository

Tra gli altri, CrudRepository contiene due metodi: deleteById e deleteAll .

Testiamo questi metodi direttamente dal nostro 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); } }

E anche se stiamo usando CrudRepository , nota che questi stessi metodi esistono per altre interfacce JPA Spring Data come JpaRepository o PagingAndSortingRepository.

4. Query di eliminazione derivata

Possiamo anche derivare metodi di query per l'eliminazione di entità. C'è una serie di regole per scriverli, ma concentriamoci solo sull'esempio più semplice.

Una query di eliminazione derivata deve iniziare con deleteBy , seguito dal nome dei criteri di selezione. Questi criteri devono essere forniti nella chiamata al metodo.

Supponiamo di voler eliminare i libri per titolo . Usando la convenzione di denominazione, iniziamo con deleteBy ed elenchiamo il titolo come criterio:

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

Il valore restituito, di tipo long , indica il numero di record eliminati dal metodo.

Scriviamo un test e assicuriamoci che sia corretto:

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

La persistenza e l'eliminazione di oggetti in JPA richiede una transazione, ecco perché dovremmo utilizzare un'annotazione @Transactional quando si utilizzano queste query di eliminazione derivate, per assicurarsi che una transazione sia in esecuzione. Questo è spiegato in dettaglio nella documentazione ORM con Spring.

5. Query di eliminazione personalizzata

I nomi dei metodi per le query derivate possono diventare piuttosto lunghi e sono limitati a una sola tabella.

Quando abbiamo bisogno di qualcosa di più complesso, possiamo scrivere una query personalizzata usando @Query e @Modifying insieme.

Controlliamo il codice equivalente per il nostro metodo derivato da prima:

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

Di nuovo, possiamo verificare che funzioni con un semplice test:

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

Entrambe le soluzioni presentate sopra sono simili e ottengono lo stesso risultato. Tuttavia, adottano un approccio leggermente diverso.

Il metodo @Query crea una singola query JPQL sul database. In confronto, i metodi deleteBy eseguono una query di lettura, quindi eliminano ciascuno degli elementi uno per uno.

6. Elimina nelle relazioni

Vediamo ora cosa succede quando abbiamo relazioni con altre entità.

Supponiamo di avere un'entità Category , che ha un'associazione OneToMany con 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 }

Il CategoryRepository può essere solo un'interfaccia vuota che estende CrudRepository :

@Repository public interface CategoryRepository extends CrudRepository {}

Dovremmo anche modificare l' entità Libro per riflettere questa associazione:

@ManyToOne private Category category;

Aggiungiamo ora due categorie e associamole ai libri che abbiamo attualmente. Ora, se proviamo a eliminare le categorie, verranno eliminati anche i libri:

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

Questo però non è bidirezionale. Ciò significa che se cancelliamo i libri, le categorie sono ancora lì:

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

È possibile modificare questo comportamento modificando le proprietà della relazione, ad esempio CascadeType .

7. Conclusione

In questo articolo, abbiamo esaminato diversi modi per eliminare entità in Spring Data JPA. Abbiamo esaminato i metodi di eliminazione forniti da CrudRepository , nonché le nostre query derivate o quelle personalizzate utilizzando l' annotazione @Query .

Abbiamo anche esaminato come viene eseguita l'eliminazione nelle relazioni. Come sempre, tutti gli snippet di codice menzionati in questo articolo possono essere trovati nel nostro repository GitHub.