1. 概述

本文将深入探讨 Spring Data 3 引入的新仓库接口。Spring Data 3 推出了基于 List 的 CRUD 仓库接口,用于替代返回 Iterable 的传统接口。此外,分页和排序接口不再默认继承原始 CRUD 仓库,而是将选择权交给开发者。我们将分析这些接口与传统接口的差异,并演示实际用法。

2. 项目搭建

2.1. 依赖配置

首先添加必要依赖。我们使用 Spring Boot Starter Data JPA

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

同时配置数据库。本教程使用 H2 内存数据库

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

2.2. 实体类

创建 实体类 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 的 CRUD 仓库

Spring Data 3 引入了一套新的 CRUD 仓库接口,返回实体列表。 这些接口与传统接口类似,但返回 List 而非 Iterable。这让我们能直接使用 List 的高级方法(如 get()indexOf())。

3.1. 新增的 List 仓库接口

Spring Data 3 新增了三个核心接口:

ListCrudRepository
提供基础 CRUD 操作(如 save()delete()findById())。与传统接口的关键区别在于:返回值现在是 List,便于对结果集进行更灵活的操作。

ListQuerydslPredicateExecutor
提供执行 Querydsl 谓词查询的方法。Querydsl 是一个支持类型安全 SQL 查询的框架。通过该接口,我们能执行 Querydsl 查询并直接返回 List 结果。

ListQueryByExampleExecutor
提供基于示例实体查询的方法并返回 List。示例实体包含要查询的属性值。例如,创建一个 title 为 "Spring Data" 的 Book 实例,就能查询同名的书籍。

3.2. ListCrudRepository 实战

创建使用该接口的仓库:

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

该仓库继承了 ListCrudRepository,自动获得基础 CRUD 方法。 我们可以直接使用这些方法保存、删除和查询书籍。此外,我们自定义了 findBooksByAuthor() 方法按作者查询。

3.3. 使用 List 仓库接口

测试仓库的实际用法:

@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");
        assertEquals(3, books.size());
    }
}

我们创建并保存了三本书,然后通过 findBooksByAuthor() 查询作者 "John Doe" 的书籍。最后验证返回列表包含这三本书。

注意:我们直接调用了 size() 方法。如果使用传统 Iterable 接口,这是不可能的,因为 Iterable 没有 size() 方法。

4. 排序仓库接口

为支持新的 List 接口,Spring Data 3 对排序接口进行了重构。排序仓库不再继承旧的 CRUD 仓库。 取而代之的是,开发者可自由选择搭配新的 List 接口或传统 Iterable 接口。 下面我们看看最新排序接口的用法。

4.1. PagingAndSortingRepository 实战

创建结合分页排序的仓库:

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

该仓库同时继承了:

  • ListCrudRepository:提供基础 CRUD 方法
  • PagingAndSortingRepository:提供排序和分页功能

旧版 Spring Data 中,我们无需显式继承 CRUD 仓库。现在必须显式指定。我们自定义了 findBooksByAuthor() 方法,支持 Pageable 参数实现分页排序。

4.2. 使用排序仓库接口

测试分页排序功能:

@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);
        assertEquals(2, books.size());
        assertEquals(book3.getId(), books.get(0).getId());
        assertEquals(book2.getId(), books.get(1).getId());
    }
}

同样创建三本书,但这次查询时传入 Pageable 对象:

  • 按标题降序排序
  • 只返回前两条结果

验证返回列表包含两本书,且按标题降序排列("Spring Data 3" 在前,"Spring Data 2" 在后)。✅

4.3. 其他排序接口变更

PagingAndSortingRepository 外,以下接口也发生了类似变化:

⚠️ ReactiveSortingRepository
不再继承 ReactiveCrudRepository

⚠️ CoroutineSortingRepository
不再继承 CoroutineCrudRepository

⚠️ RxJavaSortingRepository
不再继承 RxJavaCrudRepository

5. 总结

本文详细介绍了 Spring Data 3 新增的 List 接口,包括:

  • 如何使用新接口操作数据库
  • 传统排序接口的适配变更

这些改进让开发者能更灵活地选择返回类型,避免传统 Iterable 接口的限制。实际项目中,建议优先使用新接口以获得更好的开发体验。

本文代码示例可在 GitHub 获取。


原始标题:New CRUD Repository Interfaces in Spring Data 3