MyBatis学习笔记
一、简介
MyBatis是什么
- MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。
- MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
- MyBatis 可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO( Plain Old Java Objects,普通的Java对象)映射成数据库中的记录
二、Mybatis配置
1、数据准备
在MySQL数据库创建一数据库实例learnmybatis
,在其创建一张表
1 | CREATE TABLE employee( |
创建对应的JavaBean
1 | public class Employee { |
pom.xml
中引入依赖,注意引入最新版
1 | <dependency> |
2、Mybatis全局配置
1 |
|
MyBatis 的配置文件包含了影响 MyBatis 行为甚深的设置( settings)和属性( properties)信息。文档的顶层结构如下:
- configuration 配置
- properties 属性
- settings 设置
- typeAliases 类型命名
- typeHandlers 类型处理器
- objectFactory 对象工厂
- plugins 插件
- environments 环境
- environment 环境变量
- transactionManager 事务管理器
- dataSource 数据源
- databaseIdProvider 数据库厂商标识
- mappers 映射器
- environment 环境变量
3、全局配置文件-properties外部引入
1 | <configuration> |
在resource下中创建dbconfig.properties
文件
1 | jdbc.driver=com.mysql.cj.jdbc.Driver |
4、配置文件常用配置
settings-运行时行为设置
1 | <configuration> |
typeAliases-别名
类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。可以在xml配置<typeAliases>
,也可以在类上加注解@Alias
不过为了好排查,一般都写全类名。
enviroments-运行环境
- id:指定当前环境的唯一标识
- transactionManager、和dataSource都必须有
1 | <environments default="dev_mysql"> |
5、测试类
1 | public class MybatisTest { |
三、Mybatis 基础操作
1、基础操作
- cache –命名空间的二级缓存配置
- cache-ref – 其他命名空间缓存配置的引用。
- resultMap – 自定义结果集映射
- parameterMap – 已废弃!老式风格的参数映射
- sql –抽取可重用语句块。
- insert – 映射插入语句
- update – 映射更新语句
- delete – 映射删除语句
- select – 映射查询语句
EmployeeMapper.java
1 | public interface EmployeeMapper { |
EmployeeMapper.xml
1 |
|
- resultType:返回结果类型,可以做别名映射
- parameterType:参数类型,可以省略,
- 获取自增主键的值:
- mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGenreatedKeys();
- useGeneratedKeys=“true”;使用自增主键获取主键值策略
- keyProperty;指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性
2、映射文件-参数处理
4种映射文件,多个参数会被封装成 一个map
1 | <mapper namespace="org.demo.mapper.EmployeeMapper"> |
1 | public interface EmployeeMapper { |
参数处理#{}与${}取值区别
#{}
: 是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入${}
: 取出的值直接拼装在sql语句中;会有安全问题;
3、流式查询
3.1 简介
流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。如果没有流式查询,我们想要从数据库取 1000 万条记录而又没有足够的内存时,就不得不分页查询,而分页查询效率取决于表设计,如果设计的不好,就无法执行高效的分页查询。
流式查询的过程当中,数据库连接是保持打开状态的,因此要注意的是:执行一个流式查询后,数据库访问框架就不负责关闭数据库连接了,需要应用在取完数据后自己关闭。
3.2 流式查询接口
在EmployeeMapper
创建mapper方法,这里提供两种方式
1 | // MySQL 默认情况下,创建 prepareStatement 时,就已经是 ResultSet.TYPE_FORWARD_ONLY 和 ResultSet.CONCUR_READ_ONLY ,所以这两个参数可加可不加 |
基于xml的方式
1 | <!--FORWORD_ONLY 结果集的游标只能向下滚动--> |
测试用例
1 | // 执行数据库操作 |
3.3 流式查询接口–Cursor
MyBatis 提供了一个叫 org.apache.ibatis.cursor.Cursor
的接口类用于流式查询,这个接口继承了 java.io.Closeable
和 java.lang.Iterable
接口,由此可知:
-
Cursor 是可关闭的,实际上当关闭 Cursor 时,也一并将数据库连接关闭了;
-
Cursor 是可遍历的
除此之外,Cursor 还提供了三个方法:
isOpen()
:用于在取数据之前判断 Cursor 对象是否是打开状态。只有当打开时 Cursor 才能取数据;isConsumed()
:用于判断查询结果是否全部取完;getCurrentIndex()
:返回已经获取了多少条数据。
因为 Cursor 实现了迭代器接口,因此在实际使用当中,从 Cursor 取数据非常简单:
1 | try(Cursor cursor = mapper.querySomeData()) { |
使用 try-resource 方式可以令 Cursor 自动关闭。
3.4 流式查询接口–Cursor实现
首先定义好mapper方法
1 | "select * from employee limit #{limit}") ( |
因为Cursor在取数据的过程中需要保持数据库连接,而 Mapper 方法通常在执行完后连接就关闭了,因此 Cusor 也一并关闭了,这里可能会报错,所以在spring整合的时候,有三种方法可以选择
-
SqlSessionFactory
用 SqlSessionFactory 来手工打开数据库连接。
1
2
3
4
5
6
7
8
9
10"scan/1/{limit}") (
public void scanFoo1(@PathVariable("limit") int limit) throws Exception {
try (
SqlSession sqlSession = sqlSessionFactory.openSession(); // 1
Cursor<Employee> cursor =
sqlSession.getMapper(EmployeeMapper.class).scan(limit) // 2
) {
cursor.forEach(e -> { });
}
}上述代码中,1 处我们开启了一个 SqlSession (实际上也代表了一个数据库连接),并保证它最后能关闭;2 处我们使用 SqlSession 来获得 Mapper 对象。这样才能保证得到的 Cursor 对象是打开状态的
-
TransactionTemplate
在 Spring 中,我们可以用 TransactionTemplate 来执行一个数据库事务,这个过程中数据库连接同样是打开的
1
2
3
4
5
6
7
8
9
10
11
12
13
14"scan/2/{limit}") (
public void scanFoo2(@PathVariable("limit") int limit) throws Exception {
TransactionTemplate transactionTemplate =
new TransactionTemplate(transactionManager); // 1
transactionTemplate.execute(status -> { // 2
try (Cursor<Employee> cursor = EmployeeMapper.scan(limit)) {
cursor.forEach(e -> { });
} catch (IOException e) {
e.printStackTrace();
}
return null;
});
}上面的代码中,1 处我们创建了一个 TransactionTemplate 对象(此处 transactionManager 是怎么来的不用多解释,本文假设读者对 Spring 数据库事务的使用比较熟悉了),2 处执行数据库事务,而数据库事务的内容则是调用 Mapper 对象的流式查询。注意这里的 Mapper 对象无需通过 SqlSession 创建。
-
@Transactional 注解
1
2
3
4
5
6
7"scan/3/{limit}") (
public void scanFoo3(@PathVariable("limit") int limit) throws Exception {
try (Cursor<Employee> cursor = EmployeeMapper.scan(limit)) {
cursor.forEach(e -> { });
}
}本质上和方案二,只在外部调用时生效。在当前类中调用这个方法,依旧会报错。
四、Mybatis 高级操作(一对多、多对一)
1、封装映射
1.1 map(select-记录封装)
1 | public interface EmployeeMapper { |
1.2 自定义结果映射规则(select-resultMap)
1 | <mapper namespace="org.demo.mapper.EmployeeMapper"> |
2、关联查询
实体类和数据库如上述配置
2.1 多对一处理
创建EmployeeMapper
接口文件
1 | public interface EmployeeMapper { |
mapper.xml
文件(多对一或一对一)这里有三种查询方式
- 联合查询
- 指定联合的javaBean对象查询
- 分步查询
1 | <!-- 这里修改自己的mapper --> |
延迟加载
我们每次查询Employee对象的时候,都将一起查询出来。部门信息在我们使用的时候再去查询;分段查询的基础之上加上两个配置:在全局配置文件中配置,实现懒加载
1 | <configuration> |
2.2 一对多处理
创建DepartmentMapper.xml
,一对多关联查询-collection,有两种
- 联合查询
- 分步查询
1 | <!-- 这里修改自己的mapper --> |
3、鉴别器
discriminator鉴别器,创建EmployeeMapper1.xml
1 | <!-- 这里修改自己的mapper --> |
4、动态sql
4.1 简介
MyBatis采用功能强大的基于 OGNL 的表达式来简化操作。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
严格来说,在XML中只有”<”和”&”是非法的,需要转义,其中转义字符
< |
< |
---|---|
> |
> |
& |
& |
' |
’ |
" |
" |
4.2 if判断
1 | <!-- 这里修改自己的mapper --> |
4.3 trim-自定义字符串截取
SQL语句前面或后面多出的and或者**or **where
标签不能解决
- prefix="":前缀:trim标签体中是整个字符串拼串后的结果。
- prefix给拼串后的整个字符串加一个前缀
- prefixOverrides=""
- 前缀覆盖: 去掉整个字符串前面多余的字符
- suffix="":后缀
- suffix给拼串后的整个字符串加一个后缀
- suffixOverrides=""
- 后缀覆盖:去掉整个字符串后面多余的字符
1 | <!--public List<Employee> getEmpsByConditionTrim(Employee employee); --> |
4.4 choose-分支选择
1 | <mapper namespace="com.lun.c04.dynamicsql.DynamicSQLMapper"> |
4.5 foreach-遍历集合
- collection:指定要遍历的集合:
- list类型的参数会特殊处理封装在map中,map的key就叫list
- item:将当前遍历出的元素赋值给指定的变量
- separator:每个元素之间的分隔符
- open:遍历出所有结果拼接一个开始的字符
- close:遍历出所有结果拼接一个结束的字符
- index:索引。遍历list的时候是index就是索引,item就是当前值
- 遍历map的时候index表示的就是map的key,item就是map的值
#{变量名}
就能取出变量的值也就是当前遍历出的元素
1 | <!--public List<Employee> getEmpsByConditionForeach(List<Integer> ids); --> |
4.6 sql抽取可重用的sql片段
抽取可重用的sql片段,方便后面引用:
- sql抽取:经常将要查询的列名,或者插入用的列名抽取出来方便引用
- include来引用已经抽取的sql:
- include还可以自定义一些property,sql标签内部就能使用自定义的属性
- include-property:取值的正确方式${prop},
- 不能使用
#{}
,而使用${}
1 | <!-- 这里修改自己的mapper --> |
五、缓存
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。
MyBatis系统中默认定义了两级缓存,一级缓存和二级缓存。
- 默认情况下,只有一级缓存( SqlSession级别的缓存,也称为本地缓存)开启。
- 二级缓存需要手动开启和配置,他是基于namespace级别的缓存。
- 为了提高扩展性。 MyBatis定义了缓存接口Cache。我们可以通过实现Cache接口来自定义二级缓存
- 对于Mabtis查询,查询过程默认是二级缓存->一级缓存->数据库
六、Mybatis逆向工程
1、逆向工程创建
在pom.xml
中引入逆向工程MBG依赖
1 | <dependencies> |
在项目的resources目录下创建generatorConfig.xml
,修改部分内容,最后执行插件generate目标即可
1 |
|
2、MBG相关类CURD
-
selectByExample
按条件查询,需要传入一个example对象或者null;如果传入一个null,则表示没有条件,也就是查询所有数据
-
example.createCriteria().xxx
创建条件对象,通过andXXX方法为SQL添加查询添加,每个条件之间是and关系
-
example.or().xxx
将之前添加的条件通过or拼接其他条件
-
updateByPrimaryKey
通过主键进行数据修改,如果某一个值为null,也会将对应的字段改为null
-
updateByPrimaryKeySelective()
通过主键进行选择性数据修改,如果某个值为null,则不修改这个字段
七、分页插件
1、插件配置
首先在pom.xml
中引入相关依赖
1 | <!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper --> |
在mybatis-config.xml
设置分页插件
1 | <plugins> |
2、插件使用
2.1 直接输出
在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)
开启分页功能
- pageNum:当前页的页码
- pageSize:每页显示的条数
1 | //访问第一页,每页四条数据 |
2.2 使用PageInfo
在查询获取list集合之后,使用PageInfo pageInfo = new PageInfo<>(List list, intnavigatePages)
获取分页相关数据
- list:分页之后的数据
- navigatePages:导航分页的页码数
1 | PageHelper.startPage(1, 4); |
3、分页常用数据
- pageNum:当前页的页码
- pageSize:每页显示的条数
- size:当前页显示的真实条数
- total:总记录数
- pages:总页数
- prePage:上一页的页码
- nextPage:下一页的页码
- isFirstPage/isLastPage:是否为第一页/最后一页
- hasPreviousPage/hasNextPage:是否存在上一页/下一页
- navigatePages:导航分页的页码数
- navigatepageNums:导航分页的页码,[1,2,3,4,5]
参考资料与其他文章