简介

在上几篇文章中,解析了MyBatis的核心原理部分,我们大致对其有了一定的了解,接下来我们看看在日常的开发中MyBatis是如何与Spring框架结合的

源码解析

在我们的日常开发中,使用Spring框架结合MyBatis,只需简单的进行相关的配置,在代码中使用注解即可轻松使用,而不用像下面示例中的,需要很多的代码:

public class MybatisTest {// 从SqlSessionFactory中获取Mapper进行使用@Testpublic void test() {try(SqlSession session = buildSqlSessionFactory().openSession()) {PersonMapper personMapper = session.getMapper(PersonMapper.class);personMapper.createTable();personMapper.save(Person.builder().id(1L).name(new String[]{"1", "2"}).build());personMapper.save(Person.builder().id(2L).name(new String[]{"1", "2"}).build());Map<String, Object> query = new HashMap<>(2);query.put("id", 1);query.put("name", new String[]{"1", "2"});System.out.println(personMapper.getPersonByMap(query));}}// 初始化SqlSessionFactorypublic static SqlSessionFactory buildSqlSessionFactory() {String JDBC_DRIVER = "org.h2.Driver";String DB_URL = "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1";String USER = "sa";String PASS = "";DataSource dataSource = new PooledDataSource(JDBC_DRIVER, DB_URL, USER, PASS);Environment environment = new Environment("Development", new JdbcTransactionFactory(), dataSource);Configuration configuration = new Configuration(environment);configuration.getTypeHandlerRegistry().register(String[].class, JdbcType.VARCHAR, StringArrayTypeHandler.class);configuration.addMapper(PersonMapper.class);SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();return builder.build(configuration);}
}

在上面的代码中,我们可以明显的看到大段的SqlSessionFactory初始化和Mapper的获取的繁琐代码,但我们在Spring中没有这些繁琐的操作,如下变可以使用:

@AllArgsConstructor
@Service
public class PersonService {private final PersonMapper personMapper;public void select() {personMapper.getPersonById(0);}
}

可以说非常省心和简单了

以前自己也不知道其原理,通过这段时间的学习,了解了MyBatis的原始使用和Spring的相关原理,知道了其背后的原理,在开发中使用起来感觉心中比以前有数了,希望通过这些源码解析的文章,也能对大家有所帮助

SqlSessionFactory的初始化

这部分涉及到Spring的Bean和Spring Boot自动配置

可以尝试去自己写一个Spring Boot Starter,这样会有比较大帮助(以前好像写过一篇,但找不到了,可以参考下下面其他人的链接):

  • SpringBoot自定义starter及自动配置
  • spring boot 自定义配置属性的各种方式

这里我们直接去看包:org.mybatis.spring.boot.autoconfigure

可以看到下面一段我们熟悉的内容:

可以看到Spring 在启动时便自动初始化了SqlSessionFactory,其中的细节这里就不赘述了

    @Bean@ConditionalOnMissingBeanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean factory = new SqlSessionFactoryBean();factory.setDataSource(dataSource);factory.setVfs(SpringBootVFS.class);if (StringUtils.hasText(this.properties.getConfigLocation())) {factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));}this.applyConfiguration(factory);if (this.properties.getConfigurationProperties() != null) {factory.setConfigurationProperties(this.properties.getConfigurationProperties());}if (!ObjectUtils.isEmpty(this.interceptors)) {factory.setPlugins(this.interceptors);}if (this.databaseIdProvider != null) {factory.setDatabaseIdProvider(this.databaseIdProvider);}if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());}if (this.properties.getTypeAliasesSuperType() != null) {factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());}if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());}if (!ObjectUtils.isEmpty(this.typeHandlers)) {factory.setTypeHandlers(this.typeHandlers);}if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {factory.setMapperLocations(this.properties.resolveMapperLocations());}Set<String> factoryPropertyNames = (Set)Stream.of((new BeanWrapperImpl(SqlSessionFactoryBean.class)).getPropertyDescriptors()).map(FeatureDescriptor::getName).collect(Collectors.toSet());Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {factory.setScriptingLanguageDrivers(this.languageDrivers);if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {defaultLanguageDriver = this.languageDrivers[0].getClass();}}if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);}this.applySqlSessionFactoryBeanCustomizers(factory);return factory.getObject();}

Mapper的生成使用

这部分涉及到Spring的依赖注入相关的原理了,自己以前写过一个demo,但文章还没完善,后面抽空完善后,再发出来

简答来说,核心是:

  • 1.应用启动时,自动生成需要的类实例放入容器中, 比如加了Service注解的类
  • 2.对类实例进行初始化,在构造函数中,将其需要的类实例给传递进去

比如下面的:

@AllArgsConstructor
@Service
public class PersonService {private final PersonMapper personMapper;
}

在Spring容器中已经有了类 PersonService 和 PersonMapper 的实例

在初始化阶段,调用 PersonService 构造函数时,发现需要 PersonMapper,由于从容器中取PersonMapper给其传递进去

大体的意思应该是这样,可能细节上会有些出入

下面是大致一些Debug的信息,通过调试就会发现,SqlSessionFactory 和 Mapper 初始化和获取等代码都是在应用程序其中就会执行触发

这个是PersonService这个bean初始化的相关栈函数:

   @Nullableprotected Object resolveAutowiredArgument(MethodParameter param, String beanName, @Nullable Set<String> autowiredBeanNames, TypeConverter typeConverter, boolean fallback) {Class<?> paramType = param.getParameterType();if (InjectionPoint.class.isAssignableFrom(paramType)) {InjectionPoint injectionPoint = (InjectionPoint)currentInjectionPoint.get();if (injectionPoint == null) {throw new IllegalStateException("No current InjectionPoint available for " + param);} else {return injectionPoint;}} else {try {return this.beanFactory.resolveDependency(new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter);} catch (NoUniqueBeanDefinitionException var8) {throw var8;} catch (NoSuchBeanDefinitionException var9) {if (fallback) {if (paramType.isArray()) {return Array.newInstance(paramType.getComponentType(), 0);}if (CollectionFactory.isApproximableCollectionType(paramType)) {return CollectionFactory.createCollection(paramType, 0);}if (CollectionFactory.isApproximableMapType(paramType)) {return CollectionFactory.createMap(paramType, 0);}}throw var9;}}}

调试的相关信息如下:

这个beanName是PersonService,需要的构造函数参数是 PersonMapper

一直跟着下面,它会从SqlSessionTemplate中取

    public <T> T getMapper(Class<T> type) {return this.getConfiguration().getMapper(type, this);}

然后就跳到我们熟悉的 Mybatis类中 Configuration:

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {return mapperRegistry.getMapper(type, sqlSession);}

总结

本篇中大致探索了:

  • MyBatis初始化 SqlSessionFactory:Spring boot 自动配置
  • Mapper初始化和使用:基于依赖注入,方便快捷的使用

MyBatis3源码解析(8)MyBatis与Spring的结合相关推荐

  1. Mybatis3 源码解析系列

    简介 Mybatis作为一个优秀的Java持久化框架,在我们的日常工作中相信都会用到,本次源码解析系列,就开始探索下Mybatis 总结 在MyBatis的学习中,首先通读了<MyBatis3源 ...

  2. MyBatis3源码解析(6)TypeHandler使用

    简介 在上几篇中,介绍了MyBatis3对参数和结果的解析转换,对于常规数据类型,默认的处理已经足够应付了,但日常开发中会有一些特殊的类型,就可以通过TypeHandler来进行处理 示例准备 本篇文 ...

  3. MyBatis3源码解析(5)查询结果处理

    简介 上篇中解析了MyBatis3中参数是如何传递处理的,本篇接着看看在获取到查询结果后,MyBatis3是如何将SQL查询结果与我们接口函数定义的返回结果对应的 源码 获取结果后处理的入口 在:My ...

  4. MyBatis3源码解析(3)查询语句执行

    简介 上篇探索了MyBatis中如何获取数据库连接,本篇继续探索,来看看MyBatis中如何执行一条查询语句 测试代码 本篇文中用于调试的测试代码请参考:MyBatis3源码解析(1)探索准备 完整的 ...

  5. 【Mybatis+spring整合源码探秘】--- mybatis整合spring事务原理

    文章目录 1 mybatis整合spring事务原理 1 mybatis整合spring事务原理 本篇文章不再对源码进行具体的解读了,仅仅做了下面一张图: 该图整理了spring+mybatis整合后 ...

  6. MyBatis3源码解析(2)数据库连接

    简介 基于上篇的示例感受,下面我们探索下MyBatis连接数据库的细节是如果实现的,在日常使用中是如何能和Druid数据库连接池等配合起来的 源码解析 基于上篇的示例代码: public class ...

  7. MyBatis3源码解析(1)探索准备

    简介 本篇文章将使用原生的JDBC方式操作数据库,然后在使用Mybatis提供的方式操作数据库,通过对比两部分的操作,大致得到Mybatis所做的主要工作,为接下来的源码解析做准备 示例代码 完整的工 ...

  8. 【MyBatis源码解析】MyBatis一二级缓存

    MyBatis缓存 我们知道,频繁的数据库操作是非常耗费性能的(主要是因为对于DB而言,数据是持久化在磁盘中的,因此查询操作需要通过IO,IO操作速度相比内存操作速度慢了好几个量级),尤其是对于一些相 ...

  9. MyBatis3源码解析(7)TypeHandler注册与获取

    简介 在上篇文章中,我们介绍了TypeHandler的简单使用和解析了TypeHandler的处理核心,这篇文章中我们接着看到TypeHandler是如注册和获取使用的 源码解析 TypeHandle ...

最新文章

  1. NBUT 1457 Sona(莫队算法+离散化)
  2. Maya人物角色行走动画制作视频教程
  3. rsync 同步数据
  4. K8s中的external-traffic-policy是什么?
  5. 微软的日历控件为什么从1753年开始?Sqlserver数据库不能插入1753年之前的数据?...
  6. 2023年考研之路或将更难
  7. c语言 程序延时 校准,c语言实现系统时间校正工具代码分享
  8. 机电传动控制大作业 第一阶段
  9. linux系统调用是通过软中断实现的吗
  10. spss多元线性回归散点图_SPSS线性回归|别人不想告诉你的其他操作我都总结好了(中)...
  11. 数据结构笔记(二十八)-- 图的广度优先遍历
  12. 服务器内的虚拟机无法上网,VMware虚拟机中无法上网如何解决
  13. 语音验证码api 手机接听验证码
  14. MATLAB求解三角函数
  15. 破解某设计网站充钱下载图片
  16. [源码]UnicodeTOGB,能够将Unicode串转换成GB码,方便开发。
  17. android6.0系统车载航一,谷歌确定Android 6.0命名为Marshmallow
  18. Name was not previously introduced as per JSP.5.3的解决办法
  19. 【程序人生】机灵鹤六月份的月度总结
  20. 秘密secret的安全性以及解决哪些痛点

热门文章

  1. coposer 安装 laravel
  2. RFC1323 timestamp PAWS的实现陷阱
  3. iOS --- [持续更新中] iOS移动开发中的优质资源
  4. int a[5]={1,2,3,4,5}; int *p=(int*)(a+1); printf(%d,*(p-1)); 答案为什么是5?
  5. 微信公众平台消息接口开发(34)微信墙之表白墙/婚礼墙/晚会墙/会议墙/晒单墙/照片墙/历史墙...
  6. 上传文件时$_FILES为空,可能的原因及解决方法
  7. sigmoid和softmax总结
  8. Memory Networks论文串烧
  9. 概率论 方差公式_【考研数学】概率论与数理统计
  10. 做Tiktok如何选择地区?