IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot+mybatis SqlSessionTemplate线程安全 -> 正文阅读

[Java知识库]springboot+mybatis SqlSessionTemplate线程安全

集成mybatis

springboot集成mybatis有三种方式:

  • 一、最简单的方式就是使用MyBatis官方提供的 mybatis-spring-boot-starter。
  • 二、另一种使用spring-mybatis包的xml配置方式,这样需要自己写一些代码,但可以更灵活的控制mybatis的各项配置。
  • 三、使用引入xml配置(淘汰)

关系图

mybatis session关键类关系图如下:

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

线程不安全

DefaultSqlSession与SqlSessionManager解析

在Mybatis中SqlSession默认有DefaultSqlSession和SqlSessionManager两个实现类

DefaultSqlSession是真正的实现类调用Executor,但不是线程安全的。

? ? ? ?Mybatis又实现了对SqlSession和SQLSessionFactory的封装类SqlSessionManager,线程安全并通过localSqlSession实现复用从而提高性能。

? ? ? ? ? ? private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal();
? ? ? ?SqlSessionManager通过SqlSessionInterceptor实现对DefaultSqlSession代理调用。

private class SqlSessionInterceptor implements InvocationHandler {
? ? ? ? public SqlSessionInterceptor() {
? ? ? ? }
?
? ? ? ? public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
? ? ? ? ? ? //由调用者决定当前线程是否复用 SqlSession?
? ? ? ? ? ? SqlSession sqlSession = (SqlSession)SqlSessionManager.this.localSqlSession.get();
? ? ? ? ? ? if (sqlSession != null) {
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? return method.invoke(sqlSession, args);
? ? ? ? ? ? ? ? } catch (Throwable var12) {
? ? ? ? ? ? ? ? ? ? throw ExceptionUtil.unwrapThrowable(var12);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? //如果不复用,则每次调用都新建 SqlSession 并使用后销毁
? ? ? ? ? ? ? ? SqlSession autoSqlSession = SqlSessionManager.this.openSession();
?
? ? ? ? ? ? ? ? Object var7;
? ? ? ? ? ? ? ? try {
? ? ? ? ? ? ? ? ? ? Object result = method.invoke(autoSqlSession, args);
? ? ? ? ? ? ? ? ? ? autoSqlSession.commit();
? ? ? ? ? ? ? ? ? ? var7 = result;
? ? ? ? ? ? ? ? } catch (Throwable var13) {
? ? ? ? ? ? ? ? ? ? autoSqlSession.rollback();
? ? ? ? ? ? ? ? ? ? throw ExceptionUtil.unwrapThrowable(var13);
? ? ? ? ? ? ? ? } finally {
? ? ? ? ? ? ? ? ? ? autoSqlSession.close();
? ? ? ? ? ? ? ? }
?
? ? ? ? ? ? ? ? return var7;
? ? ? ? ? ? }
? ? ? ? }
? ? }

spring-mybatis方式

? ? ? ?这里主要介绍spring-mybatis方式,虽然有些多余,有简单的不用何必要用这种,里面还是有些细节对自己还是很有帮助的。纯粹是个人爱好。

? ? ? ?这种方式和一般的用法比较接近。需要添加mybatis依赖和mybatis-spring依赖,然后创建一个MyBatisConfig配置类:

首先配置一个数据源

import org.apache.tomcat.jdbc.pool.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
?
@Configuration
public class DataSourceConfig {
? ? @Bean?
? ? public DataSource dataSource(){?
? ? ? ? DataSource dataSource = new DataSource();?
? ? ? ? dataSource.setDriverClassName("com.mysql.jdbc.Driver");
? ? ? ? dataSource.setUrl("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8");
? ? ? ? dataSource.setUsername("test");
? ? ? ? dataSource.setPassword("123456");
? ? ? ? return dataSource;
? ? }
?
}

? ? ? ?上面的类代表此类为配置类,代表需要注入的bean,使用代码的方式传入值到对象。当然也可以通过application.properties配置文件。

@Configuration
@EnableTransactionManagement //开启事务管理
public class MyBatisConfig implements TransactionManagementConfigurer {
?
? ? @Autowired
? ? DataSource dataSource;
?
? ? @Bean(name = "sqlSessionFactory")
? ? public SqlSessionFactory sqlSessionFactoryBean() {
? ? ? ? SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
? ? ? ? bean.setDataSource(dataSource);
? ? ? ? bean.setTypeAliasesPackage("tk.mybatis.springboot.model");
?
? ? ? ? //分页插件
? ? ? ? PageHelper pageHelper = new PageHelper();
? ? ? ? Properties properties = new Properties();
? ? ? ? properties.setProperty("reasonable", "true");
? ? ? ? properties.setProperty("supportMethodsArguments", "true");
? ? ? ? properties.setProperty("returnPageInfo", "check");
? ? ? ? properties.setProperty("params", "count=countSql");
? ? ? ? pageHelper.setProperties(properties);
?
? ? ? ? //添加插件
? ? ? ? bean.setPlugins(new Interceptor[]{pageHelper});
?
? ? ? ? //添加mapper操作数据库XML目录
? ? ? ? ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
? ? ? ? try {
? ? ? ? ? ? bean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
? ? ? ? ? ? return bean.getObject();
? ? ? ? } catch (Exception e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? throw new RuntimeException(e);
? ? ? ? }
? ? }
? ? /*spring通过SqlSessionTemplate对象去操作sqlsession语句*/
? ? @Bean
? ? public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
? ? ? ? return new SqlSessionTemplate(sqlSessionFactory);
? ? }
? ? /*配置事务管理器*/
? ? @Bean
? ? @Override
? ? public PlatformTransactionManager annotationDrivenTransactionManager() {
? ? ? ? return new DataSourceTransactionManager(dataSource);
? ? }
}

? ? ? ? 上面的配置我们主要通过mybatis的SqlSessionFactoryBean来获取SqlsessionFactory工厂类,通过工厂类获取SqlSessionTemplate对象操作sqlsession语句,值得注意的是SqlSessionTemplate是线程安全的

? ? ? ? 在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射语句,提交或回滚连接,最后,当不再需要它的时候, 你可以关闭 session。

? ? ? ? SqlSessionTemplate 是 MyBatis-Spring 的核心。 这个类负责管理 MyBatis 的 SqlSession, 调用 MyBatis 的 SQL 方法, 翻译异常。 SqlSessionTemplate 是线程安全的, 可以被多个 DAO 所共享使用。

? ? ? ? SqlSessionTemplate 实现了 SqlSession 接口,这就是说,在代码中无需对 MyBatis 的 SqlSession 进行替换。 SqlSessionTemplate 通常是被用来替代默认的 MyBatis 实现的 DefaultSqlSession , 因为模板可以参与到 Spring 的事务中并且被多个注入的映射器类所使 用时也是线程安全的。相同应用程序中两个类之间的转换可能会引起数据一致性的问题。

? ? ? ?SqlSessionTemplate 对象可以使用 SqlSessionFactory 作为构造方法的参数来创建。

è?é?????è?°

?sqlsessionTemplate构造方法有四个参数:

  • SqlSessionFactory sessionFactory,
  • ExecutorType executorType,
  • PersistenceExceptionTranslator exceptionTranslator

? ? ? ?从上面的源码分析,保证线程安全的重点就落在SqlSessionInterceptor()类上了,我们接着找SqlSessionManager又是什么鬼?

è?é?????è?°

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

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

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


总结

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

  • 1、DefaultSqlSession的内部没有提供像SqlSessionManager一样通过ThreadLocal的方式来保证线程的安全性;
  • 2、SqlSessionManager是通过localSqlSession这个ThreadLocal变量,记录与当前线程绑定的SqlSession对象,供当前线程循环使用,从而避免在同一个线程多次创建SqlSession对象造成的性能损耗;
  • 3、DefaultSqlSession不是线程安全的,我们在进行原生开发的时候,需要每次为一个操作都创建一个SqlSession对象,其性能可想而知;

扩展

那么问题来了:

  • 1、为什么mybatis-spring框架中不直接使用线程安全的SqlSessionManager(SqlSessionFactory它是线程安全的)而是使用DefaultSqlSession这个线程不安全的类,并通过动态代理的方式来保证DefaultSqlSession操作的线程安全性哪?
  • 2、DefaultSqlSession中是如何通过Executor来表现策略模式的或者DefaultSqlSession如何使用策略模式模式的?

参考:

Mybatis源码系列3-三种SqlSession的区别 - 知乎

springboot+mybatis整合配置事务详解(^_^)_jaryle的博客-CSDN博客_mybatis事务配置

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-06-08 18:52:54  更:2022-06-08 18:54:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 20:40:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码