Repository
Repository 是 Spring Data JPA 的数据访问入口。你定义接口,Spring 在运行时生成代理对象,负责把方法调用转换成 JPA 操作和 SQL。
本页讲 Repository 的基本用法和职责边界。完整的代理创建、方法解析、SimpleJpaRepository、EntityManager 调用链见 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 | 分页和排序 |
JpaRepository | JPA 常用增强方法,最常用 |
推荐常规业务直接继承 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 |
练习
- 定义一个
UserRepository extends JpaRepository<User, Long>。 - 写
findByUsername,返回Optional<User>。 - 写一个按状态分页查询的方法。
- 写一个
existsByUsername判断用户名是否存在。 - 使用
@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。
