蓝凌EKP产品:Hibernate 中 SessionFactory、Session 与事务的关系

在使用 Hibernate / Spring 的项目中,

很多人会混淆 Session、Connection、Transaction 的职责,

甚至误以为「Session 关闭就等于事务提交」。

本文从 底层原理 + 生命周期 的角度,彻底梳理三者的关系,并说明:
为什么 Session 必须关闭、事务必须提交。

一、核心结论先行(给没耐心的读者)

  • SessionFactory

    • 全局唯一

    • 线程安全

    • 用来创建 Session

  • Session

    • 不是数据库连接

    • 但会 持有 一个数据库连接

    • 有明确生命周期,必须关闭

  • Transaction

    • 决定数据是否真正写入数据库

    • 只有 commit() 才算完成事务

  • Session.close() ≠ Transaction.commit()

    • Session 关闭 ≈ 连接释放

    • 未提交事务会被 回滚

二、SessionFactory 是什么?为什么它是单例

1️⃣ 定义

SessionFactory 是 Hibernate 的核心入口对象:

复制代码
SessionFactory sessionFactory;

它的职责包括:

  • 读取 Hibernate 配置

  • 管理二级缓存

  • 创建 Session

  • 管理 JDBC 资源策略

2️⃣ 特点

特性 说明
是否线程安全 ✅ 是
是否昂贵 ✅ 创建成本高
生命周期 应用级(启动 → 关闭)

👉 一个数据库通常只需要一个 SessionFactory

三、Session 是什么?它和 Connection 的真实关系

1️⃣ Session 的本质

Session 是 Hibernate 的"工作单元(Unit of Work)"

它负责:

  • 实体的持久化状态管理

  • 一级缓存

  • 脏数据检查(Dirty Checking)

  • 生成 SQL

    Session session = sessionFactory.openSession();

2️⃣ Session ≠ Connection(但会持有 Connection)

关系可以理解为:

复制代码
Session
   ↓
Connection(来自连接池)

关键点:

  • 创建 Session 时不一定占用连接

  • 第一次执行 SQL 时才从连接池获取 Connection

  • 在默认配置下:

    • 一个 Session 通常会绑定一个 Connection

四、Transaction 是什么?它才是"是否入库"的裁判

1️⃣ Transaction 的职责

Transaction 管理的是:

  • 事务边界

  • 提交(commit)

  • 回滚(rollback)

    Transaction tx = session.beginTransaction();

2️⃣ flush ≠ commit(这是常见误区)

操作 含义
flush SQL 发送到数据库
commit SQL 真正生效

没有 commit,数据不会持久化。

五、三者的完整关系图(强烈建议收藏)

复制代码
┌─────────────────────┐
│   SessionFactory    │  (应用级单例)
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│       Session       │  (一次会话)
│  一级缓存 / 状态管理 │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│     Connection      │  (数据库连接)
│   autoCommit=false  │
└─────────┬───────────┘
          │
          ▼
┌─────────────────────┐
│    Transaction      │
│ commit / rollback   │
└─────────────────────┘

调用顺序必须是:

复制代码
commit / rollback
→ close Session
→ 连接归还连接池

六、Session 的几种关闭方式(非常重要)

1️⃣ 手动管理 Session(非 Web / Worker 场景)

复制代码
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
try {
    ...
    tx.commit();
} catch (Exception e) {
    tx.rollback();
    throw e;
} finally {
    session.close(); // 必须
}

推荐方式

2️⃣ Spring + @Transactional(推荐)

复制代码
@Transactional
public void save() {
    sessionFactory.getCurrentSession().save(entity);
}
  • Session 由 Spring 管理

  • Transaction 自动提交 / 回滚

  • 不允许手动 close Session

3️⃣ OpenSessionInViewFilter(Web 场景)

复制代码
请求开始
  打开 Session
  Service 层提交事务
请求结束
  关闭 Session

七、为什么一定要关闭 Session?(这是重点)

1️⃣ 不关闭 Session 的直接后果

  • 数据库连接不释放

  • 一级缓存无限增长

  • 数据库锁无法释放

  • 连接池耗尽

👉 工程上表现为"连接泄露"

2️⃣ 事务未提交 + Session 未关闭 = 生产事故

复制代码
conn.setAutoCommit(false);
// 执行 SQL
// 没 commit
// 没 close

结果:

  • 数据回滚

  • 连接长期占用

  • 系统逐渐卡死

3️⃣ 为什么 close 不能代替 commit?

因为 JDBC 规定:

Connection.close() 时,如果事务未提交 → 自动回滚

也就是说:

复制代码
close ≈ 清理
commit ≈ 确认

清理永远不会等于确认。

八、最佳实践总结(可作为团队规范)

  1. SessionFactory

    • 单例

    • 应用启动时创建

  2. Session

    • 有明确边界

    • 用完必须关闭

  3. Transaction

    • 必须显式提交或回滚

    • 不依赖 close

  4. Spring 项目

    • @Transactional

    • 不手动操作事务和 Session

  5. 非 Web 线程

    • 不依赖 OSIV

    • 手动管理 Session 生命周期

九、结语

Hibernate 的问题,
80% 不是 API 用错,
而是生命周期理解错。

一旦你真正搞清楚:

  • SessionFactory 的"重"

  • Session 的"短"

  • Transaction 的"严"

你会发现:

  • 事务问题少了

  • 连接泄露没了

  • 系统稳定性直接上一个台阶

相关推荐
要站在顶端5 小时前
Jenkins PR编号提取&环境变量赋值问题总结
java·servlet·jenkins
透明的玻璃杯5 小时前
sqlite数据库连接池
jvm·数据库·sqlite
TT哇5 小时前
【每日八股】面经常考
java·面试
何中应5 小时前
【面试题-4】JVM
java·jvm·后端·面试题
老毛肚5 小时前
黑马头条-再回首
java
专注于大数据技术栈5 小时前
java学习--8个包装类
java·学习
Lyinj5 小时前
从一个编辑校验问题谈接口设计的边界
java·spring boot·python·学习
消失的旧时光-19435 小时前
Java 线程通信:彻底理解 wait / notify(原理 + 图解 + 实战)
java·开发语言
VX:Fegn08955 小时前
计算机毕业设计|基于springboot + vue非遗传承文化管理系统(源码+数据库+文档)
数据库·vue.js·spring boot·后端·课程设计