@Transaction

@Transactional = 把一段代码包进"要么全成功、要么全失败"的数据库事务里

事务

事务可以理解成一组数据库操作的保险箱

事务最关键的两个特性:

  • 原子性:要么都成功,要么都失败
  • 一致性:数据不会出现"写了一半"的怪状态

@Transactioal

java 复制代码
@Transactional
public void doSomething() {
    saveA();
    saveB();
}

加上这个注解后,Spring会在方法执行前后帮你做这件事:

复制代码
开启事务
  ↓
执行方法
  ↓
没异常 → 提交事务
有异常 → 回滚事务

完全不用手写 begin / commit / rollback

但JPA自带方法,如save(),delete()都是自带@Transactional的

如果在类上加@Transactional,则这个类里所有public方法默认都有事务

rollbackFor属性

用来指定:当抛出哪些"异常类型"时,事务也要回滚,因为Spring默认并不是"所有异常都会回滚"

默认为:

异常类型 是否回滚
RuntimeException ✅ 回滚
Error ✅ 回滚
受检异常(Exception) 不回滚

也就是说对于:

java 复制代码
@Transactional
public void test() throws Exception {
    repository.save(order);
    throw new Exception("出错了");
}

事务不会回滚

如果没有事务

java 复制代码
public void refundAndLog(RefundOrder order) {
    refundOrderRepository.save(order);   // 步骤1:保存退款订单
    logRepository.save(new Log("退款成功")); // 步骤2:写日志
}

如果没有@Transactional,save(order)会独立开一个事务提交,save(log)也会再开一个事务提交,如果第二步报错(如数据库断开,或唯一约束冲突)

就会产生结果:

refund_order 表里已经插入了一条数据 log 表没插入

数据库就处于 不一致状态

但如果加上@Transactional,Spring会在方法开始时开启事务,如果第二步报错,则事务回滚,refund_order的插入也被撤销,

推荐用法

  • Service层:凡是有"写数据库"的方法,加@Transactional
  • 查询可以不加,或者加@Transactional(readOnly = true)
  • Controller上不要加,防止事务时间过长,容易锁表

脏检查

脏检查 = Hibernate 在事务提交前,自动检查"被托管的实体对象有没有被改过",如果改过,就自动生成 UPDATE SQL

java 复制代码
@Service
@Transactional   // 注意:类上有事务
public class RefundOrderService {

    public RefundOrder getAndFormat(Long id) {
        RefundOrder order = repository.findById(id).orElse(null);

        // 你只是想格式化一下数据,给前端看
        order.setMoney(
            order.getMoney().setScale(2, RoundingMode.HALF_UP)
        );

        return order;
    }
}

这段代码看似

也就是说你不用写Update,它也会帮你更新

什么情况下会有脏检查

脏检查不是随时都有,需要满足三个条件

  1. 实体是"托管状态"
java 复制代码
`RefundOrder order = repository.findById(1L).get();`

此时:

  • order 被 Hibernate 托管

  • 放在 Session / Persistence Context 里

  1. 处在事务中(@Transactional)
java 复制代码
@Transactional
public void test() {
    ...
}
  1. 修改了实体属性
java 复制代码
order.setMoney(new BigDecimal("100"));

满足这三点时,脏检查一定会发生

脏检查是怎么工作的:

  1. 实体第一次加载时:
java 复制代码
RefundOrder order = repository.findById(1L).get();

Hibernate会:

  • 从数据库查数据
  • 保存一份"快照"
  1. 你在Java里改对象
java 复制代码
order.setMoney(100);
  1. 事务即将提交

Hibernate 在commit前:

  • 对比快照和当前对象
  • 发现不一样->脏了
  1. 自动生成update的SQL

Session:Hibernate的核心概念

Session是什么

Java对象和数据库之间的"中间人"

它负责:

  1. 持久化对象管理(Persistent Context)

    • 把你 new 出来的对象变成"托管对象"(managed entity)

    托管对象 = 正被Hibernate监控的对象,直到你有没有改它,什么时候该同步到数据库

  2. 缓存

    • Session 内部会缓存你查出来的实体,减少重复 SQL
  3. 事务同步

    • 事务提交时,自动把对象状态同步到数据库(自动 flush)
  4. 延迟加载(Lazy Loading)

    • 代理对象在访问时通过 Session 去数据库加载数据

和数据库的关系

  • Session不是数据库
  • 它是 Hibernate 管理对象和数据库连接的工具
  • Session 内部会拿 Connection 去操作数据库

当你在事务里操作JPA/Hibernate:

rust 复制代码
方法开始 -> Spring 开启事务 -> Hibernate 打开 Session -> 获取 JDBC Connection
  • Session 里保存实体的状态
  • flush / commit 时 → JDBC Connection 执行 SQL

为什么 Lazy Loading 需要 Session?

java 复制代码
RefundOrder order = repository.findById(1L).orElse(null);
System.out.println(order.getItems().size()); // 懒加载触发
  • items 是 LAZY
  • Hibernate 给你一个代理对象
  • 代理对象会去 Session 拿连接,发 SQL
  • 如果 Session 关闭(事务结束) → 连接没了 → 报 LazyInitializationException

Session生命周期

阶段 发生什么
开启事务 Session 开始,连接准备好
执行查询 Session 缓存实体,生成代理对象
访问 LAZY 属性 Session 用 Connection 发 SQL
事务提交/回滚 Session flush 或清空
方法返回后 Session 被关闭,延迟加载不再可用

总结

Session 是 Hibernate 管理实体对象、缓存和数据库连接的中间人,Lazy Loading 和事务依赖它,但它本身不是数据库

相关推荐
+VX:Fegn089531 分钟前
计算机毕业设计|基于springboot + vue在线音乐播放系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计
code bean1 小时前
Flask图片服务在不同网络接口下的路径解析问题及解决方案
后端·python·flask
+VX:Fegn08951 小时前
计算机毕业设计|基于springboot + vue律师咨询系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
努力的小郑1 小时前
2025年度总结:当我在 Cursor 里敲下 Tab 的那一刻,我知道时代变了
前端·后端·ai编程
颜淡慕潇3 小时前
深度解析官方 Spring Boot 稳定版本及 JDK 配套策略
java·后端·架构
Victor3563 小时前
Hibernate(28)Hibernate的级联操作是什么?
后端
Victor3563 小时前
Hibernate(27)Hibernate的查询策略是什么?
后端
superman超哥3 小时前
Rust 内部可变性模式:突破借用规则的受控机制
开发语言·后端·rust·rust内部可变性·借用规则·受控机制
柒.梧.4 小时前
Spring核心知识全解析:从入门实战到进阶
java·后端·spring
乌日尼乐4 小时前
【Java基础整理】Java字符串处理,String、StringBuffer、StringBuilder
java·后端