Skip to content

Repository

Repository 是 Spring Data JPA 的数据访问入口。你定义接口,Spring 在运行时生成代理对象,负责把方法调用转换成 JPA 操作和 SQL。

本页讲 Repository 的基本用法和职责边界。完整的代理创建、方法解析、SimpleJpaRepositoryEntityManager 调用链见 Spring Data JPA 核心全过程原理

Repository 调用流程

mermaid
flowchart TD
    A["Service 调用 Repository 方法"] --> B["Spring 代理拦截"]
    B --> C{"是否是内置 CRUD"}
    C -- "是" --> D["调用 EntityManager"]
    C -- "否" --> E{"是否方法名查询"}
    E -- "是" --> F["解析方法名生成查询"]
    E -- "否" --> G["@Query 或自定义实现"]
    F --> D
    G --> D
    D --> H["Hibernate 生成 SQL"]
    H --> I["数据库执行"]

常见父接口

接口能力
Repository标记接口,几乎不提供方法
CrudRepository基础增删改查
PagingAndSortingRepository分页和排序
JpaRepositoryJPA 常用增强方法,最常用

推荐常规业务直接继承 JpaRepository

基本用法

java
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);

    List<User> findByStatus(Integer status);

    Page<User> findByStatus(Integer status, Pageable pageable);
}

常见返回类型:

返回类型适合场景
Optional<T>按唯一条件查询一条
List<T>小结果集列表
Page<T>需要总数的分页
Slice<T>只需要是否有下一页
boolean判断是否存在
long统计数量

方法名查询

java
Optional<User> findByUsernameAndStatus(String username, Integer status);

List<User> findByStatusOrderByCreatedAtDesc(Integer status);

boolean existsByUsername(String username);

long countByStatus(Integer status);

方法名查询适合简单条件。超过三四个条件后,方法名会变得很长,建议改用 @Query 或 Specification。

Repository 职责边界

Repository 只做数据访问,不写业务规则。

java
// 推荐:Repository 只表达查询
Optional<User> findByUsername(String username);

// 不推荐:方法名包含业务流程
void checkUserCanLoginAndUpdateLastLoginTime(...);

业务判断应该放到 Service:

java
@Service
public class UserService {
    public void login(String username) {
        User user = userRepository.findByUsername(username)
            .orElseThrow(() -> new BizException("用户不存在"));
        // 业务校验
    }
}

删除和更新

删除:

java
void deleteById(Long id);

自定义更新需要事务和 @Modifying

java
@Modifying
@Query("update User u set u.status = :status where u.id = :id")
int updateStatus(Long id, Integer status);

调用更新方法的 Service 要加事务:

java
@Transactional
public void disableUser(Long id) {
    userRepository.updateStatus(id, 0);
}

常见问题

问题原因建议
方法名太长条件太多使用 @Query 或 Specification
返回 List 数据过大没分页列表接口使用 Pageable
更新语句不生效缺少 @Modifying 或事务补注解和事务
Optional 被直接 get空值报错使用 orElseThrow
Repository 写业务职责混乱业务放 Service

练习

  1. 定义一个 UserRepository extends JpaRepository<User, Long>
  2. findByUsername,返回 Optional<User>
  3. 写一个按状态分页查询的方法。
  4. 写一个 existsByUsername 判断用户名是否存在。
  5. 使用 @Modifying 写一个更新用户状态的方法。

小结

Repository 是数据访问层,不是业务层。它负责把接口方法转换成查询或更新。简单查询可以用方法名,复杂查询要及时切换到 @Query、Specification 或自定义实现。

继续深入:Spring Data JPA 核心全过程原理

为什么 Repository 不写业务逻辑

Repository 的原理是把接口方法映射为数据访问操作,它应该只表达“怎么查、怎么改数据”,不应该决定业务流程。

mermaid
flowchart TD
    A["Service 业务决策"] --> B["Repository 数据访问"]
    B --> C["JPA 生成 SQL"]
    C --> D["数据库"]

Demo:Repository 边界

java
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByUsername(String username);

    boolean existsByUsername(String username);
}

用户名是否允许注册、是否发验证码、是否写审计日志,应该放在 Service,不应该塞进 Repository。