MyBatis 也是一个极其简单的框架,源码设计的非常巧妙。建议大家多读一读。
今天我就给大家从源码角度给大家说一说 MyBatis 中 SqlSession 为什么是线程安全的?
首先,你翻开 SqlSession 的源码,你会发现它只是一个接口。我们通过 MyBatis 操作数据库,实际上就是通过 SqlSession 获取一个 JDBC 链接,然后操作数据库。但 SqlSession 是一个接口,怎么操作?
SqlSession 虽然是一个接口,但它有好几个实现类。比如:DefaultSqlSession、SqlSessionManager、SqlSessionTemplate 等。在 Mac 系统的 idea 中按 alt + command + b 快捷键即可查看到这个接口的所有实现类。
我们先来看 DefaultSqlSession,这个类上有一个很明显的注释:“Note that this class is not Thread-Safe.”
DefaultSqlSession 不是线程安全的。而根据它的名字,我们就应该知道 MyBatis 在默认情况下,使用的就是 DefaultSqlSession。既然 DefaultSqlSession 不是线程安全的,所以你在使用它的时候,切记不能把它设为单例。
那么有没有线程安全的实现呢?当然有,不是还有另外两个吗?所以,我们来看看 SqlSessionTemplate。
注释的非常的明确,它是线程安全的。并且还告诉了我们如何在配置文件中使用它。
你注意总结哈,你在面试中被问题的哪些面试题,其实都在工作中有遇到。你在配置 SqlSessionTemplate 的时候,可能从来都没有想过它是否是线程安全的。你甚至都不想点进去看一看,你当初对我爱理不理,现在血亏了吧。
那么现在问题又来了,SqlSessionTemplate 是如何保证线程安全的呢?
我们在实际开发过程中,无论是多个 Mapper 或者是 Dao 使用一个 SqlSessionTemplate,还是一个 Mapper 使用一个 SqlSessionTemplate,SqlSessionTemplate 都是对应一个 SqlSession 对象,当多个 web 线程调用同一个 Mapper 时,它们使用的是同一个 SqlSessionTemplate,也就是同一个 SqlSession,那么它是如何确保线程安全的呢?
顺着源码往下看,在它的构造函数中有一个 sqlSessionProxy。
它为 SqlSession 生成了一个代理类。重点就是这个 SqlSessionInterceptor 类。
SqlSessionInterceptor 是 SqlSessionTemplate 的一个内部类。重点我们看下它的 invoke 方法。
getSqlSession() 方法可以根据 Spring 的事物上下文来获取事物范围内的 sqlSession。
getSqlSession() 方法也很好理解。TransactionSynchronizationManager 根据 sqlSessionFactory 从当前线程对应的资源 map 中获取 SqlSessionHolder,当 sqlSessionFactory 创建了 sqlSession,就会在事务管理器中添加一对映射:key 为 sqlSessionFactory,value 为 SqlSessionHolder,该类保存 sqlSession 及执行方式。
然后从 SqlSessionHolder 中提取 SqlSession 对象。这时如果 SqlSession 不为空,则返回。如果为空则通过 SqlSessionFactory 重新 openSession 一个 SqlSession 对象。然后将新创建的 SqlSession 对象包装成 SqlSessionHolder 注册到TransactionSynchronizationManager 中。
上面的 sessionFactory.openSession 其实就是 new DefaultSqlSession 对象。
剩下的一个 SqlSessionManager 也很简单。看名字就知道,它是一个 SqlSession 管理器。
它的构造方法虽然是 private 的。但是它提供了多个 newInstance 方法。最终也是通过 sqlSessionFactory.openSession() 获得一个 SqlSession。然后和当前线程绑定,放入 ThreadLocal<SqlSession> 中。最终也就是不同的线程,有不同的 SqlSession。
它的内部类 SqlSessionInterceptor 和上面 SqlSessionTemplate 类的内部类 SqlSessionInterceptor 类似。
这个 openSession 执行的就是 sessionFactory.openSession,其实就是 new DefaultSqlSession 对象。由此可见,SqlSessionManager 通过获得的 SqlSession 也是线程安全的。
总结
-
DefaultSqlSession 非线程安全的,切记不要使用单例模式
-
SqlSessionTemplate 和 SqlSessionManager 都使用了装饰器模式
-
SqlSessionTemplate 和 SqlSessionManager 都持有 SqlSessionFactory
-
SqlSessionTemplate 和 SqlSessionManager 都是线程安全的
-
SqlSessionTemplate 和 SqlSessionManager 都能增强 SqlSession 生命周期管理
-
SqlSessionTemplate 与 SqlSessionManager 实现 SqlSession 生命周期管理的方式相同
-
SqlSessionManager 由使用者决定是否共用 SqlSession
-
SqlSessionTemplate 由 Spring 事务管理器决定是否共用 SqlSession。事务内部共用 SqlSession,非事务不共用 SqlSession。统一个事务,内部存在新开线程的不共用 SqlSession。
最后一张不清晰的关于 MyBatis 源码、架构、原理等教程的思维导图送个大家。原图和文件太大了,这里只截图了十分之一。如果需要高清的,请加微信号:xmtxtt,备注脑图,我发给大家!
本文摘录自我的个人知识星球。让本该造火箭的你,不再拧螺丝!
: » MyBatis 中 SqlSession 是线程安全的吗?
原创文章,作者:1402239773,如若转载,请注明出处:https://blog.ytso.com/252060.html