最近对Hibernate的ThreadLocal Session模式有点兴趣。于是根据曹晓钢翻译的Hibernate Reference做了个小测验,结果发现了一个小bug。
代码很简单,都是利用Hibernate Reference中现成的代码。
首先是一个辅助的得到线程安全的session的HibernateUtil类,
public class HibernateUtil {
public static final SessionFactory sessionFactory;
static{
try {
sessionFactory = new Configuration().configure().buildSessionFactory();
}
catch(Throwable ex){
throw new ExceptionInInitializerError(ex);
}
}
public static final ThreadLocal session = new ThreadLocal();
public static Session currentSession()
{
Session s = (Session) session.get();
if (s==null )
{
s = sessionFactory.getCurrentSession();
session.set(s);
}
return s;
}
public static void closeSession()
{
Session s = (Session) session.get();
if (s!=null)
s.close();
session.set(null);
}
public static SessionFactory getSessionFactory()
{
return sessionFactory;
}
}
然后是一个测试插入数据的代码。也很简单,也是仿Hibernate Reference上面的代码。
public class InsertUser {
public static void main(String[] args) {
Session session = HibernateUtil.currentSession();
Transaction tx= session.beginTransaction();
TUser user = new TUser();
user.setName("Emma");
session.save(user);
tx.commit();
HibernateUtil.closeSession();
}
}
就这么简单一个程序,运行到最后,出现一个错误。
org.hibernate.SessionException: Session was already closed
at org.hibernate.impl.SessionImpl.close(SessionImpl.java:270)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:301)
at $Proxy0.close(Unknown Source)
at Util.HibernateUtil.closeSession(HibernateUtil.java:36)
at test.InsertUser.main(InsertUser.java:20)
Exception in thread "main"
错误出现在 HibernateUtil.closeSession(); 这一行,意思是session已经关闭了,再次关闭它就引起异常了。
不过前面的代码中只有个tx.commit(); 提交事务 而已,并没有自动关闭session啊?
于是把DEBUG信息调用出来,发现了以下几句提示:
DEBUG [main] - after transaction completion
DEBUG [main] - automatically closing session
DEBUG [main] - closing session
DEBUG [main] - connection already null in cleanup : no action
DEBUG [main] - allowing proxied method [close] to proceed to real session
DEBUG [main] - closing session
org.hibernate.SessionException: Session was already closed
特别是下面这3句话引起了我的注意,果然是session关闭了,而且是在 事务结束以后自动关闭的。
DEBUG [main] - after transaction completion
DEBUG [main] - automatically closing session
DEBUG [main] - closing session
那么这个机制是怎么发生的呢?
打开了Hibernate3的源码,我找到了答案。
首先,根据sessionFactory = new Configuration().configure().buildSessionFactory();
打开Configuration类的buildSessionFactory()方法,找到sessionFactory的生成语句
return new SessionFactoryImpl(
this,
mapping,
settings,
getInitializedEventListeners()
);
,然后找到SessionFactoryImpl的getCurrentSession方法,发现是这么定义的。
public org.hibernate.classic.Session getCurrentSession() throws HibernateException {
if ( currentSessionContext == null ) {
throw new HibernateException( "No CurrentSessionContext configured!" );
}
return currentSessionContext.currentSession();
}
他调用的是一个currentSessionContext的currentSession方法。查找currentSessionContext变量,
currentSessionContext = buildCurrentSessionContext();
,知道了buildCurrentSessionContext方法产生了这个currentSessionContext 对象。
private CurrentSessionContext buildCurrentSessionContext() {
String impl = properties.getProperty( Environment.CURRENT_SESSION_CONTEXT_CLASS );
// for backward-compatability
if ( impl == null && transactionManager != null ) {
impl = "jta";
}
if ( impl == null ) {
return null;
}
else if ( "jta".equals( impl ) ) {
return new JTASessionContext( this );
}
else if ( "thread".equals( impl ) ) {
return new ThreadLocalSessionContext( this );
}
else {
try {
Class implClass = ReflectHelper.classForName( impl );
return ( CurrentSessionContext ) implClass
.getConstructor( new Class[] { SessionFactoryImplementor.class } )
.newInstance( new Object[] { this } );
}
catch( Throwable t ) {
log.error( "Unable to construct current session context [" + impl + "]", t );
return null;
}
}
}
这个方法就是用来判断使用JTA管理这个SessionContext还是用ThreadLocal来管理SessionContext的。
在我们这里是用 ThreadLocal 来管理的,于是找到了currentSessionContext 的实现类是 ThreadLocalSessionContext。
找到该类的currentSession方法
public final Session currentSession() throws HibernateException {
Session current = existingSession( factory );
if (current == null) {
current = buildOrObtainSession();
// register a cleanup synch
current.getTransaction().registerSynchronization( buildCleanupSynch() );
// wrap the session in the transaction-protection proxy
if ( needsWrapping( current ) ) {
current = wrap( current );
}
// then bind it
doBind( current, factory );
}
return current;
}
然后跟踪到 buildOrObtainSession(),就是这里,打开了session。
protected Session buildOrObtainSession() {
return factory.openSession(
null,
isAutoFlushEnabled(),
isAutoCloseEnabled(),
getConnectionReleaseMode()
);
}
注意第三个参数:isAutoCloseEnabled
打开Session这个接口,看到 openSession方法中这个参数是如下描述的:
* @param autoCloseSessionEnabled Should the session be auto-closed after
* transaction completion?
,就是说session是否应该在事务提交后自动关闭。
然后打开 ThreadLocalSessionContext 的isAutoCloseEnabled()方法。
/**
* Mainly for subclass usage. This impl always returns true.
*
* @return Whether or not the the session should be closed by transaction completion.
*/
protected boolean isAutoCloseEnabled() {
return true;
}
看到如下提示:Whether or not the the session should be closed by transaction completion ,即无论如何session应该在事务完成后关闭。
答案就在这里,就是说在ThreadLocal Session模式下面,只要提交了事务,那么session就自动关闭了,因此我参照Hibernate Refernece上面的代码写的在事务关闭以后再调用HibernateUtil.closeSession();是不对的,这句代码是完全多余的。
分享到:
相关推荐
**线程局部变量(ThreadLocal)是Java编程中一个非常重要的工具类,它在多线程环境下提供了线程安全的数据存储。ThreadLocal并不是一个变量,而是一个类,它为每个线程都创建了一个独立的变量副本,使得每个线程都...
ThreadLocal是Java编程语言中的一个类,用于在多线程环境中提供线程局部变量。它是一种特殊类型的变量,每个线程都有自己的副本,互不影响,从而实现线程间数据隔离。ThreadLocal通常被用来解决线程共享数据时可能...
在 `LeakingServlet` 的 `doGet` 方法中,如果 `ThreadLocal` 没有设置值,那么会创建一个新的 `MyCounter` 并设置到 `ThreadLocal` 中。关键在于,一旦 `MyCounter` 被设置到 `ThreadLocal`,那么它将与当前线程...
Java事务和ThreadLocal是两种在Java编程中至关重要的概念,它们分别用于处理多线程环境下的数据一致性问题和提供线程局部变量。 首先,我们来深入理解Java事务。在数据库操作中,事务是一系列操作的集合,这些操作...
### 正确理解ThreadLocal:深入解析其工作原理与应用场景 #### 一、ThreadLocal的基本概念 `ThreadLocal`是Java平台提供的一种线程局部变量的解决方案,它为每一个使用该变量的线程都提供了独立的变量副本,使得每...
【Android ThreadLocal 实现原理】 ThreadLocal 是 Android 开发中常用的一个工具类,它允许我们在多线程环境中为每个线程维护独立的变量副本。理解它的实现原理对于优化并发编程和减少线程间的干扰至关重要。 1. ...
ThreadLocal是Java编程中一种非常特殊的变量类型,它主要用于在多线程环境下为每个线程提供独立的变量副本,从而避免了线程间的数据共享和冲突。然而,ThreadLocal在理解和使用过程中容易产生一些误区,这里我们将...
ThreadLocal是Java编程语言中的一个线程局部变量类,它为每个线程提供了一个独立的变量副本,使得每个线程可以独立地改变自己的副本,而不会影响其他线程所对应的副本。这个特性在多线程环境下处理并发问题时非常...
### Java中ThreadLocal详解 #### 一、ThreadLocal概述 在Java多线程编程中,`ThreadLocal`是一个非常重要的工具类,它提供了一种在每个线程内部存储线程私有实例的方法。通常情况下,当多个线程共享某个变量时,...
ThreadLocal 整理 ThreadLocal 是 Java 中的一个重要组件,它能够在每个线程中保持独立的副本。这个功能是通过 Thread 类中的 threadLocals 属性来实现的,这个属性实际上是一个 Entry 数组,其中的每个 Entry 都...
private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal(); public static void setConnection(Connection conn) { connectionHolder.set(conn); } public static Connection get...
Java中的ThreadLocal是一个非常重要的工具类,它在多线程编程中扮演着独特角色,尤其在处理线程间数据隔离和共享时。ThreadLocal不是线程本身,而是为每个线程提供一个独立的变量副本,使得每个线程都可以独立地改变...
本资料主要聚焦于两种设计模式以及Java中的ThreadLocal特性。 首先,我们来探讨单例模式。单例模式是一种确保一个类只有一个实例,并提供全局访问点的设计模式。在Java中,通常通过私有构造函数、静态工厂方法或...
**线程局部变量(ThreadLocal)是Java编程中一个非常重要的概念,主要用于在多线程环境中为每个线程提供独立的变量副本。ThreadLocal不是一种数据结构,而是一种解决线程间共享数据的方式,它提供了线程安全的局部...
在Java编程中,ThreadLocal是线程局部变量的类,它提供了一种在多线程环境中为每个线程创建和维护独立副本的机制。ThreadLocal主要用于解决线程间的数据隔离问题,确保各线程拥有自己的变量副本,避免了数据共享带来...
Java中的`ThreadLocal`类是一个非常实用的工具,它提供了线程局部变量的功能。线程局部变量意味着每个线程都拥有自己独立的变量副本,互不干扰,这在多线程编程中尤其有用,可以避免数据共享带来的同步问题。下面...
这个压缩包 "Quartz-ThreadLocal.rar" 内含的学习资源很可能是关于如何在 Quartz 调度器中结合使用 ThreadLocal 的示例。 Quartz 的核心功能包括: 1. **作业与触发器**:在 Quartz 中,任务被称为“作业”(Job)...
**标题:“JDK的ThreadLocal理解(一)使用和测试”** **正文:** ThreadLocal是Java中的一个非常重要的线程安全工具类,它在多线程编程中扮演着独特的角色。通过创建ThreadLocal实例,我们可以为每个线程提供一个...
Java中的ThreadLocal是一个非常重要的工具类,它在多线程编程中扮演着独特角色,用于为每个线程提供独立的变量副本。理解ThreadLocal的工作原理和使用方法对于编写高效、安全的多线程程序至关重要。 ### ...