Spring Data JPA 从入门到实战完整指南

Spring Data JPA 从入门到实战完整指南

本文档基于 spring-boot-demo 项目,帮助理解 Repository、Entity、Service 三层架构的协作关系。


📚 目录

  1. 核心概念:一句话理解

  2. ORM:对象与数据库的桥梁

  3. Entity:数据库表在 Java 中的投影

  4. Repository:不写 SQL 的数据库操作

  5. Service:业务逻辑的组织者

  6. 完整调用链:从 Controller 到数据库

  7. 如何设计 Repository 方法

  8. 常见问题与调试技巧


1. 核心概念:一句话理解

Spring Data JPA

ini 复制代码
Spring Data JPA = 我们不用写 SQL,就能操作数据库的工具

对比传统 JDBC 方式:

java 复制代码
// ❌ 传统 JDBC:要写这么多代码
String sql = "SELECT * FROM t_order WHERE order_no = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, orderNo);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
    Order order = new Order();
    order.setId(rs.getLong("id"));
    order.setOrderNo(rs.getString("order_no"));
    // ... 还要一个个字段赋值
}

// ✅ Spring Data JPA:就这一行!
Optional<Order> order = orderRepository.findByOrderNo("ORD123");

2. ORM:对象与数据库的桥梁

什么是 ORM?

ini 复制代码
ORM = 让 Java 对象和数据库表之间可以互相转换的桥梁

映射关系图

sql 复制代码
┌─────────────────────────────────────────────────────────────────┐
│   Java 对象(内存中)              ↔        数据库表(硬盘上)    │
├─────────────────────────────────────────────────────────────────┤
│   Order 类                                 ↔  t_order 表        │
│   ├── id: Long                             ↔  id BIGINT         │
│   ├── orderNo: String                      ↔  order_no VARCHAR  │
│   ├── amount: BigDecimal                   ↔  amount DECIMAL    │
│   ├── status: OrderStatus                  ↔  status VARCHAR    │
│   ├── createTime: LocalDateTime            ↔  create_time       │
│   └── userId: Long                         ↔  user_id BIGINT    │
├─────────────────────────────────────────────────────────────────┤
│   Order 对象实例                            ↔  表中的一行记录    │
│   new Order("ORD123", 99.5)                ↔  |1|ORD123|99.5|...│
└─────────────────────────────────────────────────────────────────┘

双向转换过程

方向一:Java → 数据库(写入)

java 复制代码
Order order = new Order();
order.setOrderNo("ORD123");
order.setAmount(new BigDecimal("99.50"));

orderRepository.save(order);

// ORM 自动执行:
// INSERT INTO t_order (order_no, amount) VALUES ('ORD123', 99.50)

方向二:数据库 → Java(读取)

java 复制代码
Order order = orderRepository.findByOrderNo("ORD123").get();

// ORM 自动执行:
// SELECT * FROM t_order WHERE order_no = 'ORD123'
// 并将结果转换为 Order 对象

String no = order.getOrderNo();   // 直接拿到 "ORD123"

3. Entity:数据库表在 Java 中的投影

项目中的 Order 实体类

java 复制代码
@Entity                          // ← 告诉 JPA:这个类对应一张表
@Table(name = "t_order")         // ← 指定表名(不写则默认是 order)
public class Order {

    @Id                         // ← 主键
    @GeneratedValue(strategy = GenerationType.IDENTITY)  // ← 自增策略
    private Long id;            // → id BIGINT AUTO_INCREMENT PRIMARY KEY

    @Column(name = "order_no", unique = true, nullable = false, length = 32)
    private String orderNo;     // → order_no VARCHAR(32) UNIQUE NOT NULL

    @Column(nullable = false, precision = 10, scale = 2)
    private BigDecimal amount;  // → amount DECIMAL(10,2) NOT NULL

    @Enumerated(EnumType.STRING) // ← 枚举存为字符串(推荐)
    @Column(nullable = false, length = 20)
    private OrderStatus status; // → status VARCHAR(20) NOT NULL

    @Column(name = "user_id")
    private Long userId;        // → user_id BIGINT

    @Column(name = "create_time", updatable = false)
    private LocalDateTime createTime; // → create_time DATETIME

    @Column(name = "update_time")
    private LocalDateTime updateTime; // → update_time DATETIME
}

注解对照表

注解 作用 对应数据库
@Entity 标记这是数据库表对应的类 一张表
@Table(name = "...") 指定表名 表名
@Id 标记主键 主键列
@GeneratedValue 主键生成策略 AUTO_INCREMENT
@Column 指定列属性 列名、类型、约束
@Enumerated(STRING) 枚举存储方式 存字符串

自动生成的数据库表结构

sql 复制代码
CREATE TABLE t_order (
    id          BIGINT AUTO_INCREMENT PRIMARY KEY,
    order_no    VARCHAR(32) UNIQUE NOT NULL,
    amount      DECIMAL(10,2) NOT NULL,
    status      VARCHAR(20) NOT NULL,
    user_id     BIGINT,
    create_time DATETIME,
    update_time DATETIME
);

4. Repository:不写 SQL 的数据库操作

项目中的 OrderRepository 接口

java 复制代码
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {

    // ========== 自定义查询方法 ==========

    /**
     * 根据订单号查询订单
     * 自动生成:SELECT * FROM t_order WHERE order_no = ?
     */
    Optional<Order> findByOrderNo(String orderNo);

    /**
     * 查询指定状态且创建时间早于某时间的订单
     * 用于查询超时未支付的订单
     * 自动生成:SELECT * FROM t_order WHERE status = ? AND create_time < ?
     */
    List<Order> findByStatusAndCreateTimeBefore(OrderStatus status, LocalDateTime createTime);

    /**
     * 查询指定状态且发货时间早于某时间的订单
     * 用于查询需要自动确认收货的订单
     * 自动生成:SELECT * FROM t_order WHERE status = ? AND delivery_time < ?
     */
    List<Order> findByStatusAndDeliveryTimeBefore(OrderStatus status, LocalDateTime deliveryTime);

    /**
     * 查询所有订单,按创建时间倒序
     * 自动生成:SELECT * FROM t_order ORDER BY create_time DESC
     */
    List<Order> findAllByOrderByCreateTimeDesc();
}

方法命名规则解析

Spring Data JPA 通过解析方法名自动生成 SQL:

方法名组成部分 含义 示例
findBy 查询操作 findByOrderNo
findAllBy 查询返回列表 findAllByStatus
countBy 统计数量 countByStatus
existsBy 是否存在 existsByOrderNo
deleteBy 删除操作 deleteByStatus

条件关键词对照表

关键词 生成的 SQL 条件 示例
And AND findByStatusAndCreateTimeBefore
Or OR findByStatusOrOrderNo
Before <(时间比较) findByCreateTimeBefore
After >(时间比较) findByCreateTimeAfter
Between BETWEEN findByPriceBetween
LessThan <(数值比较) findByAmountLessThan
GreaterThan >(数值比较) findByAmountGreaterThan
Like LIKE findByOrderNoLike
Containing LIKE %xxx% findByOrderNoContaining
StartingWith LIKE xxx% findByOrderNoStartingWith
EndingWith LIKE %xxx findByOrderNoEndingWith
In IN (...) findByStatusIn(List)
NotIn NOT IN (...) findByStatusNotIn(List)
IsNull IS NULL findByRemarkIsNull
IsNotNull IS NOT NULL findByRemarkIsNotNull
OrderBy ORDER BY findAllByOrderByCreateTimeDesc

JpaRepository 自带的方法

继承 JpaRepository<Order, Long> 后,自动拥有以下方法:

java 复制代码
// ========== 保存/更新 ==========
orderRepository.save(order);           // INSERT 或 UPDATE
orderRepository.saveAll(orders);       // 批量保存

// ========== 查询单个 ==========
orderRepository.findById(1L);          // 根据 ID 查询,返回 Optional
orderRepository.getById(1L);           // 根据 ID 查询,不存在则抛异常

// ========== 查询列表 ==========
orderRepository.findAll();             // 查询所有
orderRepository.findAllById(ids);      // 根据 ID 列表查询

// ========== 删除 ==========
orderRepository.delete(order);         // 删除对象
orderRepository.deleteById(1L);        // 根据 ID 删除
orderRepository.deleteAll();           // 删除所有

// ========== 统计 ==========
orderRepository.count();               // 总数
orderRepository.existsById(1L);        // 是否存在

5. Service:业务逻辑的组织者

Service 层的作用

css 复制代码
Service 层 = 业务逻辑的载体
├── 调用 Repository 来读写数据库
├── 编写业务逻辑(状态校验、金额计算、超时判断等)
├── 管理事务(@Transactional)
└── 给 Controller 提供干净的接口

项目中的 OrderService 示例

java 复制代码
@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    // ========== 创建订单 ==========

    @Transactional
    public Order createOrder(Long userId, BigDecimal amount, String remark) {
        // 1. 构建订单对象
        Order order = new Order();
        order.setOrderNo(generateOrderNo());
        order.setUserId(userId);
        order.setAmount(amount);
        order.setStatus(OrderStatus.CREATED);
        order.setCreateTime(LocalDateTime.now());

        // 2. 调用 Repository 保存到数据库
        // 执行:INSERT INTO t_order (...) VALUES (...)
        return orderRepository.save(order);
    }

    // ========== 查询订单 ==========

    public Order getByOrderNo(String orderNo) {
        // 调用 Repository 的 findByOrderNo 方法
        // 执行:SELECT * FROM t_order WHERE order_no = ?
        return orderRepository.findByOrderNo(orderNo)
            .orElseThrow(() -> new RuntimeException("订单不存在:" + orderNo));
    }

    // ========== 支付订单 ==========

    @Transactional
    public Order payOrder(String orderNo) {
        // 1. 查询订单(调用 Repository)
        Order order = getByOrderNo(orderNo);

        // 2. 业务逻辑:校验状态
        if (order.getStatus() != OrderStatus.CREATED) {
            throw new RuntimeException("订单状态不正确,无法支付");
        }

        // 3. 业务逻辑:校验是否超时
        if (order.getCreateTime().plusMinutes(30).isBefore(LocalDateTime.now())) {
            order.setStatus(OrderStatus.CANCELLED);
            orderRepository.save(order); // 更新状态
            throw new RuntimeException("订单已超时,已自动取消");
        }

        // 4. 调用支付网关(业务逻辑)
        boolean paySuccess = mockPayGateway(order);

        // 5. 更新订单状态(调用 Repository)
        // 执行:UPDATE t_order SET status = 'PAID', pay_time = ? WHERE id = ?
        order.setStatus(OrderStatus.PAID);
        order.setPayTime(LocalDateTime.now());
        return orderRepository.save(order);
    }

    // ========== 定时任务:取消超时订单 ==========

    @Transactional
    public int cancelTimeoutOrders(int minutes) {
        LocalDateTime deadline = LocalDateTime.now().minusMinutes(minutes);

        // 调用 Repository 的自定义查询方法
        // 执行:SELECT * FROM t_order WHERE status = 'CREATED' AND create_time < ?
        List<Order> timeoutOrders = orderRepository.findByStatusAndCreateTimeBefore(
            OrderStatus.CREATED, deadline
        );

        // 业务逻辑:批量更新状态
        for (Order order : timeoutOrders) {
            order.setStatus(OrderStatus.CANCELLED);
            orderRepository.save(order);
        }

        return timeoutOrders.size();
    }
}

Repository 在 Service 中的调用位置

Repository 方法 在 Service 中被调用 作用
save(order) createOrder, payOrder, cancelOrder 插入或更新
findByOrderNo(orderNo) getByOrderNo(被所有方法间接调用) 查询单个订单
findAllByOrderByCreateTimeDesc() getAllOrders 查询全部订单
findByStatusAndCreateTimeBefore(...) cancelTimeoutOrders 查询超时订单
findByStatusAndDeliveryTimeBefore(...) autoConfirmReceive 查询待确认订单

6. 完整调用链:从 Controller 到数据库

三层架构调用流程图

scss 复制代码
┌──────────────────────────────────────────────────────────────────┐
│                         用户请求                                  │
│                    POST /api/orders/create                        │
└──────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────┐
│                      Controller 层                                │
│                  OrderController.createOrder()                    │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ @PostMapping("/create")                                    │  │
│  │ public ResponseEntity<Order> createOrder(...) {            │  │
│  │     Order order = orderService.createOrder(userId, amount);│  │
│  │     return ResponseEntity.ok(order);                       │  │
│  │ }                                                          │  │
│  └────────────────────────────────────────────────────────────┘  │
│  作用:接收 HTTP 请求,调用 Service,返回响应                     │
└──────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────┐
│                       Service 层                                  │
│                   OrderService.createOrder()                      │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ @Transactional                                             │  │
│  │ public Order createOrder(...) {                            │  │
│  │     Order order = new Order();                             │  │
│  │     order.setOrderNo(...);                                 │  │
│  │     return orderRepository.save(order);                    │  │
│  │ }                                                          │  │
│  └────────────────────────────────────────────────────────────┘  │
│  作用:业务逻辑、事务管理、调用 Repository                       │
└──────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────┐
│                      Repository 层                                │
│                   OrderRepository.save()                          │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ public interface OrderRepository extends JpaRepository {   │  │
│  │     // Spring 自动生成实现类                                │  │
│  │ }                                                          │  │
│  └────────────────────────────────────────────────────────────┘  │
│  作用:数据访问接口,Spring 自动生成实现                         │
└──────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────┐
│                    JPA/Hibernate 层                               │
│                  ORM 框架执行转换                                 │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ 1. 解析 Entity 类的注解                                     │  │
│  │ 2. 将 Order 对象转换为 SQL                                  │  │
│  │ 3. INSERT INTO t_order (...) VALUES (...)                  │  │
│  └────────────────────────────────────────────────────────────┘  │
│  作用:对象关系映射,生成 SQL                                     │
└──────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────┐
│                       数据库层                                    │
│                         MySQL                                     │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │ 执行 SQL 语句                                               │  │
│  │ 将数据写入 t_order 表                                       │  │
│  └────────────────────────────────────────────────────────────┘  │
│  作用:数据持久化存储                                             │
└──────────────────────────────────────────────────────────────────┘
                              ↓
┌──────────────────────────────────────────────────────────────────┐
│                         返回结果                                  │
│  数据库 → Hibernate 转换为 Order 对象 → Service → Controller     │
│                          → 用户                                   │
└──────────────────────────────────────────────────────────────────┘

完整代码示例

java 复制代码
// ========== Controller 层 ==========
@RestController
@RequestMapping("/api/orders")
public class OrderController {

    @Autowired
    private OrderService orderService;

    @PostMapping("/create")
    public ResponseEntity<Order> createOrder(@RequestParam Long userId,
                                             @RequestParam BigDecimal amount) {
        Order order = orderService.createOrder(userId, amount, "");
        return ResponseEntity.ok(order);
    }

    @GetMapping("/{orderNo}")
    public ResponseEntity<Order> getOrder(@PathVariable String orderNo) {
        Order order = orderService.getByOrderNo(orderNo);
        return ResponseEntity.ok(order);
    }
}

// ========== Service 层 ==========
@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public Order createOrder(Long userId, BigDecimal amount, String remark) {
        Order order = new Order();
        order.setOrderNo(generateOrderNo());
        order.setUserId(userId);
        order.setAmount(amount);
        order.setStatus(OrderStatus.CREATED);
        return orderRepository.save(order);
    }

    public Order getByOrderNo(String orderNo) {
        return orderRepository.findByOrderNo(orderNo)
            .orElseThrow(() -> new RuntimeException("订单不存在"));
    }
}

// ========== Repository 层 ==========
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
    Optional<Order> findByOrderNo(String orderNo);
}

// ========== Entity 层 ==========
@Entity
@Table(name = "t_order")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "order_no", unique = true)
    private String orderNo;

    private BigDecimal amount;
    private OrderStatus status;
    private Long userId;
    private LocalDateTime createTime;
}

7. 如何设计 Repository 方法

根据业务需求倒推

业务场景 需要的方法 设计思路
用户根据订单号查询订单 findByOrderNo(String orderNo) 单字段查询,返回 Optional
用户查询自己的所有订单 findByUserIdOrderByCreateTimeDesc(Long userId) 按用户查询 + 排序
定时任务查询超时未支付订单 findByStatusAndCreateTimeBefore(...) 多条件查询
管理后台查询某状态的订单 findByStatus(OrderStatus status) 单条件查询,返回 List
查询订单是否存在 existsByOrderNo(String orderNo) 返回 boolean
统计某状态的订单数量 countByStatus(OrderStatus status) 返回 long

设计步骤

vbnet 复制代码
第一步:明确业务需求
├── 用户要做什么操作?
├── 需要查询哪些字段?
├── 需要什么条件?
└── 返回单个对象还是列表?

第二步:按照命名规则写方法名
├── findBy + 字段名 + 条件关键词
├── 注意:字段名要和 Entity 类中的属性名一致
└── 时间比较用 Before/After,数值比较用 LessThan/GreaterThan

第三步:确定返回类型
├── 单个对象 → Optional<Order>
├── 列表 → List<Order>
├── 是否存在 → boolean
└── 统计数量 → long

示例:设计一个新方法

需求:查询某个用户的、已支付状态的、金额大于100元的订单

java 复制代码
// 第一步:确定条件
// - userId = ?
// - status = PAID
// - amount > 100

// 第二步:按规则拼接
List<Order> findByUserIdAndStatusAndAmountGreaterThan(
    Long userId,
    OrderStatus status,
    BigDecimal amount
);

// 第三步:使用
List<Order> orders = orderRepository.findByUserIdAndStatusAndAmountGreaterThan(
    1L,
    OrderStatus.PAID,
    new BigDecimal("100")
);
// 执行:SELECT * FROM t_order WHERE user_id = 1 AND status = 'PAID' AND amount > 100

8. 常见问题与调试技巧

Q1:方法名写错了,启动报错怎么办?

scss 复制代码
错误示例:findByOrderNumber(String orderNo)
原因:Entity 类中字段名是 orderNo,不是 orderNumber

解决:改为 findByOrderNo(String orderNo)

Q2:如何查看实际执行的 SQL?

application.yml 中配置:

yaml 复制代码
spring:
  jpa:
    show-sql: true           # 打印 SQL
    properties:
      hibernate:
        format_sql: true     # 格式化 SQL
logging:
  level:
    org.hibernate.SQL: DEBUG
    org.hibernate.type.descriptor.sql.BasicBinder: TRACE  # 显示参数绑定

Q3:复杂查询用方法名不好表达怎么办?

使用 @Query 自定义 SQL:

java 复制代码
@Query("SELECT o FROM Order o WHERE o.userId = :userId AND o.amount BETWEEN :min AND :max")
List<Order> findByUserIdAndAmountRange(@Param("userId") Long userId,
                                        @Param("min") BigDecimal min,
                                        @Param("max") BigDecimal max);

@Query(value = "SELECT * FROM t_order WHERE status = ? AND amount > ?", nativeQuery = true)
List<Order> findCustomByNativeQuery(OrderStatus status, BigDecimal amount);

Q4:IDE 中如何快速找到 Repository 的实现?

重要:Repository 接口没有实现类,Spring 在启动时自动生成代理类。

markdown 复制代码
在 IDEA 中:
1. 按住 Ctrl 点击 orderRepository.save()
2. 会跳转到 JpaRepository 接口定义
3. 实现类是 Spring 运行时生成的,源码中看不到

调试技巧:
1. 在 Service 方法中打断点
2. 查看变量窗口中的 orderRepository 对象
3. 类型是:$Proxy123 或 SimpleJpaRepository

Q5:为什么有时要用 Optional?

java 复制代码
Optional<Order> findByOrderNo(String orderNo);

// 好处:强制处理"找不到"的情况
Order order = orderRepository.findByOrderNo(orderNo)
    .orElseThrow(() -> new RuntimeException("订单不存在"));

// 或者返回默认值
Order order = orderRepository.findByOrderNo(orderNo)
    .orElse(new Order());

// 或者直接判断是否存在
if (orderRepository.findByOrderNo(orderNo).isPresent()) {
    // 存在
}

📌 快速记忆口诀

Entity 注解

less 复制代码
@Entity 标记表
@Table 指定名
@Id 是主键
@GeneratedValue 自增
@Column 定列名
@Enumerated 存枚举

Repository 方法命名

复制代码
findBy 查询
countBy 统计
existsBy 判断存在
deleteBy 删除

And/Or 连接条件
Before/After 时间比较
LessThan/GreaterThan 数值比较
Containing 模糊匹配
OrderBy 排序

三层架构

复制代码
Controller 接请求
Service 做业务
Repository 存数据
Entity 定结构

📝 练习建议

  1. 打开项目,找到 OrderRepository.java
  2. 尝试添加一个新的查询方法(如:按用户ID查询)
  3. OrderService.java 中调用这个新方法
  4. 配置 show-sql: true,观察生成的 SQL
  5. 写一个单元测试验证功能

最后总结

Spring Data JPA 是一个"翻译官":

  • 把 Java 对象给它
  • 它自动翻译成 SQL 语句
  • 然后去数据库执行
  • 最后把结果再翻译回 Java 对象返回

Repository 接口只需要定义,Spring 会自动实现。 Entity 类用注解告诉 ORM 怎么映射到数据库表。 Service 类串联业务逻辑,调用 Repository 操作数据。

相关推荐
我是大卫1 小时前
React源码解析-第一部分:宏观架构
前端·react.js·源码
我是大卫2 小时前
React源码解析-第二部分:更新与调度
前端·react.js·源码
狸礼li2 小时前
web网站设计作品--第二大脑
前端
2603_955279702 小时前
C++23新特性在CLion中的实战体验
前端·javascript·c++23
梨子同志2 小时前
HTTP
前端
kidding7232 小时前
旋转大转盘小程序
前端·css·微信小程序·小程序·前端框架
道友可好2 小时前
Claude Code 泄露源码里的 89 个秘密
前端·人工智能·后端
Tian_Hang2 小时前
Eclipse Ditto 的权限策略
java·服务器·前端·网络·ide·ubuntu·eclipse
lichenyang4533 小时前
从 0 新增一个 `has.echo`:我如何理解小程序容器里的 API 调用链路
前端