SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的

https://www.toutiao.com/a6529269074583618061/?tt_from=mobile_qq&utm_campaign=client_share×tamp=1520396400&app=news_article&utm_source=mobile_qq&iid=11417535126&utm_medium=toutiao_android



一、DefaultSqlSession的线程不安全性

在MyBatis架构中SqlSession是提供给外层调用的顶层接口,实现类有:DefaultSqlSession、SqlSessionManager以及mybatis-spring提供的实现SqlSessionTemplate。默认的实现类为DefaultSqlSession如。类图结构如下所示:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第1张图片

对于MyBatis提供的原生实现类来说,用的最多的就是DefaultSqlSession,但我们知道DefaultSqlSession这个类不是线程安全的!如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第2张图片

二、SqlSessionTemplate是如何使用DefaultSqlSession的

而在我们开发的时候肯定会用到Spring,也会用到mybatis-spring框架,在使用MyBatis与Spring集成的时候我们会用到了SqlSessionTemplate这个类,例如下边的配置,注入一个单例的SqlSessionTemplate对象:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的

SqlSessionTemplate的源代码注释如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第3张图片

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第4张图片

通过源码我们何以看到 SqlSessionTemplate实现了SqlSession接口,也就是说我们可以使用SqlSessionTemplate来代理以往的DefaultSqlSession完成对数据库的操作,但是DefaultSqlSession这个类不是线程安全的,所以DefaultSqlSession这个类不可以被设置成单例模式的。

如果是常规开发模式的话,我们每次在使用DefaultSqlSession的时候都从SqlSessionFactory当中获取一个就可以了。但是与Spring集成以后,Spring提供了一个全局唯一的SqlSessionTemplate对象来完成DefaultSqlSession的功能,问题就是:无论是多个Dao使用一个SqlSessionTemplate,还是一个Dao使用一个SqlSessionTemplate,SqlSessionTemplate都是对应一个sqlSession对象,当多个web线程调用同一个Dao时,它们使用的是同一个SqlSessionTemplate,也就是同一个SqlSession,那么它是如何确保线程安全的呢?让我们一起来分析一下:

三、SqlSessionTemplate是如何保证DefaultSqlSession线程安全的

(1)首先,通过如下代码创建代理类,表示创建SqlSessionFactory的代理类的实例,该代理类实现SqlSession接口,定义了方法拦截器,如果调用代理类实例中实现SqlSession接口定义的方法,该调用则被导向SqlSessionInterceptor的invoke方法(代理对象的InvocationHandler就是SqlSessionInterceptor,如果把它命名为SqlSessionInvocationHandler则更好理解!)

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第5张图片

核心代码就在 SqlSessionInterceptor的invoke方法当中。

一、DefaultSqlSession的线程不安全性

在MyBatis架构中SqlSession是提供给外层调用的顶层接口,实现类有:DefaultSqlSession、SqlSessionManager以及mybatis-spring提供的实现SqlSessionTemplate。默认的实现类为DefaultSqlSession如。类图结构如下所示:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第6张图片

对于MyBatis提供的原生实现类来说,用的最多的就是DefaultSqlSession,但我们知道DefaultSqlSession这个类不是线程安全的!如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第7张图片

二、SqlSessionTemplate是如何使用DefaultSqlSession的

而在我们开发的时候肯定会用到Spring,也会用到mybatis-spring框架,在使用MyBatis与Spring集成的时候我们会用到了SqlSessionTemplate这个类,例如下边的配置,注入一个单例的SqlSessionTemplate对象:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的

SqlSessionTemplate的源代码注释如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第8张图片

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第9张图片

通过源码我们何以看到 SqlSessionTemplate实现了SqlSession接口,也就是说我们可以使用SqlSessionTemplate来代理以往的DefaultSqlSession完成对数据库的操作,但是DefaultSqlSession这个类不是线程安全的,所以DefaultSqlSession这个类不可以被设置成单例模式的。

如果是常规开发模式的话,我们每次在使用DefaultSqlSession的时候都从SqlSessionFactory当中获取一个就可以了。但是与Spring集成以后,Spring提供了一个全局唯一的SqlSessionTemplate对象来完成DefaultSqlSession的功能,问题就是:无论是多个Dao使用一个SqlSessionTemplate,还是一个Dao使用一个SqlSessionTemplate,SqlSessionTemplate都是对应一个sqlSession对象,当多个web线程调用同一个Dao时,它们使用的是同一个SqlSessionTemplate,也就是同一个SqlSession,那么它是如何确保线程安全的呢?让我们一起来分析一下:

三、SqlSessionTemplate是如何保证DefaultSqlSession线程安全的

(1)首先,通过如下代码创建代理类,表示创建SqlSessionFactory的代理类的实例,该代理类实现SqlSession接口,定义了方法拦截器,如果调用代理类实例中实现SqlSession接口定义的方法,该调用则被导向SqlSessionInterceptor的invoke方法(代理对象的InvocationHandler就是SqlSessionInterceptor,如果把它命名为SqlSessionInvocationHandler则更好理解!)

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第10张图片

核心代码就在 SqlSessionInterceptor的invoke方法当中。

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第11张图片

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第12张图片

在上面的invoke方法当中使用了两个工具方法分别是:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的

那么这两个方法又是如何与Spring的事物进行关联的呢?

1、getSqlSession方法如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第13张图片

2、closeSqlSession方法如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第14张图片

大致的分析到此为止,可能有些许不够顺畅,不过:纸上得来终觉浅,绝知此事要躬行!还希望小伙伴打开自己的编译器,找到此处的代码,认真走一遍流程!

其实通过上面的代码我们可以看出Mybatis在很多地方都用到了代理模式,代理模式可以说是一种经典模式,其实不紧紧在这个地方用到了代理模式,Spring的事物、AOP、Mybatis数据库连接池技术、MyBatis的核心原理(如何在只有接口没有实现类的情况下完成数据库的操作!)等技术都使用了代理技术。

四、SqlSessionManager又是什么鬼?

上述说了SqlSession的实现还有一个SqlSessionManager,那么SqlSessionManager到底是什么个东西哪?且看定义如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第15张图片

你可能会发现SqlSessionManager的构造方法竟然是private的,那我们怎么创建这个对象哪?其实SqlSessionManager创建对象是通过newInstance的方法创建对象的,但需要注意的是他虽然有私有的构造方法,并且提供给我们了一个公有的newInstance方法,但它并不是一个单例模式!

newInstance有很多重载的方法,如下所示:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第16张图片

SqlSessionManager的openSession方法及其重载的方法是直接通过调用其中底层封装的SqlSessionFactory对象的openSession方法来创建SqlSession对象的,重载方法如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第17张图片

SqlSessionManager中实现了SqlSession接口中的方法,例如:select、update等,都是直接调用sqlSessionProxy代理对象中相应的方法。在创建该代理对像的时候使用的InvocationHandler对象是SqlSessionInterceptor,他是定义在SqlSessionManager的一个内部类,其定义如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第18张图片

五、总结

综上所述,我们应该大致了解了DefaultSqlSession和SqlSessionManager之间的区别:

1、DefaultSqlSession的内部没有提供像SqlSessionManager一样通过ThreadLocal的方式来保证线程的安全性;

2、SqlSessionManager是通过localSqlSession这个ThreadLocal变量,记录与当前线程绑定的SqlSession对象,供当前线程循环使用,从而避免在同一个线程多次创建SqlSession对象造成的性能损耗;

3、DefaultSqlSession不是线程安全的,我们在进行原生开发的时候,需要每次为一个操作都创建一个SqlSession对象,其性能可想而知;

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第19张图片

在上面的invoke方法当中使用了两个工具方法分别是:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的

那么这两个方法又是如何与Spring的事物进行关联的呢?

1、getSqlSession方法如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第20张图片

2、closeSqlSession方法如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第21张图片

大致的分析到此为止,可能有些许不够顺畅,不过:纸上得来终觉浅,绝知此事要躬行!还希望小伙伴打开自己的编译器,找到此处的代码,认真走一遍流程!

其实通过上面的代码我们可以看出Mybatis在很多地方都用到了代理模式,代理模式可以说是一种经典模式,其实不紧紧在这个地方用到了代理模式,Spring的事物、AOP、Mybatis数据库连接池技术、MyBatis的核心原理(如何在只有接口没有实现类的情况下完成数据库的操作!)等技术都使用了代理技术。

四、SqlSessionManager又是什么鬼?

上述说了SqlSession的实现还有一个SqlSessionManager,那么SqlSessionManager到底是什么个东西哪?且看定义如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第22张图片

你可能会发现SqlSessionManager的构造方法竟然是private的,那我们怎么创建这个对象哪?其实SqlSessionManager创建对象是通过newInstance的方法创建对象的,但需要注意的是他虽然有私有的构造方法,并且提供给我们了一个公有的newInstance方法,但它并不是一个单例模式!

newInstance有很多重载的方法,如下所示:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第23张图片

SqlSessionManager的openSession方法及其重载的方法是直接通过调用其中底层封装的SqlSessionFactory对象的openSession方法来创建SqlSession对象的,重载方法如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第24张图片

SqlSessionManager中实现了SqlSession接口中的方法,例如:select、update等,都是直接调用sqlSessionProxy代理对象中相应的方法。在创建该代理对像的时候使用的InvocationHandler对象是SqlSessionInterceptor,他是定义在SqlSessionManager的一个内部类,其定义如下:

SqlSessionTemplate是如何保证MyBatis中SqlSession的线程安全的_第25张图片

五、总结

综上所述,我们应该大致了解了DefaultSqlSession和SqlSessionManager之间的区别:

1、DefaultSqlSession的内部没有提供像SqlSessionManager一样通过ThreadLocal的方式来保证线程的安全性;

2、SqlSessionManager是通过localSqlSession这个ThreadLocal变量,记录与当前线程绑定的SqlSession对象,供当前线程循环使用,从而避免在同一个线程多次创建SqlSession对象造成的性能损耗;

3、DefaultSqlSession不是线程安全的,我们在进行原生开发的时候,需要每次为一个操作都创建一个SqlSession对象,其性能可想而知;


你可能感兴趣的:(mybatis)