Hibernate(21):getCurrentSession还是用openSession?

本文深入探讨Hibernate中SessionFactory的getCurrentSession与openSession的区别。分析getCurrentSession如何利用ThreadLocal管理Session,减少Session创建,以及如何在事务结束时清理Session绑定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    从Hibernate(十九): 异常"Session was already closed" 开始的,连着几遍针对"Session was already closed"问题的持续跟踪, 在上篇Hibernate(20):默认情况下决定commit时session命运的 三个条件里最终找到了源头. 路途虽说艰辛,但收获还是不小, "拔萝卜带出泥"地引出了一系列Hibernate世界里的基本构架. 在接下来,我们先来看getCurrentSession与openSession有什么区别.
--------------------
    我们知道, 一般来说, 为了使用Session, 可以有两种方式: 调用SessionFactory的getCurrentSession或openSession. 那么这两个方法有什么不同? 使用时有什么注意点? 再往深地说, 这样的设计对于我们有什么借鉴之处?

    好像是Hibernate以前的版本里没有getCurrentSession方法, 它是后来新加的(文档中有这样的描述: Starting with version 3.0.1, Hibernate added the SessionFactory.getCurrentSession() method). 为什么要加呢? Hibernate自己的文档里有这方面的介绍, 这里就不copy/paste到这了. 总的一个核心思想就是想达到"Contextual Sessions"效果. 这个效果是通过一个接口 org.hibernate.context.CurrentSessionContext来实现的, 一般最常用的是它的实现类org.hibernate.context.ThreadLocalSessionContext. 对应的配置是,hibernate.cfg.xml文件中的<property name="current_session_context_class">thread</property>.

    配置是这样的, 那么具体在代码层面又是怎么实现的? 我们从SessionFactory的getCurrentSession方法说起,照应题目中的疑问,先看从getCurrentSession的实现来看其的好处.

    在这个方法中, 首先判断有没有配置currentSessionContext,如有的话,再调用currentSessionContext的currentSession方法. 那么这个currentSessionContext 是什么时候创建的? 
    在SessionFactory(实现类SessionFactoryImpl)的构造方法中, 我们找到一句: currentSessionContext = buildCurrentSessionContext();在buildCurrentSessionContext方法中, hibernate从配置文件中取出current_session_context_class信息, (此方法中有个transactionManager的问题, 这里先不讨论),据这个current_session_context_class是否为jta或thread或managed相应创建 Hibernate内置org.hibernate.context.CurrentSessionContext接口的实现类 JTASessionContext或ThreadLocalSessionContext或ManagedSessionContext. 另外,buildCurrentSessionContext方法还支持自定义的CurrentSessionContext接口实现,这里也不做介绍.

    这里我们直接进到ThreadLocalSessionContext实现类的currentSession方法,这个也就是SessionFactory的getCurrentSession最终要调用的方法.

    首先第一句是existingSession( factory ), 这个没啥可说的, 它是想从ThreadLocal里取出与传入的factory 相对应的Session. 如果没有,就调用buildOrObtainSession, 总觉得这个方法有些歧义,看它的实现发现实际上是new了一个Session,而没有什么Obtain, new一个session是通过SessionFactory的openSession(final Connection connection,
final boolean flushBeforeCompletionEnabled,final boolean autoCloseSessionEnabled,final ConnectionReleaseMode connectionReleaseMode)方法. 这里的autoCloseSessionEnabled也就是ThreadLocalSessionContext类里写死的 isAutoCloseEnabled方法,也就是它最终导致了t.commit()后session.close时,"Session was already closed"异常的抛出.

    再看current.getTransaction().registerSynchronization( buildCleanupSynch() )一句, 第一次见registerSynchronization方法, 它是干啥的? 要给跟刚才通过buildOrObtainSession方法新建的Session绑定一个 javax.transaction.Synchronization实现. 这里看到是绑定了一个unbind( factory )方法, 看样子是在事务结束后将ThreadLocal里绑定的session去除掉. (是不是也正是这个registerSynchronization方法, 才有了三个条件 中的callback为true了?日后再进一步观察验证 ).

    接下来是是否对新建session是否要做代理wrap的判断和相应处理.(这样也就解决了为什么debug跟踪时session.save方法时总先要 执行ThreadLocalSessionContext.TransactionProtectionWrapper类里invoke方法,一个新问 题: 从TransactionProtectionWrapper的名字看是要做"事务保护",那么这个事务保护又是怎么实现的?若不保护又怎样?这两个问题 先放着)

    再接下来就是通过执行doBind方法将新建的session绑定到ThreadLocal了.

    这篇中,引出问题后,从getCurrentSession的实现看到有如下好处:
        1, 如果ThreadLocal里已有,就不必再新建session,这样也就为session的方便获取做好的铺垫.
        2, 若新建session,对其对应的Transaction做了registerSynchronization,这里做了unbind( factory )处理.
        3, 若新建session,视情况再做代理封装,这里封装为TransactionProtectionWrapper.
    下一篇中, 我们将看openSession有什么优势?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值