Exclusão e relacionamentos da JPA de dados da primavera

Exclusão e relacionamentos da JPA de dados da primavera

1. Visão geral

Neste tutorial, daremos uma olhada em como a exclusão é feita emSpring Data JPA.

2. Entidade de amostra

Como sabemos dethe Spring Data JPA reference documentation, as interfaces de repositório nos fornecem algum suporte básico para entidades.

Se tivermos uma entidade, comoBook:

@Entity
public class Book {

    @Id
    @GeneratedValue
    private Long id;
    private String title;

    // standard constructors

    // standard getters and setters
}

Então, podemos estenderCrudRepository do Spring Data JPA para nos dar acesso às operações CRUD emBook:

@Repository
public interface BookRepository extends CrudRepository {}

3. Excluir do repositório

Entre outros,CrudRepository contém dois métodos:deleteByIdedeleteAll.

Vamos testar esses métodos diretamente de nossoBookRepository:

@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 embora estejamos usandoCrudRepository, observe que esses mesmos métodos existem para outras interfaces Spring Data JPA comoJpaRepository ouPagingAndSortingRepository.

4. Consulta de exclusão derivada

Também podemos derivar métodos de consulta para excluir entidades. Há um conjunto de regras para escrevê-los, mas vamos nos concentrar apenas no exemplo mais simples.

A derived delete query must start with deleteBy, followed by the name of the selection criteria. Esses critérios devem ser fornecidos na chamada do método.

Digamos que queremos excluirBooks portitle. Usando a convenção de nomenclatura, começaríamos comdeleteBye listaríamostitle como nosso critério:

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

O valor de retorno, do tipolong, indica quantos registros o método excluiu.

Vamos escrever um teste e verificar se ele está correto:

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

Persisting and deleting objects in JPA requires a transaction, that’s why we should use a @Transactional annotation when using these derived delete queries, to make sure a transaction is running. Isso é explicado em detalhes emORM with Spring documentation.

5. Consulta de exclusão personalizada

Os nomes dos métodos para consultas derivadas podem ser longos e limitados a apenas uma tabela.

Quando precisamos de algo mais complexo, podemos escrever uma consulta personalizada usando@Querye@Modifying juntos.

Vamos verificar o código equivalente para nosso método derivado anterior:

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

Mais uma vez, podemos verificar se funciona com um teste simples:

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

As duas soluções apresentadas acima são semelhantes e alcançam o mesmo resultado. No entanto, eles adotam uma abordagem ligeiramente diferente.

O método@Query cria uma única consulta JPQL no banco de dados. Em comparação, os métodosdeleteBy executam uma consulta de leitura e, em seguida, excluem cada um dos itens um por um.

6. Excluir nos relacionamentos

Vamos ver agora o que acontece quando temosrelationships with other entities.

Suponha que temos uma entidadeCategory, que tem uma associaçãoOneToMany com a entidadeBook:

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

OCategoryRepository pode ser apenas uma interface vazia que estendeCrudRepository:

@Repository
public interface CategoryRepository extends CrudRepository {}

Devemos também modificar a entidadeBook para refletir esta associação:

@ManyToOne
private Category category;

Vamos agora adicionar duas categorias e associá-las aos livros que temos atualmente. Now, if we try to delete the categories, the books will also be deleted:

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

This is not bi-directional, though. Isso significa que se excluirmos os livros, as categorias ainda estarão lá:

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

Podemos mudar esse comportamento alterando as propriedades do relacionamento, comoCascadeType.

7. Conclusão

Neste artigo, analisamos diferentes maneiras de excluir entidades no Spring Data JPA. Vimos os métodos de exclusão fornecidos deCrudRepository, bem como nossas consultas derivadas ou personalizadas usando a anotação@Query.

Também vimos como a exclusão é feita nos relacionamentos. Como sempre, todos os trechos de código mencionados neste artigo podem ser encontrados emour GitHub repository.