springboot+mybatis, SqlSessionTemplate线程安全

101 篇文章 14 订阅
13 篇文章 0 订阅

集成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事务配置

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值