Hibernate两种获取session的方式openSession和getCurrentSession的区别与理解

1)openSession和getCurrentSession的区别

       openSession必须关闭,currentSession在事务结束后自动关闭

       openSession没有和当前线程绑定,currentSession和当前线程绑定

2)如果使用currentSession需要在hibernate.cfg.xml文件中进行配置:

        a、如果是本地事务(jdbc事务)

     <propertyname="hibernate.current_session_context_class">thread</property>

           b、如果是全局事务(jta事务)

       <propertyname="hibernate.current_session_context_class">jta</property>

   c、spring管理事务
   <prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext
    </prop>

一般我们使用下面这种:

<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext
    </prop>

由spring管理,因为通常我们都会使用 spring管理事务,如下

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"></property>
    </bean>

所以使用a、b来配置会出错。

全局事务:资源管理器管理和协调的事务,可以跨越多个数据库和进程。资源管理器一般使用XA 二阶段提交协议与“企业信息系统”(EIS) 或数据库进行交互。

本地事务:在单个 EIS或数据库的本地并且限制在单个进程内的事务。本地事务不涉及多个数据来源。

上面这两种方式都是可以用sessionFactory . 点出来的。还有一种方式获得session,就是使用辅助类HibernateUtil这种:

package com.cxp.util;

import org.hibernate.*;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;

public final class HibernateUtil {
    // 初始化一个ThreadLocal对象
    private static final ThreadLocal sessionTL = new ThreadLocal();
    private static Configuration configuration;
    private final static SessionFactory sessionFactory;
    static {
        try {
            // 加载配置文件hibernate.cfg.xml
            configuration = new Configuration().configure();

            // 获得会话工厂
            // sessionFactory = configuration.buildSessionFactory();

            // 这是Hibernate4.0之后引入的新特性,Service Register机制
            StandardServiceRegistry ssrRegistry = new StandardServiceRegistryBuilder()
                    .applySettings(configuration.getProperties()).build();
            sessionFactory = configuration.buildSessionFactory(ssrRegistry);

        } catch (Throwable ex) {
            ex.printStackTrace();
            throw new ExceptionInInitializerError(ex);
        }
    }

    /** * 获取Session */
    public static Session currentSession() {
        // sessionTL的get()方法根据当前线程返回其对应的线程内部变量,
        // 也就是我们需要的Session,多线程情况下共享数据库连接是不安全的。
        // ThreadLocal保证了每个线程都有自己的Session。
        Session session = (Session) sessionTL.get();
        // 如果session为null,则打开一个新的session
        if (session == null) {
            // 创建一个数据库连接对象session。
            session = sessionFactory.openSession();
            // 保存该数据库连接session到ThreadLocal中。
            sessionTL.set(session);
        }
        // 如果当前线程已经访问过数据库了,
        // 则从sessionTL中get()就可以获取该线程上次获取过的数据库连接对象。
        return session;
    }

    /** * 关闭Session */
    @SuppressWarnings("unchecked")
    public static void closeSession() {
        Session session = (Session) sessionTL.get();
        sessionTL.set(null);
        session.close();
    }
}

其实这种方式获得的session和使用getCurrentSession获得的session是一样的都是和当前线程绑定的。

下面是spring管理事务时获取Session的源代码分析:

***1.我们点开sessionFactory的getCurrentSession()方法就会发现
sessionFactoryImpl是下面这样的:*

    public Session getCurrentSession() throws HibernateException {
        if ( currentSessionContext == null ) {
            throw new HibernateException( "No CurrentSessionContext configured!" );
        }
        return currentSessionContext.currentSession();
    }

2.而按Ctrl再点击currentSession()时会发现其实现有四个(ssh环境下),其中最后一个是SpringSessionContext,它实现CurrentSessionContext这个接口,我们点击SpringSessionContext类下的currentSession() 方法:

    public Session currentSession() throws HibernateException {
        Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);
        if (value instanceof Session) {
            return (Session) value;
        }
        else if (value instanceof SessionHolder) {
            SessionHolder sessionHolder = (SessionHolder) value;
            Session session = sessionHolder.getSession();
            if (!sessionHolder.isSynchronizedWithTransaction() &&
                    TransactionSynchronizationManager.isSynchronizationActive()) {
                TransactionSynchronizationManager.registerSynchronization(
                        new SpringSessionSynchronization(sessionHolder, this.sessionFactory, false));
                sessionHolder.setSynchronizedWithTransaction(true);
                // Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
                // with FlushMode.MANUAL, which needs to allow flushing within the transaction.
                FlushMode flushMode = session.getFlushMode();
                if (flushMode.equals(FlushMode.MANUAL) &&
                        !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                    session.setFlushMode(FlushMode.AUTO);
                    sessionHolder.setPreviousFlushMode(flushMode);
                }
            }
            return session;
        }

        if (this.transactionManager != null) {
            try {
                if (this.transactionManager.getStatus() == Status.STATUS_ACTIVE) {
                    Session session = this.jtaSessionContext.currentSession();
                    if (TransactionSynchronizationManager.isSynchronizationActive()) {
                        TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session));
                    }
                    return session;
                }
            }
            catch (SystemException ex) {
                throw new HibernateException("JTA TransactionManager found but status check failed", ex);
            }
        }

        if (TransactionSynchronizationManager.isSynchronizationActive()) {
            Session session = this.sessionFactory.openSession();
            if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
                session.setFlushMode(FlushMode.MANUAL);
            }
            SessionHolder sessionHolder = new SessionHolder(session);
            TransactionSynchronizationManager.registerSynchronization(
                    new SpringSessionSynchronization(sessionHolder, this.sessionFactory, true));
            TransactionSynchronizationManager.bindResource(this.sessionFactory, sessionHolder);
            sessionHolder.setSynchronizedWithTransaction(true);
            return session;
        }
        else {
            throw new HibernateException("Could not obtain transaction-synchronized Session for current thread");
        }
    }

}

3.先不去看其它的,这个方法最终都是返回Session,那它是怎么获得Session的呢,通过下面这句:

    Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);

关键是getResource(this.sessionFactory)这个方法,点开来看:

    public static Object getResource(Object key) {
        Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
        Object value = doGetResource(actualKey);
        if (value != null && logger.isTraceEnabled()) {
            logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
                    Thread.currentThread().getName() + "]");
        }
        return value;
    }

4.找到关键的方法doGetResource(actualKey),继续点开:

    private static Object doGetResource(Object actualKey) {
        Map<Object, Object> map = resources.get();
        if (map == null) {
            return null;
        }
        Object value = map.get(actualKey);
        // Transparently remove ResourceHolder that was marked as void...
        if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
            map.remove(actualKey);
            // Remove entire ThreadLocal if empty...
            if (map.isEmpty()) {
                resources.remove();
            }
            value = null;
        }
        return value;
    }

5.这个resources是个 NamedThreadLocal 对象,而其继承于ThreadLocal,其get()方法为:

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }

可以看到,其调用的就是ThreadLocal的get()方法,所以其实跟用HibernateUtil时用来绑定当前线程的方式是一样的。

你可能感兴趣的:(Hibernate,线程,session,jdbc,事务)