mybatis与spring的整合之SqlSessionTemplate

本文详细介绍了mybatis与spring整合过程中SqlSessionTemplate的使用,包括SqlSessionTemplate的作用、构造方法以及源码解析,特别是动态代理创建SqlSession的过程,并探讨了如何关闭SqlSession。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用 SqlSession

  • MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射语句,提交或回滚连接。最后,当不再需要它的时候, 你可以关闭session
  • 使用 MyBatis-Spring 之后, 你不再需要直接使用 SqlSessionFactory 了,因为你的 bean 可以通过一个线程安全的 SqlSession 来注入,基于 Spring 的事务配置来自动提交、回滚、关闭 session

SqlSessionTemplate

SqlSessionTemplateMyBatis-Spring 的核心。 这个类负责管理 MyBatisSqlSession, 调用 MyBatisSQL 方法, 翻译异常。 SqlSessionTemplate 是线程安全的, 可以被多个 DAO 所共享使用。

当调用 SQL 方法时, 包含从映射器 getMapper()方法返回的方法, SqlSessionTemplate 将会保证使用的 SqlSession 是和当前 Spring 的事务相关的。此外,它管理 session 的生命 周期,包含必要的关闭,提交或回滚操作。

SqlSessionTemplate 实现了 SqlSession 接口,这就是说,在代码中无需对 MyBatisSqlSession 进行替换。 SqlSessionTemplate 通常是被用来替代默认的 MyBatis 实现的 DefaultSqlSession , 因为模板可以参与到 Spring 的事务中并且被多个注入的映射器类所使 用时也是线程安全的。相同应用程序中两个类之间的转换可能会引起数据一致性的问题。

SqlSessionTemplate 对象可以使用 SqlSessionFactory 作为构造方法的参数来创建。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg index="0" ref="sqlSessionFactory" />
</bean>

源码

SqlSessionTemplate只有构造方法注入,没有setter方法

SqlSessionTemplate构造方法

一个入参

这是用得最多的构造方法,注入一个SqlSessionFactory
sqlSessionFactory.getConfiguration().getDefaultExecutorType(),获取Configuration默认的executorType,为SIMPLEConfiguration的初始化可详见mybatis与spring的整合之SqlSessionFactoryBean

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
    }

两个入参

一般执行批量操作时需要改变executorTypeBATCH
MyBatisExceptionTranslator是异常转换器

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
    this(sqlSessionFactory, executorType, new MyBatisExceptionTranslator(sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
}

三个入参

一般都是SqlSessionTemplate内部自己调用,完成属性赋值
其中最重要的是sqlSessionProxy,通过JDK动态 代理创建的session,执行sql语句是通过这个代理对象完成的

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
    Assert.notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    Assert.notNull(executorType, "Property 'executorType' is required");
    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession)Proxy.newProxyInstance(SqlSessionFactory.class.getClassLoader(), new Class[]{SqlSession.class}, new SqlSessionTemplate.SqlSessionInterceptor());
}

动态代理创建sqlSession

JDK动态代理需要有一个实现InvocationHandler接口的实现类
SqlSessionTemplate内部类SqlSessionInterceptor

private class SqlSessionInterceptor implements InvocationHandler {
    private SqlSessionInterceptor() {
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取SqlSession(这个SqlSession才是真正使用的,它不是线程安全的)
        //这个方法可以根据Spring的事物上下文来获取事物范围内的sqlSession
        SqlSession sqlSession = SqlSessionUtils.getSqlSession(SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);

        Object unwrapped;
        try {
            // 调用真实SqlSession的方法
            Object result = method.invoke(sqlSession, args);
            // 然后判断一下当前的sqlSession是否被Spring托管 如果未被Spring托管则自动commit
            if (!SqlSessionUtils.isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
                sqlSession.commit(true);
            }

            unwrapped = result;
        } catch (Throwable var11) {
            unwrapped = ExceptionUtil.unwrapThrowable(var11);
            if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
                // 异常关闭session
                SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
                sqlSession = null;
                Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException)unwrapped);
                if (translated != null) {
                    unwrapped = translated;
                }
            }

            throw (Throwable)unwrapped;
        } finally {
            if (sqlSession != null) {
                // 关闭sqlSession
				// 它会根据当前的sqlSession是否在Spring的事物上下文当中来执行具体的关闭动作
				// 如果sqlSession被Spring管理 则调用holder.released(); 使计数器-1
				// 否则才真正的关闭sqlSession
                SqlSessionUtils.closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
            }

        }

        return unwrapped;
    }
}

SqlSessionUtils.getSqlSession创建sqlSession

  1. 根据sqlSessionFactory当前线程对应的资源map中获取SqlSessionHolder
  2. 如果找不到,则根据执行类型构造一个新的sqlSession
  3. 将新构造的sqlSession注册进事务管理器中
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
    Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
    Assert.notNull(executorType, "No ExecutorType specified");
    // 根据sqlSessionFactory从当前线程对应的资源map中获取SqlSessionHolder
    // 当sqlSessionFactory创建了sqlSession,就会在事务管理器中添加一对映射
    // key为sqlSessionFactory,value为SqlSessionHolder,该类保存sqlSession及执行方式 
    SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
    // 获取session
    SqlSession session = sessionHolder(executorType, holder);
    if (session != null) {
        // session若不为空直接返回
        return session;
    } else {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Creating a new SqlSession");
        }
        // 如果找不到,则根据执行类型构造一个新的sqlSession
        session = sessionFactory.openSession(executorType);
        // 将sqlSession注册进事务管理器中
        registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
        return session;
    }
}
从事务管理器中寻找sqlSession
  1. 如果holder不为空,且和当前事务同步
  2. holder中取出sqlSession
  3. 增加引用数
private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {
    SqlSession session = null;
    // 如果holder不为空,且和当前事务同步 
    if (holder != null && holder.isSynchronizedWithTransaction()) {
        // hodler保存的执行类型和获取SqlSession的执行类型不一致,就会抛出异常
        // 也就是说在同一个事务中,执行类型不能变化,原因就是同一个事务中同一个sqlSessionFactory创建的sqlSession会被重用
        if (holder.getExecutorType() != executorType) {
            throw new TransientDataAccessResourceException("Cannot change the ExecutorType when there is an existing transaction");
        }
        // 增加该holder,也就是同一事务中同一个sqlSessionFactory创建的唯一sqlSession,其引用数增加,被使用的次数增加 
        holder.requested();
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Fetched SqlSession [" + holder.getSqlSession() + "] from current transaction");
        }
        // 返回该session
        session = holder.getSqlSession();
    }

    return session;
}
根据执行类型构造一个新的sqlSession

SessionFactory.openSession(executorType)

public SqlSession openSession(ExecutorType execType) {
    return this.openSessionFromDataSource(execType, (TransactionIsolationLevel)null, false);
}

第三个参数autoCommit默认为false,关闭自动提交

  1. configuration配置环境中获取事物工厂,默认为SpringManagedTransactionFactory,由Spring管理事务
  2. 根据数据源自动提交类型构建事物
  3. 根据事物执行器类型构建执行器
  4. 最后构建SqlSession,就是DefaultSqlSession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;

    DefaultSqlSession var8;
    try {
        Environment environment = this.configuration.getEnvironment();
        TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
        // 创建事物
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        // 构造执行器,底层是通过执行器完成sql语句
        Executor executor = this.configuration.newExecutor(tx, execType);
        var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
    } catch (Exception var12) {
        this.closeTransaction(tx);
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
    } finally {
        ErrorContext.instance().reset();
    }

    return var8;
}
将sqlSession注册进事务管理器中
  1. 判断同步是否激活,只要SpringTX被激活,就是true,比如@EnableTransactionManagement
  2. 判断配置的事物是否由spring管理
  3. 将当前sqlSession加载进事务管理的本地线程缓存中,构造成SqlSessionHolder
  4. holdersessionFactory的同步加入本地线程缓存中
  5. 设置当前holder和当前事务同步
  6. 增加引用数
private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {
    // 判断同步是否激活,只要SpringTX被激活,就是true 
    if (TransactionSynchronizationManager.isSynchronizationActive()) {
        // 加载环境变量,判断注册的事务管理器是否是SpringManagedTransaction,也就是Spring管理事务
        Environment environment = sessionFactory.getConfiguration().getEnvironment();
        if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");
            }
            // 如果是,则将sqlSession加载进事务管理的本地线程缓存中
            SqlSessionHolder holder = new SqlSessionHolder(session, executorType, exceptionTranslator);
            // 以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal<Map<Object, Object>> resources中
            TransactionSynchronizationManager.bindResource(sessionFactory, holder);
            // 将holder, sessionFactory的同步加入本地线程缓存中ThreadLocal<Set<TransactionSynchronization>> synchronizations 
            TransactionSynchronizationManager.registerSynchronization(new SqlSessionUtils.SqlSessionSynchronization(holder, sessionFactory));
            // 设置当前holder和当前事务同步 
            holder.setSynchronizedWithTransaction(true);
            // 增加引用数 
            holder.requested();
        } else {
            if (TransactionSynchronizationManager.getResource(environment.getDataSource()) != null) {
                throw new TransientDataAccessResourceException("SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization");
            }

            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional");
            }
        }
    } else if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active");
    }

}

method.invoke反射执行sql语句

这里找到一篇文章解释比较好的Mybatis的SqlSession运行原理

SqlSessionUtils.closeSqlSession关闭sqlSession

判断sqlSession是否被Spring事务管理

  • 是: 交由spring关闭
  • 不是: 自己关闭
public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {
    Assert.notNull(session, "No SqlSession specified");
    Assert.notNull(sessionFactory, "No SqlSessionFactory specified");
    // 其实下面就是判断session是否被Spring事务管理,如果管理就会得到holder  
    SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);
    if (holder != null && holder.getSqlSession() == session) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Releasing transactional SqlSession [" + session + "]");
        }
        // 这里释放的作用,不是关闭,只是减少一下引用数,因为后面可能会被复用 
        holder.released();
    } else {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Closing non transactional SqlSession [" + session + "]");
        }
        // 如果不是被spring管理,那么就不会被Spring去关闭回收,就需要自己close 
        session.close();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值