Spring Data JPA Delete and Relationships – Spring Data JPA删除和关系

最后修改: 2019年 4月 23日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this tutorial, we’ll have a look at how deleting is done in Spring Data JPA.

在本教程中,我们将看看如何在Spring Data JPA中进行删除。

2. Sample Entity

2.实体样本

As we know from the Spring Data JPA reference documentation, repository interfaces provide us some basic support for entities.

正如我们从Spring Data JPA 参考文档中了解到的那样,存储库接口为我们提供了对实体的一些基本支持。

Let’s say we have an entity, such as a Book:

假设我们有一个实体,比如说

@Entity
public class Book {

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

    // standard constructors

    // standard getters and setters
}

Then we can extend Spring Data JPA’s CrudRepository to give us access to CRUD operations on Book:

然后我们可以扩展Spring Data JPA的CrudRepository,使我们能够访问对Book的CRUD操作。

@Repository
public interface BookRepository extends CrudRepository<Book, Long> {}

3. Delete from Repository

3.从存储库中删除

Among others, CrudRepository contains two methods: deleteById and deleteAll.

其中,CrudRepository包含两个方法。deleteByIddeleteAll

Let’s test these methods directly from our BookRepository:

让我们直接从我们的BookRepository测试这些方法。

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class})
public class DeleteFromRepositoryUnitTest {

    @Autowired
    private BookRepository repository;

    Book book1;
    Book book2;
    List<Book> 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);
    }
}

And even though we are using CrudRepository, note that these same methods exist for other Spring Data JPA interfaces such as JpaRepository or PagingAndSortingRepository.

尽管我们使用的是CrudRepository,但请注意,这些方法同样存在于其他Spring Data JPA接口,如JpaRepositoryPagingAndSortingRepository

4. Derived Delete Query

4.衍生的删除查询

We can also derive query methods for deleting entities. There is a set of rules for writing them, but let’s just focus on the simplest example.

我们还可以推导出删除实体的查询方法。有一套编写它们的规则,但我们只关注最简单的例子。

A derived delete query must start with deleteBy, followed by the name of the selection criteria. These criteria must be provided in the method call.

一个派生的删除查询必须以deleteBy开始,后面是选择标准的名称。这些标准必须在方法调用中提供。

Let’s say that we want to delete Books by title. Using the naming convention, we’d start with deleteBy and list title as our criteria:

假设我们想按title删除Books。使用命名惯例,我们将从deleteBy开始,并将title列为我们的标准。

@Repository
public interface BookRepository extends CrudRepository<Book, Long> {
    long deleteByTitle(String title);
}

The return value, of type long, indicates how many records the method deleted.

返回值,类型为long,表示该方法删除了多少条记录。

Let’s write a test and make sure that is correct:

让我们写一个测试,确保这一点是正确的。

@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. This is explained in detail in the ORM with Spring documentation.

在JPA中存在和删除对象需要一个事务。这就是为什么我们在使用这些衍生的删除查询时应使用@Transactional注解,以确保事务正在运行。这在ORM with Spring 文档中得到了详细说明。

5. Custom Delete Query

5.自定义删除查询

The method names for derived queries can get quite long, and they are limited to just a single table.

派生查询的方法名称可能会变得相当长,而且它们只限于一个表。

When we need something more complex, we can write a custom query using @Query and @Modifying together.

当我们需要更复杂的东西时,我们可以使用@Query@Modifying一起编写一个自定义查询。

Let’s check the equivalent code for our derived method from earlier:

让我们检查一下我们前面的派生方法的等效代码。

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

Again, we can verify it works with a simple test:

同样,我们可以通过一个简单的测试来验证它的作用。

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

Both solutions presented above are similar and achieve the same result. However, they take a slightly different approach.

上面介绍的两种解决方案都很相似,并取得了相同的结果。然而,它们采取的方法略有不同。

The @Query method creates a single JPQL query against the database. By comparison, the deleteBy methods execute a read query and then delete each of the items one by one.

@Query方法针对数据库创建一个单一的JPQL查询。相比之下,deleteBy方法执行一个读取查询,然后一个一个地删除每个项目。

6. Delete in Relationships

6.在关系中删除

Now let’s see what happens when we have relationships with other entities.

现在让我们看看当我们与其他实体有关系时会发生什么。

Assume we have a Category entity that has a OneToMany association with the Book entity:

假设我们有一个Category实体,它与Book实体有OneToMany关联。

@Entity
public class Category {

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

    @OneToMany(mappedBy = "category", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Book> books;

    // standard constructors

    // standard getters and setters
}

The CategoryRepository can just be an empty interface that extends CrudRepository:

CategoryRepository可以只是一个扩展CrudRepository的空接口。

@Repository
public interface CategoryRepository extends CrudRepository<Category, Long> {}

We should also modify the Book entity to reflect this association:

我们还应该修改Book实体以反映这种关联。

@ManyToOne
private Category category;

Let’s now add two categories and associate them with the books we currently have.

现在让我们添加两个类别,并将它们与我们目前拥有的书籍联系起来。

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, meaning that if we delete the books, the categories are still there:

但这不是双向的,也就是说,如果我们删除了书籍,类别仍然在那里。

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

We can change this behavior by changing the properties of the relationship, such as the CascadeType.

我们可以通过改变关系的属性来改变这种行为,比如CascadeType

7. Conclusion

7.结语

In this article, we saw different ways to delete entities in Spring Data JPA.

在这篇文章中,我们看到了在Spring Data JPA中删除实体的不同方法。

We looked at the provided delete methods from CrudRepository as well as our derived queries or custom ones using @Query annotation.

我们查看了CrudRepository提供的删除方法,以及我们的衍生查询或使用@Query注解的自定义查询。

We also saw how deleting is done in relationships.

我们还看到了在关系中是如何进行删除的。

As always, all of the code snippets mentioned in this article can be found on our GitHub repository.

一如既往,本文中提到的所有代码片段都可以在我们的GitHub仓库中找到。