深入了解 Spring 管理 Hibernate 的 Session,事务

深入了解 Spring 管理 Hibernate 的 Session,事务

  Spring 整合 Hibernate 后会接管其 Session,事务,下面通过源码进行分析:

<bean id="sessionFactory"
        class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"
        p:dataSource-ref="dataSource"></bean>

  Spring 管理 session 和 Hibernate 管理 session 都建议把 session 和线程绑定,确保一个线程里只存在一个 session,避免了混乱,底层都是使用了 ThreadLocal 进行绑定,绑定的变量是一个 map,key 为 sessionFactory,value 为 session,因为 sessionFactory 在应用里是唯一的,那就确保了一条线程里面只存在一个 session;

  以上是Spring 配置文件的一部分,通过 LocalSessionFactoryBean 来注入 bean sessionFactory,该 sessionFactory 的成员属性 currentSessionContext 为 Spring 内部的 SpringSessionContext 对象,其获取 currentSession 的方式是这样的:

复制代码

    /**
     * Retrieve the Spring-managed Session for the current thread, if any.
     */
    @Override
    @SuppressWarnings("deprecation")
    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 = SessionFactoryUtils.getFlushMode(session);
                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");
        }
    }

复制代码

  其中,可以看到

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

  就是这里获得与线程绑定的 session,之前已经绑定了,通常是在 OpenSessionInViewFilter 和 OpenSessionInViewInterceptor 里面进行绑定的;

  

  以上是对 session 的管理,下面是对事务的管理:

  Spring 对事务管理有一个统一的接口,PlatformTransactionManager,不同的事务管理策略对应不同的该接口的实现类,以下介绍 Spring 整合 Hibernate 的进行事务管理的核心类 HibernateTransactionManager,其实现了接口 PlatformTransactionManager,继承了 ;

  

  以上方法,用于事务的管理,这个介绍下比较值得关注的,Spring 事务管理对于当前线程没有绑定 session 的处理,

  AbstractPlatformManager 的相关代码:

复制代码

    public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();

        // Cache debug flag to avoid repeated checks.
        boolean debugEnabled = logger.isDebugEnabled();

        if (definition == null) {
            // Use defaults if no transaction definition given.
            definition = new DefaultTransactionDefinition();
        }

        if (isExistingTransaction(transaction)) {
            // Existing transaction found -> check propagation behavior to find out how to behave.
            return handleExistingTransaction(definition, transaction, debugEnabled);
        }

        // Check definition settings for new transaction.
        if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
            throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
        }

        // No existing transaction found -> check propagation behavior to find out how to proceed.
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
            throw new IllegalTransactionStateException(
                    "No existing transaction found for transaction marked with propagation 'mandatory'");
        }
        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            SuspendedResourcesHolder suspendedResources = suspend(null);
            if (debugEnabled) {
                logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
            }
            try {
                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);
                return status;
            }
            catch (RuntimeException ex) {
                resume(null, suspendedResources);
                throw ex;
            }
            catch (Error err) {
                resume(null, suspendedResources);
                throw err;
            }
        }
        else {
            // Create "empty" transaction: no actual transaction, but potentially synchronization.
            if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
                logger.warn("Custom isolation level specified but no actual transaction initiated; " +
                        "isolation level will effectively be ignored: " + definition);
            }
            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
            return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
        }
    }

复制代码

  可以看到,调用了实现类 HibernateTransactionManager 的 doBegin() 方法:

复制代码

    protected void doBegin(Object transaction, TransactionDefinition definition) {
        HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;

        if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
            throw new IllegalTransactionStateException(
                    "Pre-bound JDBC Connection found! HibernateTransactionManager does not support " +
                    "running within DataSourceTransactionManager if told to manage the DataSource itself. " +
                    "It is recommended to use a single HibernateTransactionManager for all transactions " +
                    "on a single DataSource, no matter whether Hibernate or JDBC access.");
        }

        Session session = null;

        try {
            if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
                Interceptor entityInterceptor = getEntityInterceptor();
                Session newSession = (entityInterceptor != null ?
                        getSessionFactory().withOptions().interceptor(entityInterceptor).openSession() :
                        getSessionFactory().openSession());
                if (logger.isDebugEnabled()) {
                    logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
                }
                txObject.setSession(newSession);
            }

            session = txObject.getSessionHolder().getSession();

            if (this.prepareConnection && isSameConnectionForEntireSession(session)) {
                // We're allowed to change the transaction settings of the JDBC Connection.
                if (logger.isDebugEnabled()) {
                    logger.debug("Preparing JDBC Connection of Hibernate Session [" + session + "]");
                }
                Connection con = ((SessionImplementor) session).connection();
                Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
                txObject.setPreviousIsolationLevel(previousIsolationLevel);
                if (this.allowResultAccessAfterCompletion && !txObject.isNewSession()) {
                    int currentHoldability = con.getHoldability();
                    if (currentHoldability != ResultSet.HOLD_CURSORS_OVER_COMMIT) {
                        txObject.setPreviousHoldability(currentHoldability);
                        con.setHoldability(ResultSet.HOLD_CURSORS_OVER_COMMIT);
                    }
                }
            }
            else {
                // Not allowed to change the transaction settings of the JDBC Connection.
                if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
                    // We should set a specific isolation level but are not allowed to...
                    throw new InvalidIsolationLevelException(
                            "HibernateTransactionManager is not allowed to support custom isolation levels: " +
                            "make sure that its 'prepareConnection' flag is on (the default) and that the " +
                            "Hibernate connection release mode is set to 'on_close' (the default for JDBC).");
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Not preparing JDBC Connection of Hibernate Session [" + session + "]");
                }
            }

            if (definition.isReadOnly() && txObject.isNewSession()) {
                // Just set to MANUAL in case of a new Session for this transaction.
                session.setFlushMode(FlushMode.MANUAL);
            }

            if (!definition.isReadOnly() && !txObject.isNewSession()) {
                // We need AUTO or COMMIT for a non-read-only transaction.
                FlushMode flushMode = SessionFactoryUtils.getFlushMode(session);
                if (FlushMode.MANUAL.equals(flushMode)) {
                    session.setFlushMode(FlushMode.AUTO);
                    txObject.getSessionHolder().setPreviousFlushMode(flushMode);
                }
            }

            Transaction hibTx;

            // Register transaction timeout.
            int timeout = determineTimeout(definition);
            if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
                // Use Hibernate's own transaction timeout mechanism on Hibernate 3.1+
                // Applies to all statements, also to inserts, updates and deletes!
                hibTx = session.getTransaction();
                hibTx.setTimeout(timeout);
                hibTx.begin();
            }
            else {
                // Open a plain Hibernate transaction without specified timeout.
                hibTx = session.beginTransaction();
            }

            // Add the Hibernate transaction to the session holder.
            txObject.getSessionHolder().setTransaction(hibTx);

            // Register the Hibernate Session's JDBC Connection for the DataSource, if set.
            if (getDataSource() != null) {
                Connection con = ((SessionImplementor) session).connection();
                ConnectionHolder conHolder = new ConnectionHolder(con);
                if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
                    conHolder.setTimeoutInSeconds(timeout);
                }
                if (logger.isDebugEnabled()) {
                    logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]");
                }
                TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
                txObject.setConnectionHolder(conHolder);
            }

            // Bind the session holder to the thread.
            if (txObject.isNewSessionHolder()) {
                TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
            }
            txObject.getSessionHolder().setSynchronizedWithTransaction(true);
        }

        catch (Throwable ex) {
            if (txObject.isNewSession()) {
                try {
                    if (session.getTransaction().getStatus() == TransactionStatus.ACTIVE) {
                        session.getTransaction().rollback();
                    }
                }
                catch (Throwable ex2) {
                    logger.debug("Could not rollback Session after failed transaction begin", ex);
                }
                finally {
                    SessionFactoryUtils.closeSession(session);
                    txObject.setSessionHolder(null);
                }
            }
            throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
        }
    }

复制代码

  其中,

复制代码

            if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
                Interceptor entityInterceptor = getEntityInterceptor();
                Session newSession = (entityInterceptor != null ?
                        getSessionFactory().withOptions().interceptor(entityInterceptor).openSession() :
                        getSessionFactory().openSession());
                if (logger.isDebugEnabled()) {
                    logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
                }
                txObject.setSession(newSession);
            }

复制代码

  如果 session 为 null,就会调用 openSession() 方法获取新的 session,并与当前线程绑定,然后在后面的处理中,会将其与当前线程解除绑定,以下是相关的代码(位于 HibernateTransactionManager):

复制代码

    protected void doCleanupAfterCompletion(Object transaction) {
        HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;

        // Remove the session holder from the thread.
        if (txObject.isNewSessionHolder()) {
            TransactionSynchronizationManager.unbindResource(getSessionFactory());
        }

        // Remove the JDBC connection holder from the thread, if exposed.
        if (getDataSource() != null) {
            TransactionSynchronizationManager.unbindResource(getDataSource());
        }

        Session session = txObject.getSessionHolder().getSession();
        if (this.prepareConnection && isPhysicallyConnected(session)) {
            // We're running with connection release mode "on_close": We're able to reset
            // the isolation level and/or read-only flag of the JDBC Connection here.
            // Else, we need to rely on the connection pool to perform proper cleanup.
            try {
                Connection con = ((SessionImplementor) session).connection();
                Integer previousHoldability = txObject.getPreviousHoldability();
                if (previousHoldability != null) {
                    con.setHoldability(previousHoldability);
                }
                DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
            }
            catch (HibernateException ex) {
                logger.debug("Could not access JDBC Connection of Hibernate Session", ex);
            }
            catch (Throwable ex) {
                logger.debug("Could not reset JDBC Connection after transaction", ex);
            }
        }

        if (txObject.isNewSession()) {
            if (logger.isDebugEnabled()) {
                logger.debug("Closing Hibernate Session [" + session + "] after transaction");
            }
            SessionFactoryUtils.closeSession(session);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Not closing pre-bound Hibernate Session [" + session + "] after transaction");
            }
            if (txObject.getSessionHolder().getPreviousFlushMode() != null) {
                session.setFlushMode(txObject.getSessionHolder().getPreviousFlushMode());
            }
            if (!this.allowResultAccessAfterCompletion && !this.hibernateManagedSession) {
                disconnectOnCompletion(session);
            }
        }
        txObject.getSessionHolder().clear();
    }

复制代码

  可以看到,其中

        // Remove the session holder from the thread.
        if (txObject.isNewSessionHolder()) {
            TransactionSynchronizationManager.unbindResource(getSessionFactory());
        }

  把 session 与当前线程解除绑定,其中,doBegin() 中调用 openSession() 获得的 session 是 “new” 的Session,这个 new 就是 Spring 用于判定 session 是否由 HibernateTransactionManager 创建的 session;

  以上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值