博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mybatis 为什么每次插入的时候总会创建一个SqlSession?
阅读量:6278 次
发布时间:2019-06-22

本文共 6316 字,大约阅读时间需要 21 分钟。

问题记录:


工作环境是使用spring boot,使用用的mybatis,在一次调试中。发现每一次插入一条 数据都会创建一个SqlSession。如图:

图1:

clipboard.png

问题可能的原因:


原因分析:#1 没有使用缓存

因为这个是插入,不是查询,所以这里不存在什么缓存的问题。

后来百度了一波,网上说是没有使用事务。

加上@Transactional


图2:

clipboard.png

发现“Creating a new SqlSession”这两个烦人的东西居然还在。

不管了,直接分析源码

直接分析源码,老子还不信了,搞不定你我还混什么:1.开启debug2.打上断点

图3:

clipboard.png

发现session为空

继续走...

clipboard.png

图2 #分析


public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {     //从从前线程的threadLocal 中获取sqlSessionHolder     SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);     //调用静态方法sessionHoler 判断是否存在符合要求的sqlSession     SqlSession session = sessionHolder(executorType, holder);     // 判断当前sqlSessionHolder 中是否持有sqlSession (即当前操作是否在事务当中)     if (session != null) {         //如果持有sqlSesison 的引用,则直接获取         return session;     }     if (LOGGER.isDebugEnabled()) {        LOGGER.debug("Creating a new SqlSession");     }    //获取新的sqlSession 对象。这里由sessionFacory产生的defaultSqlSession     session = sessionFactory.openSession(executorType);    //判断判断,当前是否存在事务,将sqlSession 绑定到sqlSessionHolder 中,并放到threadLoacl 当中    registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);    return session;}

图3 #分析

private static SqlSession sessionHolder(ExecutorType executorType, SqlSessionHolder holder) {     SqlSession session = null;     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");         }         //返回sqlSession          session = holder.getSqlSession();    }    return session;}

当然,这里还少了一个注册的方法,贴上:

private static void registerSessionHolder(SqlSessionFactory sessionFactory, ExecutorType executorType,PersistenceExceptionTranslator exceptionTranslator, SqlSession session) {        SqlSessionHolder holder;         //判断事务是否存在        if (TransactionSynchronizationManager.isSynchronizationActive()) {            Environment environment =  sessionFactory.getConfiguration().getEnvironment();            //加载环境变量,判断注册的事务管理器是否是SpringManagedTransaction,也就是Spring管理事务        if (environment.getTransactionFactory() instanceof SpringManagedTransactionFactory) {             if (LOGGER.isDebugEnabled()) {                 LOGGER.debug("Registering transaction synchronization for SqlSession [" + session + "]");              }             holder = new SqlSessionHolder(session, executorType, exceptionTranslator);              //如果当前回话处在事务当中,则将holder 绑定到ThreadLocal 中             //以sessionFactory为key,hodler为value,加入到TransactionSynchronizationManager管理的本地缓存ThreadLocal
> resources中 TransactionSynchronizationManager.bindResource(sessionFactory, holder); //将holder, sessionFactory的同步加入本地线程缓存中ThreadLocal
> synchronizations TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory)); //设置当前holder和当前事务同步 holder.setSynchronizedWithTransaction(true); //holder 引用次数+1 holder.requested(); } else { if (TransactionSynchronizationManager.getResource(environment.getDataSource()) == null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because DataSource is not transactional"); } } else { throw new TransientDataAccessResourceException( "SqlSessionFactory must be using a SpringManagedTransactionFactory in order to use Spring transaction synchronization"); } }} else { if (LOGGER.isDebugEnabled()) { LOGGER.debug("SqlSession [" + session + "] was not registered for synchronization because synchronization is not active"); }}

}

补充


  • sqlSessionHolder 继承了spring的ResourceHolderSupport

    public abstract class ResourceHolderSupport implements ResourceHolder {   //事务是否开启private boolean synchronizedWithTransaction = false;       private boolean rollbackOnly = false;   private Date deadline;   // 引用次数   private int referenceCount = 0;   private boolean isVoid = false;}

  • 在sqlSession 关闭session 的时候, 使用了工具了sqlSessionUtils的closeSqlSession 方法。sqlSessionHolder 也是做了判断,如果回话在事务当中,则减少引用次数,没有真实关闭session。如果回话不存在事务,则直接关闭session

    public static void closeSqlSession(SqlSession session, SqlSessionFactory sessionFactory) {     notNull(session, NO_SQL_SESSION_SPECIFIED);     notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);     SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);     //如果holder 中持有sqlSession 的引用,(即会话存在事务)     if ((holder != null) && (holder.getSqlSession() == session)) {        if (LOGGER.isDebugEnabled()) {            LOGGER.debug("Releasing transactional SqlSession [" + session + "]");        }        //每当一个sqlSession 执行完毕,则减少holder 持有引用的次数       holder.released();       } else {              if (LOGGER.isDebugEnabled()) {                  LOGGER.debug("Closing non transactional SqlSession [" + session + "]");              }              //如果回话中,不存在事务,则直接关闭session              session.close();        }}

    到了这一步,问题已经很明显了。


出现原因 与 分析


  • SqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory); 这一句:他从从前线程的threadLocal 中获取sqlSessionHolder。但是在在sqlSession 关闭session 的时候,sqlSessionHolder也是做了判断。如果会话在事务中,就减少引用次数,没有真实关闭session。如果会话不存在事务,则直接关闭session。也就是说,必须开启事务,但这个问题好像只是插入了一下,事务已经执行完成了,下一次插入的时候,由于上一个事务执行完成了, 如果不存在holder或没有被事务锁定,则会创建新的sqlSession,即 Creating a new SqlSession,通过sessionFactory.openSession()方法。如果会话不存在事务,就直接把session关闭了,同时,也减少了引用次数。

  • 换一句话来说:如果在插入的代码块中,再加上一个查询的代码,或者再插入一条数据的代码,这样就不会出现Creating a new SqlSession这个烦人的家伙。好了,祝大家好运!!!

引用:

如果有侵权,马上删除

转载地址:http://cogpa.baihongyu.com/

你可能感兴趣的文章
留德十年
查看>>
迷人的卡耐基说话术
查看>>
PHP导出table为xls出现乱码解决方法
查看>>
PHP问题 —— 丢失SESSION
查看>>
PyCairo指南--目录
查看>>
Java中Object类的equals()和hashCode()方法深入解析
查看>>
Linux/centos 下挂载硬盘的 方法
查看>>
数据库
查看>>
Vue------第二天(计算属性、侦听器、绑定Class、绑定Style)
查看>>
DICOM医学图像处理:WEB PACS初谈
查看>>
maven assembly plugin使用
查看>>
5: Calling Programs(Working with programs)
查看>>
QML学习笔记-入门篇(2)
查看>>
H3C设备网络组建和设置
查看>>
windows上一样使用linux命令--xshell登陆cygwin
查看>>
Linux查看系统配置常用命令
查看>>
大端 VS 小端
查看>>
dojo.mixin(混合进)、dojo.extend、dojo.declare
查看>>
虚拟机克隆之后无法正确获取静态ip
查看>>
Java 连接Kafka报错java.nio.channels.ClosedChannelExcep
查看>>