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