蓝凌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 的"严"

你会发现:

  • 事务问题少了

  • 连接泄露没了

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

相关推荐
啦啦啦_99994 小时前
Redis实例-2
java
alice--小文子4 小时前
cursor-mcp工具使用
java·服务器·前端
进阶小白猿4 小时前
Java技术八股学习Day33
java·开发语言·学习
程序员敲代码吗4 小时前
如何通过命令行启动COMSOL的参数化、批处理和集群扫描
java·c#·bash
晚霞的不甘4 小时前
揭秘 CANN 内存管理:如何让大模型在小设备上“轻装上阵”?
前端·数据库·经验分享·flutter·3d
MX_93594 小时前
Spring的bean工厂后处理器和Bean后处理器
java·后端·spring
市场部需要一个软件开发岗位4 小时前
JAVA开发常见安全问题:纵向越权
java·数据库·安全
海奥华24 小时前
mysql索引
数据库·mysql
历程里程碑5 小时前
普通数组----合并区间
java·数据结构·python·算法·leetcode·职场和发展·tornado
程序员泠零澪回家种桔子5 小时前
Spring AI框架全方位详解
java·人工智能·后端·spring·ai·架构