1. 概述
本文将快速梳理 Spring Data 中不同仓库接口的功能差异,重点分析:
- CrudRepository
- PagingAndSortingRepository
- JpaRepository
简单来说,Spring Data 中的所有仓库都继承自通用的 Repository 接口,但每个子接口都提供了独特的功能扩展。
2. Spring Data 仓库体系
先看核心接口的继承关系:JpaRepository 继承自 PagingAndSortingRepository,而后者又继承自 CrudRepository。
每个接口的核心职责如下:
- CrudRepository:提供基础的 CRUD 操作
- PagingAndSortingRepository:增加分页和排序功能
- JpaRepository:提供 JPA 特有方法(如持久化上下文刷新、批量删除等)
关键点:由于继承关系,JpaRepository 实际包含了 CrudRepository 和 PagingAndSortingRepository 的全部功能。
当不需要完整功能时,直接使用 CrudRepository 是更轻量的选择。
下面通过一个简单示例加深理解。先定义实体类:
@Entity
public class Product {
@Id
private long id;
private String name;
// getters and setters
}
实现按名称查询的仓库接口:
@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
Product findByName(String productName);
}
就这么简单!Spring Data 会根据方法名自动生成实现。当然这只是基础用法,更深入的 Spring Data JPA 知识可以参考这里。
3. CrudRepository 接口详解
查看 CrudRepository 的源码:
public interface CrudRepository<T, ID extends Serializable>
extends Repository<T, ID> {
<S extends T> S save(S entity);
T findOne(ID primaryKey);
Iterable<T> findAll();
Long count();
void delete(T entity);
boolean exists(ID primaryKey);
}
核心方法分析:
- ✅
save(...)
:保存实体(支持批量保存) - ✅
findOne(...)
:根据主键查询单个实体 - ✅
findAll()
:获取所有实体 - ✅
count()
:统计实体总数 - ✅
delete(...)
:删除指定实体 - ✅
exists(...)
:判断主键对应的实体是否存在
这个接口看似简单,但已覆盖应用层所需的基础查询抽象。
4. PagingAndSortingRepository 接口详解
该接口继承 CrudRepository 并新增分页排序功能:
public interface PagingAndSortingRepository<T, ID extends Serializable>
extends CrudRepository<T, ID> {
Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);
}
核心方法 findAll(Pageable pageable)
是实现分页的关键。
使用 Pageable 需要指定三个基本参数:
- 页面大小
- 当前页码(从0开始)
- 排序规则
示例:获取按 lastName 升序排序的第一页数据(每页5条):
Sort sort = new Sort(new Sort.Order(Direction.ASC, "lastName"));
Pageable pageable = new PageRequest(0, 5, sort);
将 Pageable 对象传入查询方法即可获得分页结果(注意页码从0开始)。
5. JpaRepository 接口详解
最后看 JpaRepository 的核心方法:
public interface JpaRepository<T, ID extends Serializable> extends
PagingAndSortingRepository<T, ID> {
List<T> findAll();
List<T> findAll(Sort sort);
List<T> save(Iterable<? extends T> entities);
void flush();
T saveAndFlush(T entity);
void deleteInBatch(Iterable<T> entities);
}
关键方法解析:
- ✅
findAll()
:返回所有实体的 List(非 Iterable) - ✅
findAll(...)
:带排序条件的查询 - ✅
save(...)
:批量保存实体 - ✅
flush()
:强制将持久化上下文同步到数据库 - ✅
saveAndFlush(...)
:保存后立即刷新 - ✅
deleteInBatch(...)
:批量删除实体
⚠️ 注意:该接口继承了 PagingAndSortingRepository,因此也包含 CrudRepository 的所有方法。
6. Spring Data 3 中的仓库接口
在 Spring Data 3 中,仓库接口的内部实现有所优化:
- 新增基于 List 的 CRUD 仓库接口
- 调整了部分仓库类的继承结构
具体细节可参考我们的Spring Data 3 新版 CRUD 仓库接口文章。
7. Spring Data 仓库的潜在问题
尽管这些仓库接口非常实用,但直接依赖它们也存在一些隐患:
- 耦合风险:代码会与特定库的抽象(如
Page
/Pageable
)强绑定,需注意避免暴露这些实现细节 - 方法暴露过度:继承 CrudRepository 会暴露完整的持久化方法集。某些场景下可能需要更细粒度的控制,例如创建不包含
save(...)
和delete(...)
的只读仓库
8. 总结
本文梳理了 Spring Data JPA 仓库接口的核心差异和特性。想了解更多细节,可参考我们的Spring 持久化系列教程。