首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

hibernate中session的线程安全有关问题

2013-10-01 
hibernate中session的线程安全问题Hibernate的基本特征是完成面向对象的程序设计语言到关系数据库的映射,

hibernate中session的线程安全问题

Hibernate的基本特征是完成面向对象的程序设计语言到关系数据库的映射,在Hibernate中使用持久化对象PO(Persistent Object)完成持久化操作,对PO的操作必须在Session管理下才能同步到数据库,?
但是这里的Session并非指HttpSession,可以理解为基于JDBC的Connnection,Session是 Hibernate运作的中心,?
对象的生命周期、事务的管理、数据库的存取都与Session息息相关,首先,我们需要知道,?
SessionFactory负责创建Session,SessionFactory是线程安全的,多个并发线程可以同时访问一个?
SessionFactory 并从中获取Session实例。?

而Session并非线程安全,也就是说,如果多个线程同时使用一个Session实例进行数据存取,?
则将会导致 Session 数据存取逻辑混乱.因此创建的Session实例必须在本地存取空上运行,?
使之总与当前的线程相关。这里就需要用到ThreadLocal,在很多种Session 管理方案中都用到了它.?

ThreadLocal 是Java中一种较为特殊的线程绑定机制,通过ThreadLocal存取的数据,?
总是与当前线程相关,?
也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种隔离机制,ThreadLocal并不是线程本地化的实现,而是线程局部变量。也就是说每个使用该变量的线程都必须为该变量提供一个副本,每个线程改变该变量的值仅仅是改变该副本的值,而不会影响其他线程的该变量的值,ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源,因此不再需要对线程同步。常规设计,SESSION是放置在Threadlocal里面的,每个线程都有自己独占的SESSION!

?

无论是立即加载还是延迟加载必须要连接数据库的,而在java中连接数据库是依赖java.sql.Connection,在Hibernate session就是Connection的一层高级封装,一个session对应了一个Connection,要实现延迟加载必须有session才行。 而且要进行延迟加载还必须保证是同一个session才行,用另外一个session去延迟加载前一个session的代理对象是不行的。大家都知道 Connection是使用过后必须要进行关闭的,那么我们如何保证一次http请求过程中,一直都使用一个session呢,即一个 Connection呢。而且还要保证http请求结束后正确的关闭。

??? 好,现在我们知道了我们要解决的问题

??? 1.如何保证http请求结束后正确的关闭session

??? 2.如何保证http请求过程中一直使用同一个session

??? 第一个问题很容易想到,使用过滤器

??? public void doFilter(ServletRequest request, ServletResponse response,FilterChain filterChain) {

??? try {

??? filterChain.doFilter(request, response);

??? }

??? catch (IOException e) {

??? e.printStackTrace();

??? }

??? catch (ServletException e) {

??? e.printStackTrace();

??? }

??? finally {

??? try {

??? HibernateUtil.commitTransaction();

??? }

??? catch (Exception e) {

??? HibernateUtil.rollbackTransaction();

??? }

??? finally {

??? HibernateUtil.closeSession();

??? }

??? }

??? }

??? 要解决第二个问题我们必须先搞清楚,http请求在java中是以什么样的机制实现的,在java中一个请求就是一个线程,像流行的web容器 Tomcat等,往往都是采用线程池机制的也就是说有n个线程在池子里面,每当有http请求时,随机从线程池中取出一个线程对象去处理请求,实际上多次请求可能使用的是同一线程也可能不是,这是随机的。要保证整个请求中使用同一session最容易想到的就是把这个session绑定到线程上,在 java中使用ThreadLocal可以轻松绑定变量,每个线程有一个自己的ThreadLocal,这个ThreadLocal会随线程的销毁一起销毁,既然是每个线程有一个那么多个线程间自然是不会有影响了,所以把session绑定在ThreadLocal里面是最好的选择了,最后我把Hibernate session的相关代码发出来。

?

public class HibernateUtil {

public static final ThreadLocal session =new ThreadLocal();

public static final SessionFactory sessionFactory;
?? static {
????? try {
??????? sessionFactory = new Configuration().configure().buildSessionFactory();
????? } catch (Throwable ex) {
?????????? throw new ExceptionInInitializerError(ex);
????? }????
}

???? public static Session currentSession() throws HibernateException {
??????? Session s = session.get();
??????? if(s == null) {
????????? s = sessionFactory.openSession();
????????? session.set(s);
?????????? }
???????? return s;
?????? }

??? public static void closeSession() throws HibernateException {
?????????? Session s = session.get();
??????? if(s != null) {
??????????? s.close();
??????? }
??????? session.set(null);
??? }
}

?

ThreadLocal类是一个全局共享Map,里面用于存放每一个线程的唯一实例。比如你放一个A类进去后,只要还是你这个线程来读取的话,那么这个A类还是你存进去的那个A。而如果是别的线程来读则是读不到的,因为他的线程号和你是不同滴。同理,如果你再放一个A类进去就会覆盖你以前存入的A类。 在你的程序中,你首先声明一个final的静态ThreadLocal,是为了让此ThreadLocal成为服务器中唯一的一个实例,然后在这个实例中进行操作:首先就是查找这个实例中是否存在session,用的是s.get()方法,如果你以前存过的话,会给你以前存储过的那个session。相反,如果你没有存过,那么他会将一个null值传递给你程序中的那个sssion。这时来一个判断,如果是空,那么就将你程序中的session进行初始赋值,并将他放入到ThreadLocal中,以便于下次直接调用。

热点排行