问题记录:


     工作环境是使用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 中获取sqlSessionHolderSqlSessionHolder holder = (SqlSessionHolder)TransactionSynchronizationManager.getResource(sessionFactory);//调用静态方法sessionHoler 判断是否存在符合要求的sqlSessionSqlSession session = sessionHolder(executorType, holder);// 判断当前sqlSessionHolder 中是否持有sqlSession (即当前操作是否在事务当中)if (session != null) {//如果持有sqlSesison 的引用,则直接获取return session;}if (LOGGER.isDebugEnabled()) {LOGGER.debug("Creating a new SqlSession");}//获取新的sqlSession 对象。这里由sessionFacory产生的defaultSqlSessionsession = 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<Map<Object, Object>> resources中 TransactionSynchronizationManager.bindResource(sessionFactory, holder);//将holder, sessionFactory的同步加入本地线程缓存中ThreadLocal<Set<TransactionSynchronization>> synchronizations TransactionSynchronizationManager.registerSynchronization(new SqlSessionSynchronization(holder, sessionFactory));//设置当前holder和当前事务同步 holder.setSynchronizedWithTransaction(true);//holder 引用次数+1holder.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 + "]");}//如果回话中,不存在事务,则直接关闭sessionsession.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作用
如果有侵权,马上删除

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

  1. Android 每次插入U盘自动创建了不需要的文件夹

    Android 11 每次插入U盘都自动创建了很多文件夹 (Podcast Ringtones Music Alarm Notification Pictures Movies Download DC ...

  2. mybatis学习(6):IntelliJ IDEA 如何创建一个普通的 Java 项目,及创建 Java 文件并运行

    一.创建 Java 项目:     1.打开 IDEA 软件,点击界面上的 Create New Project       2.出现以下界面,选中 Java,然后选择 JDK,最后点击 Next,进 ...

  3. mysql基础----mybatis的批量插入(一)

    这里面记录一下使用mybatis处理mysql的批量插入的问题,测试有可能不准.只愿世间风景千般万般熙攘过后,字里行间,人我两忘,相对无言. mybatis的批量插入 我们的测试主体类是springb ...

  4. mybatis 动态字段与表中不一样_mybatis创建一个或多个新用户 insert 字段和表名不确定时动态添加问题...

    创建用户: /** * 创建一个或多个新用户 insert 字段和表名不确定时动态添加 */ @Test public void createAccount() { String lineColumn ...

  5. mybatis中批量插入的两种方式(高效插入)

    MyBatis简介 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装.MyBatis可以使用 ...

  6. java应用程序接口批量访问_spring中使用mybatis实现批量插入的示例代码

    有3种实现方式:foreach,spring事务,以及ExecutorType.BATCH. 1. foreach方式 这种方式实际是对SQL语句进行拼接,生成一个长长的SQL,对很多变量进行绑定.如 ...

  7. mybatis使用map插入数据和Mybatis的核心配置文件LOG4J

    mybatis使用map插入数据 UserMapper.java包中写入 int addUser2(Map<Object,String> map); Usermapper.xml < ...

  8. 众所周知,static修饰的成员只实例化一次,而string类型每次赋值都会重新创建一个实例,那么用static修饰string呢?...

    string 类型每次实例化都会重新创建一个实例: 解释:string 类型重载了运算符 "=" ,每次 "=" 操作都是一次 "new". ...

  9. Mybatis 源码探究 (3)创建 SqlSessionFactory对象 执行sqlSession.getMapper()方法

    Mybatis 源码探究 (3)创建 SqlSessionFactory对象 时隔许久,终于又能接着来搞他啦.Mybatis 一起来探究吧. 先笑会再进入主题吧 开始啦 一.new SqlSessio ...

  10. Java连接HBASE数据库,创建一个表,删除一张表,修改表,输出插入,修改,数据删除,数据获取,显示表信息,过滤查询,分页查询,地理hash

    准备工作 1.创建Java的Maven项目 创建好的目录结构如下: 另外注意junit的版本,最好不要太高,最开始笔者使用的junit4.12的,发现运行的时候会报错.最后把Junit的版本改成4.7 ...

最新文章

  1. 快应用开发常见问题以及解决方案【持续更新】
  2. __cdecl __stdcall区别-转
  3. 如何异步提交表单 如何异步跨域提交表单
  4. Java程序员越来越多工资反而越高?
  5. JavaScript开发中几个常用知识点总结
  6. Java入门教程[9天快速入门JAVA]
  7. linux下文件删除的原理精华讲解(考试题答案系列)
  8. 用户需求说明书_「软件项目管理入门」(21) 需求调研和需求分析怎么做?
  9. echarts 柱状图 不要平均占据_扎心!工资跑不赢房价!平均薪资4127元/月,配得上8字头的九江?...
  10. Cloud Container Service experimentation
  11. Matlab绘制直方图、概率密度函数、累积分布函数
  12. 荣耀4a android art,华为荣耀4a root教程_荣耀4a获取root权限的方法
  13. SAR舰船检测数据集总结
  14. 盛大啊啊实打实倒萨的a
  15. The Development of Han Chinese’s Cuisine and Diet
  16. node.js入门之child_process子进程
  17. oracle查询结果加上总计
  18. frp代理工具流量分析
  19. 前端进阶(12) - css 的弱化与 js 的强化
  20. 微型计算机用什么显卡,流言终结者 侧板风扇真能给显卡降温吗

热门文章

  1. ECS 云助手,实现云上运维自动化
  2. 【速来报名】中国影响力巨大的开源峰会即将召开
  3. 【路径规划】基于matlab粒子群和遗传算法求解机器人栅格地图避障路径规划问题【含Matlab源码 202期】
  4. 【优化求解】基于matlab GUI模拟退火算法求解全局最大值最小值问题【含Matlab源码 1242期】
  5. 【疾病分类】基于matlab GUI模糊逻辑分类叶病严重程度分级系统【含Matlab源码 194期】
  6. ai人工智能图片修复_潜在客户追踪已中断,以及如何使用AI和自动化进行修复...
  7. mysql实际项目中使用多长时间_mysql常在项目中使用的语句总结
  8. python 执行文件的扩展名_python脚本文件的扩展名是什么
  9. apollo源码分析 感知_Scikit-Learn 源码研读 (第一期)项目结构介绍
  10. sping jdbc 链接mysql_java项目使用spring jdbc连接数据库