hiberante获取session的故事

    使用hibernate时,接口SessionFactory中获取session的方法挺多:openSession, getCurrentSession.如果使用spring,则有更多的用法。这些用法不免迷惑,他们分别是什么意思,哪些会自动提交呢?
    下面是我结合实例与源码的分析。

没有使用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的回退,可能有重复的地方。

猜你喜欢

转载自lingceng.iteye.com/blog/1693892