总结MyBatis的知识点

MyBatis简易教程

什么是 MyBatis?

示例项目:mybatisdemo

1. 准备工作

1.1 创建测试数据库及表

create database testdb;

use testdb;
CREATE TABLE `user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(20),
`age` INT,
`birthday` DATE,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8;

1.2 项目添加MyBatis maven依赖

https://mvnrepository.com

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.4.6</version>
</dependency>

<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>6.0.6</version>
</dependency>

1.3 Mybatis XML配置

mybatis-config.xml //名称可以定制,可以配置多个环境

jdbc.properties //可以通过引用properties文件将数据库连接配置独立出来

详细配置节点参考官方配置说明

1.4 日志输出配置

通过settings节点配置日志输出组件,示例中使用log4j2

mybatis-config.xml中日志配置

<settings>
    <setting name="logImpl" value="LOG4J2"/>
</settings>

添加log4j2日志依赖

<!--为了查看mybatis输出的日志信息包括sql语句,使用log4j2来打印日志-->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>${log4j2-version}</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>${log4j2-version}</version>
</dependency>

添加log4j2的配置文件log4j2.xml,log4j2的配置文件节点参考官方配置说明

2. XML配置方式示例

2.1 添加数据库实体模型

User.java

2.2 添加数据库操作接口

UserMapper.java

2.3 添加XML映射文件(数据库操作类<==>sql)

动态sql映射文件UserMapper.xml,动态sql的语法参考官方说明文档,这里不详细讲

注:
a) UserMapper.java与UserMapper.xml一般保持命名空间一致
b) mybatis-config.xml中需要配置UserMapper.xml,MyBatis通过该配置动态生成数据库操作接口对应实现类
c) xml文件在编译时可能没有被默认加入到resources目录,所以需要在pom文件中build>resources节点显式将xml文件include到resources目录下
d) 如果模型字段名称和数据字段名称不一致,可以通过resultMap节点指定映射关系

2.4 添加获取SqlSession封装工具类

MyBatisUtil.java

该工具类的作用:
a) 通过createSqlSessionFactoryFromXML方法根据XML配置文件mybatis-config.xml初始化SqlSessionFactory
b) 获取SqlSession对象

2.5 测试

新建App.java,添加增删改查一共5个测试方法

public static void insertUser();
public static void deleteUser();
public static void updateUser();
public static void selectUserById();
public static void selectAllUsers();

3. 代码配置方式示例

通过本地xml、properties文件的配置方式在公司内部使用时可能会有很多限制: a) 运维部门可能不允许线上直接修改本地配置文件,需要接入配置中心统一管理
b) 线上应用是分布式发布,多台机器手工修改配置文件不太现实

再加上约定大于配置、去配置文件是一个趋势,通过code方式进行配置也是不可缺少的

3.1 添加code配置

MyBatisUtil.java工具类中可以直接进行配置,createSqlSessionFactoryFromCode方法将帮助你使用code方式来进行配置,这时候mybatis-config.xml配置文件就可以丢弃了(包括独立出来的jdbc.properties文件)。

注:
a) log4j.xml日志配置文件在使用本地配置的时候可以保留,但如果接入公司日志中心平台,也可以通过code方式去除,通过自定义appender将日志写入到日志中心平台
b) 同样的,需要指定mapper.xml映射文件的地址,可以通过Configuration.addMappers(String packageName)方法通过包名批量添加

MyBatisUtil.java中切换createSqlSessionFactoryFromCode方法初始化SqlSessionFactory之后,之前的5个测试方法同样生效

3.2 如何将动态sql映射文件UserMapper.xml一并去除?

动态sql功能很强大,统一维护到xml映射文件中管理也很方便,但是可以做到0配置文件吗?

MyBatis提供了annotation机制,可以在数据库操作接口UserMapper中直接通过annotation来直接编写sql语句,对于非常复杂的sql,也可以通过SqlProvider来将复杂sql语句处理独立出来

示例见UserMapper.java中以下两个方法:

@Select("select * from user where " +
            "name like concat('%',#{searchKey},'%') " +
            "limit #{offset},#{limit}")
List<User> searchByName(@Param("offset")int offset, @Param("limit") int limit, @Param("searchKey")String searchKey);

@SelectProvider(type=CustomSqlProvider.class,method = "searchUsersByName")
List<User> searchByNameWithSqlProvider(String searchKey);

执行效果跟在mapper.xml中写动态sql是一样的

注: a) 如果模型字段名称和数据字段名称不一致,可以通过@Results指定映射关系
b) 不建议直接通过字符串拼接的方式写sql,通过参数化的方式可以避免sql注入问题 c) 使用annotation写sql的方式与在mapper.xml中写sql的方式可以混合用,MyBatis官方建议复杂查询(例如嵌套join)还是使用mapper.xml,建议简单sql使用annotation,复杂sql使用mapper.xml,或者项目中统一使用mapper.xml来统一管理sql,性能上的差异没有看到过比较,你可以自己测试一下

4. MyBatis扩展

4.1 拦截器,自动分页拦截器

项目中实现org.apache.ibatis.plugin.Interceptor接口创建示例分页拦截器PageInterceptor.java,该拦截器只要发现查询参数类型为Page,就执行自动分页操作

在MyBatisUtil.java中通过Configuration.addInterceptor(Interceptor interceptor)方法添加分页拦截器

数据库操作接口UserMapper中添加自动分页操作方法

@SelectProvider(type=CustomSqlProvider.class,method = "autoPagedQuery")
List<User> autoPagedQuery(Page<User> pageCondition);

App.java中的测试方法

/**
 * 利用分页拦截器来进行sql语句的自动分页查询
 * 【注意】:本示例中仅在通过code的方式创建session factory时才有效, 通过读取mybatis-config.xml的方式没有配置分页拦截器
 */
private static void autoPagedQuery();

拦截器的其它信息可以参考官方文档

4.2 代码生成器

数据库添加测试表

use testdb;
CREATE TABLE `user_address` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`user_id` INT(11) NOT NULL,
`address` varchar(256),
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=37 DEFAULT CHARSET=utf8;

pom添加mybatis生成器插件

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
    <dependency>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-core</artifactId>
        <version>1.3.5</version>
    </dependency>
    
    <!--其它依赖-->
</dependencies>

<plugins>
    <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.3.5</version>
        <configuration>
            <verbose>true</verbose>
            <overwrite>true</overwrite>
        </configuration>
    </plugin>
    <!--其它插件-->
</plugins>

添加生成器指定的配置文件generatorConfig.xml,详细配置见说明,这里指定只生成刚才创建的user_address表对应的实体模型、数据库操作接口、映射mapper.xml文件

生成器需要用到sql驱动,需要在jdbc.properties中指定驱动jar包地址

jdbc.driverLocation=C:\\Users\\Administrator\\.m2\\repository\\mysql\\mysql-connector-java\\6.0.6\\mysql-connector-java-6.0.6.jar

点击idea右侧maven>Plugins>mybatis-generator>mybatis-generator:generate,或者项目中命令执行mvn命令mvn: mybatis-generator:generate

注:
生成器只是一个辅助工具,不建议正式项目中添加生成器,多人使用时由于使用不当,可能会造成代码覆盖等问题

5. MyBatis缓存

5.1 介绍

MyBatis提供了一级缓存(Session级别缓存,默认开启)和二级缓存(Mapper级别缓存,可以跨Session,需要mapper.xml中配置)

网上搬运过来的介绍
a). 一级缓存: 基于PerpetualCache 的 HashMap本地缓存,其存储作用域为 Session,当 Session flush 或 close 之后,该Session中的所有 Cache 就将清空。
b). 二级缓存与一级缓存其机制相同,默认也是采用 PerpetualCache,HashMap存储,不同在于其存储作用域为 Mapper(Namespace),并且可自定义存储源,如 Ehcache。
c). 对于缓存数据更新机制,当某一个作用域(一级缓存Session/二级缓存Namespaces)的进行了 C/U/D 操作后,默认该作用域下所有 select 中的缓存将被clear。

MyBatis缓存

注:
排除特殊场景,个人认为没有必要在实际项目中使用二级缓存或者自定义缓存
建议使用分布式缓存框架(例如redis),或者本地缓存框架(例如ehcache)根据实际业务场景自己控制缓存

5.2 一级缓存

通过App.java中一级缓存测试方法测试,观察输出的sql判断是否结果被缓存

private static void testCache1();

5.3 二级缓存

在mapper.xml中配置cache节点

<cache 
    eviction="FIFO"  <!--回收策略为先进先出-->
    flushInterval="60000" <!--自动刷新时间60s-->
    size="512" <!--最多缓存512个引用对象-->
    readOnly="true" /> <!--只读-->

通过App.java中一级缓存测试方法测试,观察输出的sql判断是否结果被缓存

private static void testCache2();

开启mapper.xml中二级缓存之后,指定其中某个查询不使用缓存也是可以的

<select id="xxx" resultMap="xxx" useCache="false" />

5.4 自定义缓存

实现org.apache.ibatis.cache.Cache接口来自定义缓存,mapper.xml中配置<cache type="自定义缓存类全路径" />来指定使用缓存

参考官方cache说明

6. 批量操作、分库分表、读写分离

6.1 批量操作 & 多表关联查询

动态sql:foreach

annotation: 配合script标签+foreach
复杂sql使用SqlProvider

//示例
public interface MybatisDemoDAO {
	@Insert({
			"<script>",
			"insert into mybatis_demo (name, age)",
			"values ",
			"<foreach  collection='dmoList' item='dmo' separator=','>",
			"( #{dmo.name,jdbcType=VARCHAR}, #{dmo.age,jdbcType=INTEGER})",
			"</foreach>",
			"</script>"
	})
	int insertBatch(@Param("dmoList") List<MybatisDemoDMO> dmoList);
}

多表关联查询

Advanced Result Maps - association

6.2 分库分表拦截器

6.3 MyBatis + sharding-jdbc

参考:sharding-jdbc + mybatis +spring boot的分库分表实现

7. mybatis-plus

mybatis-plus指南

7.1 通用CRUD

7.2 代码生成器

7.3 分页插件

参考

MyBatis Getting started