资讯专栏INFORMATION COLUMN

mybatis 为什么每次插入的时候总会创建一个SqlSession?

sixgo / 2192人阅读

摘要:问题记录工作环境是使用使用用的,在一次调试中。发现每一次插入一条数据都会创建一个。如图图问题可能的原因原因分析没有使用缓存因为这个是插入,不是查询,所以这里不存在什么缓存的问题。但是在在关闭的时候也是做了判断。

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

图1:

问题可能的原因: 原因分析:#1 没有使用缓存
因为这个是插入,不是查询,所以这里不存在什么缓存的问题。
后来百度了一波,网上说是没有使用事务。
加上@Transactional

图2:

发现“Creating a new SqlSession”这两个烦人的东西居然还在。
不管了,直接分析源码
直接分析源码,老子还不信了,搞不定你我还混什么:
1.开启debug
2.打上断点

图3:

发现session为空

继续走... 图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这个烦人的家伙。好了,祝大家好运!!!

引用:

SqlSessionHolder作用
如果有侵权,马上删除

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69739.html

相关文章

  • 通过项目逐步深入了解Mybatis(四)

    摘要:相关阅读通过项目逐步深入了解一通过项目逐步深入了解二通过项目逐步深入了解三本项目所有代码及文档都托管在地址延迟加载什么是延迟加载可以实现高级映射使用实现一对一及一对多映射,具备延迟加载功能。一级缓存是级别的缓存。 相关阅读: 1、通过项目逐步深入了解Mybatis 2、通过项目逐步深入了解Mybatis 3、通过项目逐步深入了解Mybatis 本项目所有代码及文档都托管在 Github...

    kuangcaibao 评论0 收藏0
  • Mybatis入门看这一篇就够了

    摘要:什么是本是的一个开源项目年这个项目由迁移到了,并且改名为。如下的代码,如果有多个条件的话,那么拼接起来很容易出错查询语句根据是否为来判断是否是条件查询。而如果我们使用的话,就可以免去查询助手类了。 什么是MyBatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,...

    livem 评论0 收藏0
  • MyBatis知识点整理

    摘要:得到用户信息,将用户信息存储到一级缓存中。如果中间去执行操作执行插入更新删除,则会清空中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。 基础: 1、 概念:Java当中的一个持久层框架。2、 特点、优势:(1)把java代码和SQL代码做了一个完全分离。(2)良好支持复杂对象的映射(输入映射、输出映射)(3)使用动态SQL,可以预防SQL注入。3、 ...

    zhangqh 评论0 收藏0
  • Mybatis学习笔记(一)——基本CRUD操作

    摘要:将语句硬编码到代码中,修改语句需要重新编译代码设想使用配置文件配置。从结果集中遍历数据的时候存在硬编码。表示一个拼接符号,会引用注入,所以不建议使用。和表示查询出一条记录进行映射。 MyBatis是什么 mybatis是托管在github上的ORM框架,让程序员将主要精力放在SQL上,通过mybatis提供映射方式,自由灵活(SQL的可定制性较高,半自动化)生成满足需求的SQL语句。m...

    evin2016 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<