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 获取。