最近对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可以在多线程环境下保证session数据的安全性,避免了同步锁带来的性能开销。 3. **如何使用ThreadLocal实现Session管理?** 首先,定义一个ThreadLocal变量来存储session对象: ```java ...
当开始一个事务时,`TransactionStatus`会被放入ThreadLocal,然后在事务结束(提交或回滚)时移除。这种设计使得在同一个线程内的所有数据库操作都可以访问到相同的事务上下文,无需手动传递。 例如,在Spring的`...
例如,在分布式服务中,可能会使用分布式事务解决方案如两阶段提交(2PC)、补偿事务(TCC)等,而ThreadLocal则可以辅助管理这些分布式事务的状态。 总结一下,Java事务和ThreadLocal都是Java多线程编程中不可或缺...
在Java中,代理模式常用于动态代理,允许在运行时为对象绑定额外的行为,如AOP(面向切面编程)中的事务管理。 然后,我们转向ThreadLocal。ThreadLocal是Java中的一个线程局部变量,它为每个线程都提供了一个独立...
在IT行业中,线程安全和事务管理是两个非常重要的概念,尤其在多线程环境下的分布式系统中。本文将深入探讨ThreadLocal的使用以及Spring框架中的事务管理,这两个主题都是Java开发人员必须掌握的关键技能。 首先,...
【ThreadLocal模式管理Session的理解】 在使用Hibernate进行数据库操作时,正确管理Session是优化系统性能的关键。Session由SessionFactory创建,而SessionFactory是线程安全的,这意味着它可以被多个并发线程共享...
- 使用ThreadLocal时,必须谨慎处理资源的生命周期,确保在事务结束时正确关闭数据库连接,防止资源泄露。 - 虽然ThreadLocal提供了线程隔离,但并不意味着它是线程安全的,对于修改ThreadLocal本身的操作仍需同步...
在Java编程领域,ThreadLocal和事务管理是两个关键的概念,特别是在构建复杂且高效的Web应用程序时。ThreadLocal是一种线程局部变量,而事务则是数据库操作的原子性保证。在这个小型简单练习中,我们看到如何结合c3p...
在默认情况下,JDBC连接是非自动提交模式,这意味着每条SQL语句执行后,如果未显式提交,事务会一直持续到程序结束或遇到异常。要手动控制事务,我们需要调用`Connection`对象的`setAutoCommit(false)`方法来禁用...
5. **事务管理**:在使用ThreadLocal的模式下,事务管理通常与Session绑定。每个线程内的操作在同一个Session中进行,方便控制事务边界。 6. **性能优化**:ThreadLocal提供了一种高效的Session管理方式,避免了...
在这个例子中,getSession()方法会检查ThreadLocal中是否存在Session,若不存在则创建并设置,而closeSession()方法则负责关闭并移除当前线程的Session副本。 总结来说,ThreadLocal是Java中解决多线程数据隔离问题...
如果有异常,事务会被回滚,从而实现了自动的事务回滚策略。这种机制大大简化了事务管理,使得开发者能更专注于业务逻辑。 在实际应用中,我们可以通过SessionFactory的openSession方法来创建新的Session,并使用...
ThreadLocal是Java编程中一种非常特殊的变量类型,它主要用于在多线程环境下为每个线程提供独立的变量副本,从而避免了线程间的数据共享和冲突。然而,ThreadLocal在理解和使用过程中容易产生一些误区,这里我们将...
在多用户客户端/服务器应用中,最常用的模式是“session-per-request”,即每个客户端请求对应一个Session,所有的数据库操作都在这个Session内完成,Session在请求结束时同步并关闭,事务也在此时提交。 实现...
**线程局部变量(ThreadLocal)是Java编程中一个非常重要的工具类,它在多线程环境下提供了线程安全的数据存储。ThreadLocal并不是一个变量,而是一个类,它为每个线程都创建了一个独立的变量副本,使得每个线程都...
2. **事务处理**:在事务管理中,`ThreadLocal`可以用于维护每个线程的事务状态,确保事务的完整性和一致性。 3. **日志记录**:在多线程环境中,`ThreadLocal`可以用于维护每个线程的日志上下文,如线程ID、用户名...
通过对这个文件的详细分析,我们可以学习如何将模板模式应用于事务管理,如何使用ThreadLocal来改善多线程环境下的事务处理,以及如何通过子类化来适应不同的业务场景。 总结来说,Java事务模板设计模式结合...
在传统的多线程Servlet环境下,如果将Session作为类变量存储,如上述例子中的`TestServlet`,则会面临线程安全问题。由于Servlet容器(如Tomcat)通常只创建一个Servlet实例供所有请求共享,每个线程在执行时可能会...
ThreadLocal是Java编程语言中的一个类,用于在多线程环境中提供线程局部变量。它是一种特殊类型的变量,每个线程都有自己的副本,互不影响,从而实现线程间数据隔离。ThreadLocal通常被用来解决线程共享数据时可能...