springboot集成mybatis以及通过解读源码了解mybatis的自动配置原理

320 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第16天,点击查看活动详情

一、整合mybatis

1-1、使用自动生成器生成mybatis代码

在上篇文章中,创建springboot项目的时候,已经引入了相关依赖:jdbc/drui/web/mysql/mybatis这四个,现在首先通过generator生成相关mybatis代码。

1-1-1、在pom中引入generator插件

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <!-- Mybatis-Generator插件,自动生成代码 -->
        <plugin>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-maven-plugin</artifactId>
            <version>1.3.5</version>
            <configuration>
                <configurationFile>${project.basedir}/src/main/resources/generatorConfig.xml</configurationFile>
                <verbose>true</verbose>
                <overwrite>true</overwrite>
            </configuration>
            <dependencies>
                <!--必須要引入数据库驱动-->
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <!--必须制定版本-->
                    <version>8.0.22</version>
                </dependency>
            </dependencies>
        </plugin>
    </plugins>
</build>

1-1-2、创建generatorConfig.xml

这个文件如果不知道怎么写可以去官网拷贝一下,连接为:mybatis.org/generator/c…

image.png

我之前文章中也有介绍mybatis-generator的使用方式,里面也有这个配置文件的详解,感兴趣同学可以去看下,这里就直接使用之前的文件了,内如如下:

<!DOCTYPE generatorConfiguration PUBLIC
        "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>


    <!--如果需要使用 command的方式生成需要配置数据库驱动的jar包路径
    <classPathEntry location="指定数据驱动的磁盘路径"/>-->

    <!--context 生成上下文 配置生成规则
            id 随意写
           targetRuntime 生成策略
                MyBatis3DynamicSql 默认的,会生成 动态生成sql的方式(没有xml)
                MyBatis3 生成通用的查询,可以指定动态where条件
                MyBatis3Simple 只生成简单的CRUD
    -->
    <context id="simple" targetRuntime="MyBatis3Simple">


        <commentGenerator>
            <!--设置是否生成注释  true 不生成  注意: 如果不生成注释,下次生成代码就不会进行合并-->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--数据源 -->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/springboot_mybatis"
                        userId="root"
                        password="root"/>

        <!--pojo
        javaModelGenerator  java实体生成规则(POJO)
            targetPackage 生成到哪个包下
            targetProject 生成到当前文件的哪个相对路径下
        -->
        <javaModelGenerator targetPackage="com.jony.pojo" targetProject="src/main/java"/>
        <!--mapper xml映射文件
            sqlMapGenerator mapper xml映射文件生成规则
            targetPackage 生成到哪个包下
            targetProject 生成到当前文件的哪个相对路径下
        -->
        <sqlMapGenerator targetPackage="com.jony.mapper" targetProject="src/main/resources"></sqlMapGenerator>
        <!--mapper接口
            javaClientGenerator mapper mapper接口生成规则
            type 指定生成的方式
                1.使用注解的方式生成
                2.使用接口绑定的方式生成(要配置sqlMapGenerator)
            targetPackage 生成到哪个包下
            targetProject 生成到当前文件的哪个相对路径下-->
        <javaClientGenerator type="XMLMAPPER" targetPackage="com.jony.mapper" targetProject="src/main/java"/>


        <!--配置哪些表需要进行代码生成
        tableName 表名
        domainObjectName pojo类名
        mapperName 对应mapper接口的类名 和 mapper xml文件名
        -->
        <table tableName="emp" domainObjectName="Emp" mapperName="EmpMapper" />
        <table tableName="dept" domainObjectName="Dept" mapperName="DeptMapper" />
    </context>
</generatorConfiguration>

1-1-3、启动generator

选择maven插件,然后选择到当前项目,找到mybatis-generator插件,点击标红的双击 image.png

1-1-3-1、运行错误

运行的时候,可能会报如下错误 image.png

我们只需要在generator.xml中的数据库地址加上serverTimezone=UTC即可,最终数据源配置如下: image.png

再此运行就可以成功了,如下: image.png

1-2、配置mybatis

1-2-1、在application.yml中配置Mapper.xml路径

image.png

1-2-2、配置扫描Mapper接口

将Mapper扫描注解配置到启动类上,并输入Mapper接口的包路径 image.png

1-2-3、测试一下是否成功

1-2-3-1、创建一个控制器

创建一个查询所有的方法,为了省事就不创建service层了。 image.png

1-2-3-2、启动项目浏览器访问

可以正常访问成功,如下 image.png

1-3、mybatis的自动配置原理

要了解mybatis给我们提供了哪些配置信息,就需要去看一下源码了,还是按照之前的办法查找自动配置类,如下: image.png

进入这个类如图 image.png

1-3-1、mybatis自动配置类核心注解

@EnableConfigurationProperties(MybatisProperties.class)

这个注解主要配置了mybatis的相关配置信息,进入这个类如下:

image.png

1-3-1-1、configLocation设置mybatis配置文件

通过这个配置,就可以指定mybatis的配置文件,这样mybatis加载配置的时候就回去指向的配置文件获取相关配置 image.png

1-3-1-1-1、mybatis-config.xml

在mybatis-config.xml配置文件中设置了,驼峰命名法,以及在Mapper.xml中的package别名,这样在mapper.xml中返回pojo的type就不用写类的完整限定名了。 image.png

如下在未设置pojo别名的情况下启动项目是报错的 image.png

启用别名之后就正常了,这样在Mapper.xml中,就不用繁琐的写pojo的完整想顶面了 image.png

实际上如果没有mybatis配置文件,也可以在application.yml中进行配置如下: image.png

1-3-2、mybatis中的setting设置

mytabis中的setting设置同样可以在mybatis-config.xml中进行配置,也可以在application.yml中进行配置,看源码如下 image.png

进入这个方法,主要通过如下配置进行获取相关配置的

Configuration configuration = this.properties.getConfiguration();

image.png

其中this.properties.getConfiguration();实际在配置文件中就是configuration

image.png

image.png

而Configuration类中实际就是mybatis的所有信息 image.png 如下这些都是setting的配置 image.png

1-3-2-1、设置驼峰命名

一般情况下我们会设置全局的resultMap,将数据库的蛇形命名对应到pojo的驼峰命名,但是在没有做映射的情况下,还可以通过mybatis.configuration.map-underscore-to-camel-case: true来进行设置,如下: image.png

1-3-2-1-1、测试一下驼峰命名映射关系
1-3-2-1-1-1、取消驼峰命名映射关系以及Mapper.xml中的映射关系

首先将mybatis全局配置文件以及application.yml配置文件中的驼峰命名注释掉,同时将Mapper.xml中的映射关系也注释掉如下:

不加载配置文件,并且驼峰命名已注释 image.png

再将数据库字段和pojo的映射关系注释 image.png

1-3-2-1-1-2、访问测试

启动项目访问接口,可以看到查询出来的deptName的值为Null,数据中的dept_name并没有映射到deptName image.png

1-3-2-1-1-3、启用application中的驼峰命名映射关系

再次将application.yml中的驼峰命名启用 image.png

再次访问接口,就可以成功将dept_name映射到deptName了,如下: image.png

1-4、核心方法

上面解析的配置信息,都是在这个方法中进行加载处理的

@Bean
@ConditionalOnMissing
Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
  SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
  factory.setDataSource(dataSource);
  factory.setVfs(SpringBootVFS.class);
  / 设置Mybaits的全局配置文件
  if (StringUtils.hasText(this.properties.getConfigLocation())) {
    factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
  }
  applyConfiguration(factory);
  // 相当于mybatis全局配置文件中 -之前mybatis文章中有介绍这个属性
  /*<properties> 
      <property name="" value=""/>
    </properties>
    */
  if (this.properties.getConfigurationProperties() != null) {
    factory.setConfigurationProperties(this.properties.getConfigurationProperties());
  }
  // 就是配置插件-拦截器 只需要配置一个实现了Interceptor的接口为Bean
  if (!ObjectUtils.isEmpty(this.interceptors)) {
    factory.setPlugins(this.interceptors);
  }
  // 设置数据库厂商id-- 之前mybatis文章中有介绍这个属性
  if (this.databaseIdProvider != null) {
    factory.setDatabaseIdProvider(this.databaseIdProvider);
  }
  // 设置别名:去application.yml中获取is.typeAliasesPackage
  if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
    factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
  }
  // 可以通过父类过滤哪些类需要使用别名
      比如:pojo.user extends basePojo
          pojo.user2
          去application.yml中设置mybatis.typeAliasesSuperType: com.tulingxueyuan.pojo.basePojo
         这样就只有user设置了别名,user2未进行设置
  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);
  }
  // 设置mapper.xml映射文件:mapper-locations: classpath:com/tulingxueyuan/mapper/*Mapper.xml
  if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
    factory.setMapperLocations(this.properties.resolveMapperLocations());
  }
  Set<String> factoryPropertyNames = Stream
      .of(new BeanWrapperImpl(SqlSessionFactoryBean.class).getPropertyDescriptors()).map(PropertyDescriptor::getName)
      .collect(Collectors.toSet());
  Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
  if (factoryPropertyNames.contains("scriptingLanguageDrivers") && !ObjectUtils.isEmpty(this.languageDrivers)) {
    // Need to mybatis-spring 2.0.2+
    factory.setScriptingLanguageDrivers(this.languageDrivers);
    if (defaultLanguageDriver == null && this.languageDrivers.length == 1) {
      defaultLanguageDriver = this.languageDrivers[0].getClass();
    }
  }
  if (factoryPropertyNames.contains("defaultScriptingLanguageDriver")) {
    // Need to mybatis-spring 2.0.2+
    factory.setDefaultScriptingLanguageDriver(defaultLanguageDriver);
  }

  return factory.getObject();
}

private void applyConfiguration(SqlSessionFactoryBean factory) {
  Configuration configuration = this.properties.getConfiguration();
  if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
    configuration = new Configuration();
  }
  if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
    for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
      customizer.customize(configuration);
    }
  }
  factory.setConfiguration(configuration);
}