下面是我结合实例与源码的分析。
没有使用spring管理的情况
主要区别openSession()和openCurrentSession(),openSession()即打开一个session,但程序中直接使用它,而是使用getCurrentSession(),典型用法如下(没有使用spring申明式管理管理事务)
private List listEvents() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); List result = session.createQuery("from Event").list(); session.getTransaction().commit(); return result; }
openCurrentSession()即获取当前上下文session
/** * Obtains the current session. The definition of what exactly "current" * means controlled by the {@link org.hibernate.context.CurrentSessionContext} impl configured * for use. * <p/> * Note that for backwards compatibility, if a {@link org.hibernate.context.CurrentSessionContext} * is not configured but a JTA {@link org.hibernate.transaction.TransactionManagerLookup} * is configured this will default to the {@link org.hibernate.context.JTASessionContext} * impl. * * @return The current session. * * @throws HibernateException Indicates an issue locating a suitable current session. */ public org.hibernate.classic.Session getCurrentSession() throws HibernateException;
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 ) ) { if ( settings.getTransactionFactory().areCallbacksLocalToHibernateTransactions() ) { log.warn( "JTASessionContext being used with JDBCTransactionFactory; auto-flush will not operate correctly with getCurrentSession()" ); } return new JTASessionContext( this ); } else if ( "thread".equals( impl ) ) { return new ThreadLocalSessionContext( this ); } else if ( "managed".equals( impl ) ) { return new ManagedSessionContext( 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; } } }
从上面获取CurrentSessionContext的方法中看出,current session是由配置文件制定,默认是jta。 如果使用spring管理,切勿配置该项,默认将为springContextProvider
current_session_context_class thread
使用spring管理
spring中的HibernateTemplate有对hibernate的封装,有很多便捷的方法,在DAO实现中
@Override public Magic read(int id) { return super.getHibernateTemplate().get(Magic.class, id); }
但是如果需要自己使用复杂的组合查找,可以使用excute(new HiberanteCallback())
public <T> T get(final Class<T> entityClass, final Serializable id, final LockMode lockMode) throws DataAccessException { return executeWithNativeSession(new HibernateCallback<T>() { @SuppressWarnings("unchecked") public T doInHibernate(Session session) throws HibernateException { if (lockMode != null) { return (T) session.get(entityClass, id, lockMode); } else { return (T) session.get(entityClass, id); } } }); }
这样调用获取的session是通过下面这个方法
/** * Return a Session for use by this template. * <p>Returns a new Session in case of "alwaysUseNewSession" (using the same * JDBC Connection as a transactional Session, if applicable), a pre-bound * Session in case of "allowCreate" turned off, and a pre-bound or new Session * otherwise (new only if no transactional or otherwise pre-bound Session exists). * @return the Session to use (never <code>null</code>) * @see SessionFactoryUtils#getSession * @see SessionFactoryUtils#getNewSession * @see #setAlwaysUseNewSession * @see #setAllowCreate */ protected Session getSession() { if (isAlwaysUseNewSession()) { return SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()); } else if (isAllowCreate()) { return SessionFactoryUtils.getSession( getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator()); } else if (SessionFactoryUtils.hasTransactionalSession(getSessionFactory())) { return SessionFactoryUtils.getSession(getSessionFactory(), false); } else { try { return getSessionFactory().getCurrentSession(); } catch (HibernateException ex) { throw new DataAccessResourceFailureException("Could not obtain current Hibernate Session", ex); } } }
SessionFactoryUtil中的逻辑如下
/** * Get a Hibernate Session for the given SessionFactory. Is aware of and will * return any existing corresponding Session bound to the current thread, for * example when using {@link HibernateTransactionManager}. Will create a new * Session otherwise, if "allowCreate" is <code>true</code>. * <p>Same as {@link #getSession}, but throwing the original HibernateException. * @param sessionFactory Hibernate SessionFactory to create the session with * @param entityInterceptor Hibernate entity interceptor, or <code>null</code> if none * @param jdbcExceptionTranslator SQLExcepionTranslator to use for flushing the * Session on transaction synchronization (may be <code>null</code>) * @param allowCreate whether a non-transactional Session should be created * when no transactional Session can be found for the current thread * @return the Hibernate Session * @throws HibernateException if the Session couldn't be created * @throws IllegalStateException if no thread-bound Session found and * "allowCreate" is <code>false</code> */ private static Session doGetSession( SessionFactory sessionFactory, Interceptor entityInterceptor, SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate) throws HibernateException, IllegalStateException { Assert.notNull(sessionFactory, "No SessionFactory specified"); SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory); if (sessionHolder != null && !sessionHolder.isEmpty()) { // pre-bound Hibernate Session Session session = null; if (TransactionSynchronizationManager.isSynchronizationActive() && sessionHolder.doesNotHoldNonDefaultSession()) { // Spring transaction management is active -> // register pre-bound Session with it for transactional flushing. session = sessionHolder.getValidatedSession(); if (session != null && !sessionHolder.isSynchronizedWithTransaction()) { logger.debug("Registering Spring transaction synchronization for existing Hibernate Session"); TransactionSynchronizationManager.registerSynchronization( new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, 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.lessThan(FlushMode.COMMIT) && !TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { session.setFlushMode(FlushMode.AUTO); sessionHolder.setPreviousFlushMode(flushMode); } } } else { // No Spring transaction management active -> try JTA transaction synchronization. session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator); } if (session != null) { return session; } } logger.debug("Opening Hibernate Session"); Session session = (entityInterceptor != null ? sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession()); // Use same Session for further Hibernate actions within the transaction. // Thread object will get removed by synchronization at transaction completion. if (TransactionSynchronizationManager.isSynchronizationActive()) { // We're within a Spring-managed transaction, possibly from JtaTransactionManager. logger.debug("Registering Spring transaction synchronization for new Hibernate Session"); SessionHolder holderToUse = sessionHolder; if (holderToUse == null) { holderToUse = new SessionHolder(session); } else { holderToUse.addSession(session); } if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) { session.setFlushMode(FlushMode.MANUAL); } TransactionSynchronizationManager.registerSynchronization( new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true)); holderToUse.setSynchronizedWithTransaction(true); if (holderToUse != sessionHolder) { TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse); } } else { // No Spring transaction management active -> try JTA transaction synchronization. registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder); } // Check whether we are allowed to return the Session. if (!allowCreate && !isSessionTransactional(session, sessionFactory)) { closeSession(session); throw new IllegalStateException("No Hibernate Session bound to thread, " + "and configuration does not allow creation of non-transactional one here"); } return session; }
可以看出,总体的获取session的顺序是先查看spring的transaction manager, 其次JTA, 再次调用getCurrentSession,在getCurrentSession也会有JTA的回退,可能有重复的地方。