Hibernate中使用Threadlocal创建线程安全的Session (2012-10-29 21:29:19)转载▼标签: getcurrentsessionthreadlocalopensession线程安全的session杂谈 分类: Hibernate
一、问题的提出
我们知道Session是由SessionFactory负责创建的,而SessionFactory的实现是线程安全的,多个并发的线程可以同时访问一个SessionFactory并从中获取Session实例,而Session不是线程安全的。Session中包含了数据库操作相关的状态信息,那么说如果多个线程同时使用一个Session实例进行CRUD,就很有可能导致数据存取的混乱,你能够想像那些你根本不能预测执行顺序的线程对你的一条记录进行操作的情形吗?
二、 解决方案思路(使用Threadlocal类集合)
早在Java1.2推出之时,Java平台中就引入了一个新的支持:java.lang.ThreadLocal,给我们在编写多线程程序时提供了一种新的选择。ThreadLocal是什么呢?其实ThreadLocal并非是一个线程的本地实现版本,它并不是一个Thread,而是thread local variable(线程局部变量)。也许把它命名为ThreadLocalVar更加合适。线程局部变量(ThreadLocal)其实的功用非常简单, 就是为每一个使用该变量的线程都提供一个变量值的副本,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。从线程的角度看,就好像每一个线程都完全拥有一个该变量。
ThreadLocal这个类本身不是代表线程要访问的变量,这个类的成员变量才是。JDK1.5给ThreadLocal加了泛型功能,即是 ThreadLocal,这个泛型T即是要线程的本地变量。线程通过ThreadLocal的get和set方法去访问这个变量T。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单,在ThreadLocal类中有一个Map,用于存储每一个线程的变量的副本。比如下面的示例实现(为了简单,没有考虑集合的泛型):
public class ThreadLocal {
private Map values = Collections.synchronizedMap(new HashMap());
public Object get() {
Thread currentThread = Thread.currentThread();
Object result = values.get(currentThread);
if(result == null&&!values.containsKey(currentThread)) {
result = initialValue();
values.put(currentThread, result);
}
return result;
}
public void set(Object newValue) {
values.put(Thread.currentThread(), newValue);
}
public Object initialValue() {
return null;
}
}
三、解决方案步骤
1、在HibernateUtil类中我们需要定义一个静态的成员变量用于保存当前线程共用的Session
public class HibernateUtil {
private static SessionFactory factory;
// 使用ThreadLocal集合保存当前业务线程中的SESSION
private static ThreadLocal session = new ThreadLocal();
static {
// 第一步:读取HIBERNATE的配置文件,读取hibernate.cfg.xml文件
Configuration con = new Configuration().configure();
// 第二步:创建服务注册构建器对象,通过配置对象中加载所有的配置信息,存放到注册服务中
ServiceRegistryBuilder regBuilder = new ServiceRegistryBuilder()
.applySettings(con.getProperties());
// 创建注册服务
ServiceRegistry reg = regBuilder.buildServiceRegistry();
// 第三步:创建会话工厂
factory = con.buildSessionFactory(reg);
}
public static Session getLocalThreadSession() {
Session s = session.get();// 获取当前线程下的SESSION
if (s == null) {
s = getFactory().getCurrentSession();// 获取当前线程中的SESSION, 需在在Hibernate.cfg.xml文件,具体请看面的说明
session.set(s);// 将当前SESSION放入到当前线程的容器中保存
}
return s;
}
public static void closeSession() {
Session s = session.get();// 获取当前线程下的SESSION
if (s != null) {
// s.close();//这里无需将Session关闭,因为该Session是保存在当前线程//中的,线程执行完毕Session自然会销毁
session.set(null);// 将当前线程中的会话清除
}
}
}
2、添加OpenSessionInViewFilter过滤器(不要忘了在Web.xml配置该过滤器)
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
Session s = HibernateUtil.getThreadLocalSession();
Transaction t = null;
try {
// 开始事务
t = s.beginTransaction();
// 进入一系列的过滤链,处理相应的ACTION、业务逻辑及数据层
filterChain.doFilter(request, response);
// 提交事务
t.commit();
} catch (Exception e) {
if (t != null)
t.rollback();//出现异常回滚事务
throw new RuntimeException(e.getMessage(), e);
} finally {
HibernateUtil.closeSession();
}
}
##############################################################################################
说明:关于getCurrentSession()方法:
sessionFactory.getCurrentSession()获取当前线程中的Session,当调用时,hibernate将session绑定到当前线程,事务结束后,hibernate将session从当前线程中释放,并且关闭 session。当再次调用getCurrentSession()时,将得到一个新的session,并重新开始这一系列工作。这样调用方法如下: Session session = HibernateUtil.getSessionFactory().getCurrentSession();
getCurrentSession和openSession的区别:
1、getCurrentSession创建的session会和绑定到当前线程,而openSession不会。
2、getCurrentSession创建的线程会在事务回滚或事物提交后自动关闭,而openSession必须手动关闭
3、getCurrentSession需在在Hibernate.cfg.xml文件中添加配置:
<property name="current_session_context_class">thread</property>
四、总结
这种解决方案的优缺点:
1、优点:使用ThreadLocal除了有避免频繁创建和销毁session的好处外, 还有一个特别大的好处,
就是可以做到多线程的数据隔离, 可以避免多个线程同时操作同一个session
2.缺点: 如下图
使用拦截器在响应返回时,又重复过滤了一次,延长了响应的时间(改进:我们可以把写在过滤器中的方法写在一个具体的类,用到的时候再调用)
分享到:
相关推荐
在Hibernate框架中,Session是与数据库交互的主要接口,它负责对象的持久化操作。然而,由于Session不是线程安全的,所以在多线程环境中管理Session就需要特别注意。本篇文章将详细探讨Hibernate中Session的管理,...
在 Hibernate 中,每个线程都需要一个 Session 对象来与数据库交互。如果未绑定 Session 到线程, Hibernate 将无法正确地执行数据库操作。 解决方案 解决该错误的方法很简单,只需在相应的 manager 实现类中添加 ...
“当前线程中的Session”这一概念指的是,为每个线程绑定一个单独的Session实例,这样可以更好地管理事务,避免并发问题。在Web应用中,通常使用Servlet容器如Tomcat,它支持ThreadLocal机制,能够为每个请求创建并...
Session的概念是基于对象的状态管理和数据库事务的,它的生命周期通常与一个物理事务绑定。以下是Session接口的一些核心概念和方法的详细解释: 1. **对象状态**: - **自由状态(Transient)**:对象没有被持久化...
getCurrentSession() 方法会将 Session 绑定到当前线程中,这意味着在当前线程中,只能存在一个 Session 实例。如果您使用 getCurrentSession() 方法获取 Session,然后在 commit 或 rollback 时,Session 将自动...
1. **本地线程绑定**:通过设置`hibernate.current_session_context_class`为`thread`,Session与本地线程关联。当线程首次调用SessionFactory的`getCurrentSession()`,会创建并绑定新的Session,后续调用则返回已...
1. 配置拦截器:在Struts2的配置文件中,我们需要添加一个专门处理Hibernate Session的拦截器,如`OpenSessionInViewInterceptor`。这个拦截器会在请求开始时打开Session,在请求结束时关闭Session,确保Session在...
`HibernateSessionFactory` 使用ThreadLocal变量`threadLocal`来存储线程绑定的Session实例。ThreadLocal是一种线程局部变量,每个线程都有自己的副本,不会互相干扰。这确保了每个线程都有独立的Session实例,避免...
-- 使用线程绑定的Session --> ``` 总结来说,Hibernate的事务处理是其强大功能的重要组成部分,它提供了编程式和声明式两种方式来管理事务,以适应不同开发场景的需求。理解并熟练运用事务处理,对于构建健壮的...
同时,Hibernate的session不同于JSP应用中的HttpSession。 SessionFactory SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。这里用到了工厂模式。需要注意的是...
总结起来,"Hibernage_Session_Manager_ThreadLocal"是一个关于使用ThreadLocal在多线程环境中优化Hibernate Session管理的技术实践,通过这种方式可以提升应用程序的性能和安全性。`HibernateUtil.java`是实现这一...
5. **SessionFactory和Session**:在Hibernate中,SessionFactory是线程安全的,用于创建Session实例。Session是与数据库交互的入口点,处理CRUD操作。在登录过程中,可能会使用Session来查询数据库中的用户信息。 ...
2. `getSession()` 和 `getSessionFromThread()` 方法分别提供了打开新 Session 和获取当前线程绑定的 Session 的方式。 3. `addUser` 方法演示了如何添加用户,通过 `Session` 对象的 `save()` 方法保存实体到...
- 创建`Session`实例,它是与数据库交互的单线程工作单元,类似于JDBC中的Connection。 - 在Session中开启事务(Transaction),进行数据库操作。 - 使用Session的方法(如save、update、delete、load、get等)来...
`getCurrentSession()`方法旨在支持线程绑定的Session管理,它会在合适的上下文中自动打开、关闭或重新连接Session,简化了事务管理和并发控制。 描述中的链接虽然没有提供具体内容,但通常会详细解析`...
在上述代码中,`getCurrentSession()`返回了一个与当前线程绑定的Session,简化了事务管理。 此外,为了处理事务,还需要在Spring配置文件中配置`PlatformTransactionManager`。对于Hibernate,我们使用`...
- 主要用于解决懒加载异常,将Hibernate Session与请求线程绑定,保证页面读取数据时Session开启状态。 - 遇到业务处理数据量大时,可能会引起内存溢出或者数据库连接问题。 - 解决方式可以考虑使用迫切左外连接...