Spring Data 3 中新的 CRUD 存储库接口


在本教程中,我们将了解 Spring Data 3 中引入的新存储库接口。

Spring Data 3 引入了基于 List 的 CRUD 存储库接口,可用于替换现有的返回 Iterable 的 CRUD 存储库接口。此外,分页和排序接口默认不继承原始 CRUD 存储库,而是将该选项留给用户。我们将了解这些接口与现有接口有何不同以及如何使用它们。

依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    <version>3.0.3</version>
</dependency>


基于列表的 CRUD 存储库
Spring Data 3 引入了一组新的 CRUD 存储库接口,可返回实体列表。这些接口类似于现有接口,除了它们返回一个 List 而不是 Iterable。这使我们能够使用List接口中的高级方法,例如 get() 和 indexOf()。

在Spring Data 3中,有三个新的接口被加入:

  1. ListCrudRepository接口为基本的CRUD操作提供了方法,如save()、delete()和findById()。ListCrudRepository与基于Iterable的对应接口的关键区别在于,现在返回的值是列表,这使得我们可以对返回的数据进行更高级的操作。
  2. ListQuerydslPredicateExecutor接口提供了使用Querydsl谓词执行查询的方法。Querydsl是一个框架,它允许我们在Java中建立类型安全的类似SQL的查询。通过ListQuerydslPredicateExecutor,我们可以执行Querydsl查询,并以列表形式返回结果。
  3. ListQueryByExampleExecutor接口提供了使用示例实体执行查询的方法,并将结果作为一个列表返回。一个示例实体是一个包含我们想用来搜索其他实体的值的实体。例如,如果我们有一个标题为Spring Data的Book实体,我们可以创建一个标题为Spring Data的例子实体,并使用它来搜索具有相同标题的其他书籍。

ListCrudRepository
让我们详细看看ListCrudRepository接口。我们将创建一个存储库,它将使用这个接口与数据库交互:

@Repository
public interface BookListRepository extends ListCrudRepository<Book, Long> {
    
    List<Book> findBooksByAuthor(String author);
}

@SpringBootTest
public class BookListRepositoryIntegrationTest {

    @Autowired
    private BookListRepository bookListRepository;
    
    @Test
    public void givenDbContainsBooks_whenFindBooksByAuthor_thenReturnBooksByAuthor() {
        Book book1 = new Book("Spring Data", "John Doe", "1234567890");
        Book book2 = new Book(
"Spring Data 2", "John Doe", "1234567891");
        Book book3 = new Book(
"Spring Data 3", "John Doe", "1234567892");
        bookListRepository.saveAll(Arrays.asList(book1, book2, book3));

        List<Book> books = bookListRepository.findBooksByAuthor(
"John Doe");
        Assert.assertEquals(3, books.size());
    }
}


上述资源库扩展了ListCrudRepository接口,它为我们提供了基本的CRUD方法。我们可以使用现有的资源库方法来保存、删除和查找数据库中的书籍。

除了这些方法外,我们还定义了findBooksByAuthor()方法来按作者查找书籍。

在测试代码中:我们首先创建了三本书并把它们保存到数据库中。然后,我们使用findBooksByAuthor()方法找到作者John Doe的所有书籍。最后,我们验证了返回的列表包含我们创建的三本书。

请注意,我们对返回的列表调用了size()方法。如果资源库使用基于 Iterable 的接口,这是不可能的,因为 Iterable 接口没有提供 size() 方法。


基于排序的 CRUD 存储库
为了能够使用新的基于列表的接口,Spring Data 3不得不对现有的排序接口进行修改。排序库不再扩展旧的crud库了。相反,用户可以选择扩展新的基于List的接口或基于Iterable的接口以及排序接口。让我们来看看如何使用最新的排序接口。

让我们详细看看PagingAndSortingRepository接口。我们将创建一个存储库,该存储库将使用此接口与数据库进行交互:

@Repository
public interface BookPagingAndSortingRepository extends PagingAndSortingRepository<Book, Long>, ListCrudRepository<Book, Long> {
    
    List<Book> findBooksByAuthor(String author, Pageable pageable);
}

@SpringBootTest
public class BookPagingAndSortingRepositoryIntegrationTest {

    @Autowired
    private BookPagingAndSortingRepository bookPagingAndSortingRepository;
    
    @Test
    public void givenDbContainsBooks_whenfindBooksByAuthor_thenReturnBooksByAuthor() {
        Book book1 = new Book("Spring Data", "John Doe", "1234567890");
        Book book2 = new Book(
"Spring Data 2", "John Doe", "1234567891");
        Book book3 = new Book(
"Spring Data 3", "John Doe", "1234567892");
        bookPagingAndSortingRepository.saveAll(Arrays.asList(book1, book2, book3));

        Pageable pageable = PageRequest.of(0, 2, Sort.by(
"title").descending());
        List<Book> books = bookPagingAndSortingRepository.findBooksByAuthor(
"John Doe", pageable);
        Assert.assertEquals(2, books.size());
        Assertions.assertEquals(book3.getId(), books.get(0).getId());
        Assertions.assertEquals(book2.getId(), books.get(1).getId());
    }
}

上面的仓库扩展了ListCrudRepository接口,它为我们提供了基本的 CRUD 方法。除此之外, 我们还扩展了PagingAndSortingRepository接口,它为我们提供了排序和分页的方法。

在旧版本的 Spring Data中,我们不需要显式扩展 CRUD 存储库接口。只有排序和分页界面就足够了。我们定义了一个名为findBooksByAuthor()的新方法 ,它采用 Pageable 参数并返回图书列表。在下一节中,我们将了解如何使用此方法对结果进行排序和分页。

在测试中:我们首先创建了三本书,并将它们保存到数据库中。然后,我们使用findBooksByAuthor()方法查找作者John Doe 的所有书籍 。但是这一次,我们将一个Pageable 对象传递给该方法,以按标题降序对结果进行排序,并仅返回前两个结果。

最后,我们验证返回的列表包含我们创建的两本书。我们还验证了书籍按标题降序排列,因此首先返回标题为Spring Data 3 的书籍,然后 返回 标题为 Spring Data 2的书籍。


其他排序接口
除了PagingAndSortingRepository接口外,还更改了以下接口:

  • ReactiveSortingRepository不再继承自ReactiveCrudRepository
  • CoroutineSortingRepository不再继承自CoroutineCrudRepository
  • RxJavaSortingRepository不再继承自RxJavaCrudRepository

可以在 GitHub 上找到本文的代码示例。