1. 概述

本文将快速梳理 Spring Data 中不同仓库接口的功能差异,重点分析:

  • CrudRepository
  • PagingAndSortingRepository
  • JpaRepository

简单来说,Spring Data 中的所有仓库都继承自通用的 Repository 接口,但每个子接口都提供了独特的功能扩展。

2. Spring Data 仓库体系

先看核心接口的继承关系JpaRepository 继承自 PagingAndSortingRepository,而后者又继承自 CrudRepository

每个接口的核心职责如下:

  • CrudRepository:提供基础的 CRUD 操作
  • PagingAndSortingRepository:增加分页和排序功能
  • JpaRepository:提供 JPA 特有方法(如持久化上下文刷新、批量删除等)

关键点:由于继承关系,JpaRepository 实际包含了 CrudRepositoryPagingAndSortingRepository 的全部功能。

当不需要完整功能时,直接使用 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 需要指定三个基本参数:

  1. 页面大小
  2. 当前页码(从0开始)
  3. 排序规则

示例:获取按 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 仓库的潜在问题

尽管这些仓库接口非常实用,但直接依赖它们也存在一些隐患:

  1. 耦合风险:代码会与特定库的抽象(如 Page/Pageable)强绑定,需注意避免暴露这些实现细节
  2. 方法暴露过度:继承 CrudRepository 会暴露完整的持久化方法集。某些场景下可能需要更细粒度的控制,例如创建不包含 save(...)delete(...) 的只读仓库

8. 总结

本文梳理了 Spring Data JPA 仓库接口的核心差异和特性。想了解更多细节,可参考我们的Spring 持久化系列教程


原始标题:Spring Data Repositories compared | Baeldung