Mybatis与Spring集成时都做了什么?

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: Mybatis与Spring集成时都做了什么?  Mybatis是java开发者非常熟悉的ORM框架,Spring集成Mybatis更是我们的日常开发姿势。  本篇主要讲Mybatis与Spring集成所做的事情,让读过本文的开发者对Mybatis和Spring的集成过程,有清晰的理解。

Mybatis与Spring集成时都做了什么?
  Mybatis是java开发者非常熟悉的ORM框架,Spring集成Mybatis更是我们的日常开发姿势。

  本篇主要讲Mybatis与Spring集成所做的事情,让读过本文的开发者对Mybatis和Spring的集成过程,有清晰的理解。

  注:若文中有错误或其他疑问,欢迎留下评论。

  以mybatis-spring-2.0.2 为例,工程划分六个模块。

1、annotation 模块

  

  定义了@MapperScan和@MapperScans,用于扫描mapper接口。以及mapper扫描注册器(MapperScannerRegistrar),扫描注册器实现了 ImportBeanDefinitionRegistrar接口, 在Spring容器启动时会运行所有实现了这个接口的实现类,
注册器内部会注册一系列MyBatis相关Bean。

2、batch 模块

  

  批处理相关,基于优秀的批处理框架Spring batch 封装了三个批处理相关类:

MyBatisBatchItemWriter(批量写)
MyBatisCursorItemReader(游标读)
MyBatisPagingItemReader(分页读)
  在使用Mybatis时,方便的应用Spring batch,详见 Spring-batch使用。

3、config模块

  

  解析、处理读取到的配置信息。

4、mapper模块

  

  这里是处理mapper的地方:ClassPathMapperScanner(根据路径扫描Mapper接口)与MapperScannerConfigurer 配合,完成批量扫描mapper接口并注册为MapperFactoryBean。

5、support 模块

  

  支持包,SqlSessionDaoSupport 是一个抽象的支持类,用来为你提供 SqlSession调用getSqlSession()方法会得到一个SqlSessionTemplate。

6、transaction 模块,以及凌乱类

  

  与Spring集成后,事务管理交由Spring来做。

  还有包括异常转换,以及非常重要的SqlSessionFactoryBean,在外散落着。

下面重点讲述几个核心部分:

  一、初始化相关
  1)SqlSessionFactoryBean
  在基础的MyBatis中,通过SqlSessionFactoryBuilder创建SqlSessionFactory。集成Spring后由SqlSessionFactoryBean来创建。   

public class SqlSessionFactoryBean implements FactoryBean...
  需要注意SqlSessionFactoryBean实现了Spring的FactoryBean接口。这意味着由Spring最终创建不是SqlSessionFactoryBean本身,而是 getObject()的结果。我们来看下getObject()

复制代码
@Override
public SqlSessionFactory getObject() throws Exception {

if (this.sqlSessionFactory == null) {
  //配置加载完毕后,创建SqlSessionFactory
  afterPropertiesSet();
}
return this.sqlSessionFactory;

}
复制代码
  getObject()最终返回了当前类的 SqlSessionFactory,因此,Spring 会在应用启动时创建 SqlSessionFactory,并以 sqlSessionFactory名称放进容器。

  2) 两个重要属性:
    1. SqlSessionFactory 有一个唯一的必要属性:用于 JDBC 的 DataSource不能为空,这点在afterPropertisSet()中体现。

    2. configLocation,它用来指定 MyBatis 的 XML 配置文件路径。通常只用来配置 相关。其他均使用Spring方式配置

复制代码
5 public void afterPropertiesSet() throws Exception {
6 //dataSource不能为空
7 notNull(dataSource, "Property 'dataSource' is required");
8 //有默认值,初始化 = new SqlSessionFactoryBuilder()
9 notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
10 //判断configuration && configLocation有且仅有一个
11 state((configuration == null && configLocation == null) ||
          !(configuration != null && configLocation != null),
12 "Property 'configuration' and 'configLocation' can not specified with together");
13 //调用build方法创建sqlSessionFactory
14 this.sqlSessionFactory = buildSqlSessionFactory();
15 }
复制代码
     buildSqlSessionFactory()方法比较长所以,这里省略了一部分代码,只展示主要过程,看得出在这里进行了Mybatis相关配置的解析,完成了Mybatis核心配置类Configuration的创建和填充,最终返回SqlSessionFactory。

复制代码
1 protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
2
3 final Configuration targetConfiguration;
4
5 XMLConfigBuilder xmlConfigBuilder = null;
6    // 如果自定义了 Configuration,就用自定义的
7 if (this.configuration != null) {
8 targetConfiguration = this.configuration;
9 if (targetConfiguration.getVariables() == null) {
10 targetConfiguration.setVariables(this.configurationProperties);
11 } else if (this.configurationProperties != null) {
12 targetConfiguration.getVariables().putAll(this.configurationProperties);
13 }
14    // 如果配置了原生配置文件路径,则根据路径创建Configuration对象
15 } else if (this.configLocation != null) {
16 xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream()
        , null, this.configurationProperties);
17 targetConfiguration = xmlConfigBuilder.getConfiguration();
18 } else {21    // 兜底,使用默认的
22 targetConfiguration = new Configuration();
23    //如果configurationProperties存在,设置属性
24    Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables); }
26 //解析别名,指定包   
27 if (hasLength(this.typeAliasesPackage)) {
28 scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
29 .filter(clazz -> !clazz.isAnonymousClass())
      .filter(clazz -> !clazz.isInterface())
30 .filter(clazz -> !clazz.isMemberClass())
      .forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
31 }
32 //解析插件
33 if (!isEmpty(this.plugins)) {
34 Stream.of(this.plugins).forEach(plugin -> {
35 targetConfiguration.addInterceptor(plugin);38 }
39 ...
40 //如果需要解决原生配置文件,此时开始解析(即配置了configLocation)
41 if (xmlConfigBuilder != null) {
42 try {
43 xmlConfigBuilder.parse();
44    ... //有可能配置多个,所以遍历处理(2.0.0支持可重复注解)
52 if (this.mapperLocations != null) {
53 if (this.mapperLocations.length == 0) {
      for (Resource mapperLocation : this.mapperLocations) {
57 ... //根据mapper路径,加载所以mapper接口
62 XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
63 targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
64 xmlMapperBuilder.parse();
65  //构造SqlSessionFactory
70 return this.sqlSessionFactoryBuilder.build(targetConfiguration);
71 }
复制代码
  二、事务管理

  1)事务管理器配置
    MyBatis-Spring 允许 MyBatis 参与到 Spring 的事务管理中。 借助 Spring 的 DataSourceTransactionManager 实现事务管理。  

复制代码
/ 一、XML方式配置 /



/ 一、注解方式配置 /
@Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(dataSource());
}
注意:为事务管理器指定的 DataSource 必须和用来创建 SqlSessionFactoryBean 的是同一个数据源,否则事务管理器就无法工作了。
复制代码
  配置好 Spring 的事务管理器,你就可以在 Spring 中按你平时的方式来配置事务。并且支持 @Transactional 注解(声明式事务)和 AOP 风格的配置。在事务处理期间,一个单独的 SqlSession 对象将会被创建和使用。当事务完成时,这个 session 会以合适的方式提交或回滚。无需DAO类中无需任何额外操作,MyBatis-Spring 将透明地管理事务。

  2) 编程式事务:
  推荐TransactionTemplate 方式,简洁,优雅。可省略对 commit 和 rollback 方法的调用。    

复制代码
1 TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
2 transactionTemplate.execute(txStatus -> {
3 userMapper.insertUser(user);
4 return null;
5 });
注意:这段代码使用了一个映射器,换成SqlSession同理。
复制代码
  三、SqlSession
  在MyBatis 中,使用 SqlSessionFactory 来创建 SqlSession。通过它执行映射的sql语句,提交或回滚连接,当不再需要它的时候,可以关闭 session。使用 MyBatis-Spring 之后,我们不再需要直接使用 SqlSessionFactory 了,因为我们的bean 可以被注入一个线程安全的 SqlSession,它能基于 Spring 的事务配置来自动提交、回滚、关闭 session。

  SqlSessionTemplate  
  SqlSessionTemplate 是SqlSession的实现,是线程安全的,因此可以被多个DAO或映射器共享使用。也是 MyBatis-Spring 的核心。

  四、映射器
  1) 映射器的注册  
复制代码
1 /**
2 *@MapperScan注解方式
3 */
4 @Configuration
5 @MapperScan("org.mybatis.spring.sample.mapper")
6 public class AppConfig {
8 }
10 /**
11 *@MapperScanS注解 (since 2.0.0新增,java8 支持可重复注解)
12 * 指定多个路径可选用此种方式
13 */
14 @Configuration
15 @MapperScans({@MapperScan("com.zto.test1"), @MapperScan("com.zto.test2.mapper")})
16 public class AppConfig {
18 }
复制代码





  无论使用以上哪种方式注册映射器,最终mapper接口都将被注册为MapperFactoryBean。既然是FactoryBean,我们来跟它的getObject()方法看下。

  2) MapperFactoryBean源码解析
    1.查找MapperFactoryBean.getObject()  

复制代码
1 /**
2 * 通过接口类型,获取mapper
3 * {@inheritDoc}
4 */
5 @Override
6 public T getObject() throws Exception {
7 //getMapper 是一个抽象方法
8 return getSqlSession().getMapper(this.mapperInterface);
9 }
复制代码
    2.查看实现类,SqlSessionTemplate.getMapper()

    ( 为什么是SqlSessionTemplate,而不是默认的DefaultSqlSession?SqlSessionTemplate是整合包的核心,是线程安全的SqlSession实现,是我们@Autowired mapper接口编程的基础 )

4 @Override
5 public T getMapper(Class type) {
6 return getConfiguration().getMapper(type, this);
7 }
    3.调用Configuration.getMapper()  

1 public T getMapper(Class type, SqlSession sqlSession) {
2 return mapperRegistry.getMapper(type, sqlSession);
3 }
    4.调用MapperRegistry.getMapper()   

复制代码
1 @SuppressWarnings("unchecked")
2 public T getMapper(Class type, SqlSession sqlSession) {
3 final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) knownMappers.get(type);
4 if (mapperProxyFactory == null) {
5 throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
6 }
7 try {
8 return mapperProxyFactory.newInstance(sqlSession);
9 } catch (Exception e) {
10 throw new BindingException("Error getting mapper instance. Cause: " + e, e);
11 }
12 }
复制代码
    5.调用MapperProxyFactory.newInstance()  

1 @SuppressWarnings("unchecked")
2 protected T newInstance(MapperProxy mapperProxy) {
3 return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
4 }
    最终看到动态代理生成了一个新的代理实例返回了,也就是说,我们使用@Autowired 注解进来一个mapper接口,每次使用时都会由代理生成一个新的实例。

    为什么在Mybatis中SqlSession是方法级的,Mapper是方法级的,在集成Spring后却可以注入到类中使用?

    因为在Mybatis-Spring中所有mapper被注册为FactoryBean,每次调用都会执行getObject(),返回新实例。

  五、总结
    MyBatis集成Spring后,Spring侵入了Mybatis的初始化和mapper绑定,具体就是:

    1)Cofiguration的实例化是读取Spring的配置文件(注解、配置文件),而不是mybatis-config.xml

    2)mapper对象是方法级别的,Spring通过FactoryBean巧妙地解决了这个问题

    3)事务交由Spring管理

    注:如对文中内容有疑问,欢迎留下评论共同探讨。

原文地址https://www.cnblogs.com/xwy6/p/11279518.html

相关文章
|
8天前
|
Java 数据库连接 数据库
spring复习05,spring整合mybatis,声明式事务
这篇文章详细介绍了如何在Spring框架中整合MyBatis以及如何配置声明式事务。主要内容包括:在Maven项目中添加依赖、创建实体类和Mapper接口、配置MyBatis核心配置文件和映射文件、配置数据源、创建sqlSessionFactory和sqlSessionTemplate、实现Mapper接口、配置声明式事务以及测试使用。此外,还解释了声明式事务的传播行为、隔离级别、只读提示和事务超时期间等概念。
spring复习05,spring整合mybatis,声明式事务
|
11天前
|
Java 数据库连接 数据库
SpringBoot 整合jdbc和mybatis
本文详细介绍了如何在SpringBoot项目中整合JDBC与MyBatis,并提供了具体的配置步骤和示例代码。首先,通过创建用户实体类和数据库表来准备基础环境;接着,配置Maven依赖、数据库连接及属性;最后,分别展示了JDBC与MyBatis的集成方法及其基本操作,包括增删查改等功能的实现。适合初学者快速入门。
SpringBoot 整合jdbc和mybatis
|
4天前
|
存储 前端开发 Java
Spring Boot 集成 MinIO 与 KKFile 实现文件预览功能
本文详细介绍如何在Spring Boot项目中集成MinIO对象存储系统与KKFileView文件预览工具,实现文件上传及在线预览功能。首先搭建MinIO服务器,并在Spring Boot中配置MinIO SDK进行文件管理;接着通过KKFileView提供文件预览服务,最终实现文档管理系统的高效文件处理能力。
|
21天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
8天前
|
XML Java 关系型数据库
springboot 集成 mybatis-plus 代码生成器
本文介绍了如何在Spring Boot项目中集成MyBatis-Plus代码生成器,包括导入相关依赖坐标、配置快速代码生成器以及自定义代码生成器模板的步骤和代码示例,旨在提高开发效率,快速生成Entity、Mapper、Mapper XML、Service、Controller等代码。
springboot 集成 mybatis-plus 代码生成器
|
8天前
|
SQL XML Java
springboot整合mybatis-plus及mybatis-plus分页插件的使用
这篇文章介绍了如何在Spring Boot项目中整合MyBatis-Plus及其分页插件,包括依赖引入、配置文件编写、SQL表创建、Mapper层、Service层、Controller层的创建,以及分页插件的使用和数据展示HTML页面的编写。
springboot整合mybatis-plus及mybatis-plus分页插件的使用
|
8天前
|
XML Java 数据库连接
springboot中整合mybatis及简单使用
这篇文章介绍了如何在Spring Boot项目中整合MyBatis,包括依赖引入、配置数据源、创建测试表、编写Mapper接口和XML文件、以及创建Service和Controller层的步骤。
springboot中整合mybatis及简单使用
|
22天前
|
Java 数据库连接 数据格式
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
IOC/DI配置管理DruidDataSource和properties、核心容器的创建、获取bean的方式、spring注解开发、注解开发管理第三方bean、Spring整合Mybatis和Junit
【Java笔记+踩坑】Spring基础2——IOC,DI注解开发、整合Mybatis,Junit
|
26天前
|
前端开发 JavaScript Java
技术分享:使用Spring Boot3.3与MyBatis-Plus联合实现多层次树结构的异步加载策略
在现代Web开发中,处理多层次树形结构数据是一项常见且重要的任务。这些结构广泛应用于分类管理、组织结构、权限管理等场景。为了提升用户体验和系统性能,采用异步加载策略来动态加载树形结构的各个层级变得尤为重要。本文将详细介绍如何使用Spring Boot3.3与MyBatis-Plus联合实现这一功能。
57 2
|
2月前
|
测试技术 Java Spring
Spring 框架中的测试之道:揭秘单元测试与集成测试的双重保障,你的应用真的安全了吗?
【8月更文挑战第31天】本文以问答形式深入探讨了Spring框架中的测试策略,包括单元测试与集成测试的有效编写方法,及其对提升代码质量和可靠性的重要性。通过具体示例,展示了如何使用`@MockBean`、`@SpringBootTest`等注解来进行服务和控制器的测试,同时介绍了Spring Boot提供的测试工具,如`@DataJpaTest`,以简化数据库测试流程。合理运用这些测试策略和工具,将助力开发者构建更为稳健的软件系统。
39 0
下一篇
无影云桌面