Spring Data 3 中的新 CRUD Repository 接口

1、概览

本文将带你了解 Spring Data 3 中引入的新 Repository 接口。

Spring Data 3 引入了基于 List 的 CRUD Repository 接口,可用于替换返回 Iterable 的现有 CRUD Repository 接口。此外,分页和排序接口默认不继承原始 CRUD Repository,而是由用户自己选择。接下来看看这些接口与现有接口有何不同,以及如何使用它们。

2、项目设置

首先创建一个 Spring Boot 应用,其中包含一个简单的实体和将使用新接口的 Repository。

2.1、依赖

首先,为项目添加所需的 spring-boot-starter-data-jpa 依赖:

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

除此之外,还要配置数据库。可以使用任何 SQL 数据库,本文将使用 H2 内存数据库:

<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

2.2、Entity

创建一个 Book 实体:

@Entity
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String title;
    private String author;
    private String isbn;

    // 构造器、Getter、Setter
}

有了实体后,来看看如何使用新接口与数据库交互。

3、基于 List 的 Repository

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

3.1、Spring Data 3 中新增了三个接口

ListCrudRepository 接口提供了基本 CRUD 操作方法,如 save()、delete() 和 findById()。ListCrudRepository 与基于 Iterable 的对应 Repository 的主要区别在于,返回值现在是 List,这使我们可以对返回的数据执行更高级的操作。

ListQuerydslPredicateExecutor 接口提供了使用 Querydsl Predicate 执行查询的方法。Querydsl 是一个允许在 Java 中构建类型安全的 SQL 类查询的框架。通过 ListQuerydslPredicateExecutor,我们可以执行 Querydsl 查询并以 List 形式返回结果。

ListQueryByExampleExecutor 接口提供了使用 Example 实体执行查询并以 List 形式返回结果的方法。Example 实体是指包含我们想用来搜索其他实体的值的实体。例如,如果我们有一个 title 为 Spring Data 的 Book 实体,我们就可以创建一个 title 为 Spring Data 的示例实体,并用它来搜索 title 相同的其他 Book。

3.2、ListCrudRepository

来详细看看 ListCrudRepository 接口。首先,创建一个使用该接口与数据库交互的 Repository:

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

上述 Repository 继承了 ListCrudRepository 接口,为我们提供了基本的 CRUD 方法。我们可以使用现有的 Repository 方法来保存、删除和查找数据库中的 Book。

除这些方法外,还自定义了 findBooksByAuthor() 方法,用于按 author 查找 Book。

3.3、使用基于 List 的 Repository

来看看如何使用该 Repository 与数据库交互:

@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());
    }
}

首先创建了三个 Boot 实体,并将它们保存到数据库中。然后,使用 findBooksByAuthor() 方法查找 author为 John Doe 的所有 Book。最后,验证返回的 List 是否包含所创建的三个 Boot 实体。

注意,我们在返回的 List 上调用 了 size() 方法。如果 Repository 使用基于 Iterable 的接口,就不可能这样做,因为 Iterable 接口未提供 size() 方法。

4、排序接口

为了能够使用新的基于 List 的接口,Spring Data 3 必须对现有的排序接口进行更改。 PagingAndSortingRepository 不再继承旧的 CrudRepository。相反,用户可以选择在继承排序接口的同时继承新的基于 List 的接口或基于 Iterable 的接口。

4.1、PagingAndSortingRepository

来看看,PagingAndSortingRepository 接口,创建一个使用该接口与数据库交互的 Repository:

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

上述 Repository 继承了 ListCrudRepository 接口,该接口为我们提供了基本的 CRUD 方法。除此之外,还继承了 PagingAndSortingRepository 接口,它为我们提供了排序和分页方法。

在 Spring Data 的旧版本中,不需要明确继承 CrudRepository 接口。只有排序和分页接口就足够了。这里还定义了一个名为 findBooksByAuthor() 的新方法,它接收一个 Pageable 参数并返回一个 List<Book>。

接下来,看看如何使用该方法对结果进行排序和分页。

4.2、使用排序 Repository 接口

使用该 Repository 与数据库交互:

@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());
    }
}

和之前一样,我们首先创建了三个 Book 实体,并将它们保存到数据库中。然后,使用 findBooksByAuthor() 方法查找 author 为 John Doe 的所有书籍。但这次,我们向该方法传递了一个 Pageable 对象,按 title 降序排序,并只返回前两个结果。

最后,验证返回的 List 是否包含之前创建的两个 Book 实体。还验证了 Book 是按 title 降序排序的,因此 title 为 Spring Data 3 的书首先返回,标题为 Spring Data 2 的书其次返回。

4.3、其他排序接口

除 PagingAndSortingRepository 接口外,还更改了以下接口:

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

5、总结

本文介绍了在 Spring Data 3 中新增的基于 List 的 ListCrudRepository 接口,以及如何使用这些接口与数据库进行交互。还介绍了对现有排序接口所做的更改,以便能够同时使用新的 ListCrudRepository 的接口。


Ref:https://www.baeldung.com/spring-data-3-crud-repository-interfaces