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 实体是指包含我们想用来搜索其他实体的值的实体。例如,如果我们有一个 titleSpring DataBook 实体,我们就可以创建一个 titleSpring 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() 方法查找 authorJohn 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() 方法查找 authorJohn Doe 的所有书籍。但这次,我们向该方法传递了一个 Pageable 对象,按 title 降序排序,并只返回前两个结果。

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

4.3、其他排序接口

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

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

5、总结

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


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