简介
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。
MyBatis 免除了几乎所有的 JDBC 代码、设置参数、获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口、Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
官网讲的非常清楚。加载页面比较慢,可以慢慢等到。
作用域(Scope)和生命周期
SqlSessionFactoryBuilder
只是用来创建SqlSessionFactory,一旦创建了 SqlSessionFactory,就不再需要。
可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好用完就丢弃。
因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。
SqlSessionFactory
SqlSessionFactory 一被创建就该在应用运行期间一直存在,不能丢弃或重新创建另一个实例。
最佳实践是在应用运行期间不要重复创建多次。
因此 SqlSessionFactory 的最佳作用域是应用作用域。 可以采用单例模式或静态单例模式来实现。
SqlSession
每个线程都应该创建自己的 SqlSession 实例。
不是线程安全的,不能被共享的,所以它的最佳的作用域是请求或方法作用域。
每次用完,应该及时关闭SqlSession 实例。可以try finaly 进行关闭。
绝不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。
绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。
安装
根据官网要求,去maven依赖管理网站,找到依赖,放到pom.xml文件中。
1 | <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> |
配置
SqlSessionFactory
SqlSessionFactory 实例是基于 MyBatis 应用的核心。
SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。SqlSessionFactoryBuilder 从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。
目前构建方式有两种:
- 【推荐】使用xml + java配置文件构建
- 只使用java配置文件构建(只用于简单的配置)
【推荐】使用xml + java配置文件构建
XML 配置文件中包含了对 MyBatis 系统的核心设置,具体为:
- 数据源DataSource
- 决定事务作用域和控制方式的事务管理器TransactionManager
mybatis-config.xml
一个完整的配置文件
1 |
|
1 | #config.properties |
说明:
environment :包含事务管理和连接池的配置。
mappers :包含一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。
配置会先从.xml文件内部的properties读取,而后再读取外部配置文件中的配置(优先级最高)
MyBatis 3.4.2以上版本,使用:可为占位符指定默认值。该特性默认关闭,需要进行配置。
1
2
3
4
5
6
7
8
9
10
11<!-- 使用 -->
<property name="driver" value="${driver:org.postgresql.Driver}"/>
<!-- 配置 -->
<properties resource="org/mybatis/example/config.properties">
<!-- 启用默认值特性 -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
<!-- 自定义修改默认值的分隔符,默认是:冒号 -->
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>
</properties>
设置文件
这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了常见设置中各项设置的含义、默认值等。
https://mybatis.org/mybatis-3/zh/configuration.html
设置名 | 描述 | 有效值 | 默认值 | ||||||
---|---|---|---|---|---|---|---|---|---|
cacheEnabled | 全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。 | true \ | false | true | |||||
mapUnderscoreToCamelCase | 是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。 | true \ | false | False | |||||
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J \ | LOG4J \ | LOG4J2 \ | JDK_LOGGING \ | COMMONS_LOGGING \ | STDOUT_LOGGING \ | NO_LOGGING | 未设置 |
logPrefix | 指定 MyBatis 增加到日志名称的前缀。 | 任何字符串 | 未设置 |
1 | <settings> |
别名:
在sql语句,指定返回类型时候,需要带自定义类型的全路径。可以使用别名进行简化。
1 | 别名简化前 |
当别名配置后,Blog
可以直接使用到任何使用 domain.blog.Blog
的地方。
1 | <typeAliases> |
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean
1 | <typeAliases> |
每一个在包 domain.blog
中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author
的别名为 author
;
1 | 别名简化前 |
若有注解,则别名为其注解值。见下面的例子:
1 | "author") ( |
映射器(mappers)
1 | <!-- 将包内的映射器接口实现全部注册为映射器 --> |
MyBatisConfig.java
1 | String resource = "org/mybatis/example/mybatis-config.xml"; |
只使用java配置文件构建
其实就是把xml文件的配置信息用java编写
1 | DataSource dataSource = BlogDataSourceFactory.getBlogDataSource(); |
SqlSession
真正去调用sql映射文件,并执行。
映射器是一些绑定映射语句的接口,映射器接口的实例从 SqlSession 中获得。映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。 最好将映射器放在方法作用域内。
1 | try (SqlSession session = sqlSessionFactory.openSession()) { |
xml映射文件
Mybatis接口方法与真实SQL关联
方法1:注解
- 适合简单SQL语句
- 自动提交事务
- 底层通过反射实现
1 | @Select("select * from user where id=#{id}") |
方法2:mapper.xml
适合复杂SQL语句
手动提交事务
通常使用mapper.xml的方式。具体如下:
https://mybatis.org/mybatis-3/zh/sqlmap-xml.html
mapper.java
1 | package org.mybatis.example; |
mapper.xml
1 |
|
SQL 映射文件只有很少的几个顶级元素(按照如下顺序列出):
cache
– 该命名空间的缓存配置。cache-ref
– 引用其它命名空间的缓存配置。resultMap
– 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。parameterMap
– 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。sql
– 可被其它语句引用的可重用语句块。insert
– 映射插入语句。update
– 映射更新语句。delete
– 映射删除语句。select
– 映射查询语句。
增删改查
1 | List<FgmKpi> getKpiList(); |
参数的传递
- 基本类型:注解传参。
- 引用类型:
- 使用自定义的Map,SQL会自动解析Map对象的key和value
- 使用自定义的类,SQL会自动解析类的属性字段key和value
参数的接收
MyBatis 可以通过类型处理器(TypeHandler)自行推断出具体传入语句的参数。
为了可读性,可以使用parameterType
指定参数类型:
- parameterType属性可选
- 默认值为未设置(unset)
- 如果有值,则需要类的全路径名,或起别名。
1 | <insert |
1 | #{id} |
返回值
返回值类型,resultType 和 resultMap 只能同时使用一个。
resultType
- 返回值的类型,通常 MyBatis 可以推断出来。但是为了更加准确,可以使用。
- 任何基本类型都可以,包括字符串。
- 若简单返回多个列,则可使用包含期望属性的 Object 或 Map。
- 若要返回指定自定义类,则需要返回类的全路径名,或别名。
- 如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。
1 | <select id="selectUsers" resultType="map" /> |
resultMap
resultMap是 MyBatis 最强大的特性。
resultType本质上是由MyBatis 在幕后自动创建一个 ResultMap,再根据属性名来映射列到 JavaBean 的属性上。
当列名和属性名不匹配,有2种方式,且这2种方式可以混用——提供了显式指定之后,未显式指定的字段,仍然采用自动映射。
- 自动映射:可以采用原生sql的
as
+resultType
:
1 | <select id="selectUsers" resultType="User"> |
- 显式映射:可以采用resultMap显式指定
1 | <resultMap id="userResultMap" type="User"> |
- 混用
1 | <resultMap id="userResultMap" type="User"> |
resultMap更深入的用法——处理 表关联 的返回值类型
无论是association,还是collection,一定要显式映射字段,否则将不能展示。
- association:关联
案例:每个学生关联一位老师。
1 | public class Student { |
1 | <resultMap id="StudentTeacher" type="Student"> |
- collection:集合
案例:每个老师关联一批学生。
1 | public class Teacher { |
1 | <resultMap id="TeacherStudent" type="Teacher"> |
- JavaType是用来指定pojo(java bean)中属性的类型
- ofType指定的是映射到list集合属性中pojo的类型
动态sql标签
https://mybatis.org/mybatis-3/zh/dynamic-sql.html
本质还是sql,只是增加了逻辑判断
where
- 如后续没有条件,则不会插入where
- 如开头语句为and或or,则自动删除where后的and或or
1
2
3
4select * from blog
<where>
and title like #{title}
</where>
set
- 如果没有set后的条件语句,则不会插入set
- 删除多余逗号
1
2
3
4
5
6
7
8
9
10update blog
<set>
<if test="title != null">
title = #{title},
</if>
<if test="author != null">
author = #{author}
</if>
</set>
where id=#{id}
trim
- 增加前缀
- 前面符号覆盖
- 增加后缀
- 后面符号覆盖
1
<trim prefix="" prefixOverrides="" suffix="" suffixOverrides=""></trim>
替代where1
<trim prefix="where" prefixOverrides="and | or"></trim>
替代set1
<trim prefix="set" suffixOverrides="," />
if
- if分支,是否显示当前分支
- test中书写表达式
1
2
3
4
5
6
7
8
9select * from blog
<where>
<if test="author != null">
author like #{author}
</if>
<if test="title != null">
and title like #{title}
</if>
</where>
test
如何判断等于条件?
- 常量需要
==
加.toString()来转换,更稳定,推荐。 - 其他方式如.equal() 等,都存在风险。
如何判断使用=还是==?
根据变量值的类型来判断。注意char和String不是同一类型。
=
一个等号,会把=
两边的值自动拆箱成基础数据类型。
- 只要变量的值是【基础数值类型】就用“=”。因此只要变量值是【数值型】就用“=”
- 当传参的类型是Object的情况下,当Object的值为【单个的大小写字母或一些特殊字符串】会被转换成ASCII码,此时也用”=“。因此只要变量值是单个大写或小写字母就用“=”。
==
- 只要变量值是【字符串型】就用==
1 | <if test="circuitType == 1 or circuitType == 2 or circuitType == 3"> |
choose,when,otherwise
- switch分支,只选一分支
- test中书写表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16select * from blog
<where>
<choose>
<when test="title != null">
title like #{title}
</when>
<when test="author != null">
and author like #{author}
</when>
<otherwise>
and views like #{views}
</otherwise>
</choose>
</where>
foreach
【如果sql较复杂,建议还是在java层面操作】
- 可遍历任何可迭代对象(List/set等,index是索引,item是元素)、Map对象(index是键, item是值)
1
2
3
4
5
6
7
8
9
10
11// 使用在in中
select * from blog
where id in
<foreach
collection="list" item="item" index="index"
open="("
close=")"
separator=","
>
#{item}
</foreach>
1 | // 使用在多个or或and拼接中 |
sql + include
- 抽取公共片段
- 复用
注意:
- 最好基于单表来定义sql片段,方便复用
- sql片段中不要存在where标签,方便复用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15// 定义
<sql id="common-sql">
<if test="author != null">
author like #{author}
</if>
<if test="title != null">
and title like #{title}
</if>
</sql>
// 调用
select * from blog
<where>
<include refid="common-sql"></include>
</where>
bind
可以在表达式以外,创建一个变量,并将其绑定到当前的上下文。
如下示例,将接收到的参数进行格式化处理,得到新的变量pattern
1 | <select id="selectBlogsLike" resultType="Blog"> |
script
使用场景不多,主要用于注解sql。在带注解的映射器接口类中使用动态 SQL。
1 | "<script>", ({ |
占位符
${column}
不能防止sql注入。会被直接替换
#{value}
防止sql注入。会使用 ?
预处理【创建 PreparedStatement
参数占位符,并通过占位符设置参数(就像 ? )】
${}
1 | "select * from user where ${column} = #{value}") ( |
模糊查询示例
由于$是参数直接注入的,导致大括号里面不能注明jdbcType
1 | 错误的用法 |
#{}
模糊查询示例
因为#{…}解析成sql语句时候,会在变量外侧【自动加单引号’ ‘】,所以这里 % 需要使用双引号” “,不能使用单引号 ‘ ‘,不然会查不到任何结果。
1 | like "%"#{name, jdbcType=VARCHAR}"%" |
缓存
cache
– 该命名空间的缓存配置。cache-ref
– 引用其它命名空间的缓存配置。
简介
用途:将查询结果临时存在内存中,减少数据库交互,提高效率
适合:经常查询且稳定不易变的数据
Mybatis缓存:内置事务性查询Select缓存机制。
缓存顺序:
- 先查询数据库拿到数据
- 存到一级缓存,一级缓存供一次会话中调用
- 一次会话结束,一级缓存转存到二级缓存
调用顺序:
- 先查询二级缓存
- 再查一级缓存
- 查询数据库
一级缓存(默认开启)
- sqlSession会话级缓存
- 即,从sqlSession.open到sqlSession.close,同一sql只执行一次
- 缓存失效:
- 只要遇到增删改insert/delete/update,必定缓存失效
- 手动清理缓存,sqlSession.clearCache()
- 会自动使用LRU Least Recently Used算法,来清除不必要的缓存
二级缓存
- namespace级别缓存(基于mapper.xml)
- 当会话关闭,一级缓存消失,才会将一级缓存对象保存到二级缓存中。【转存】
- 此时,依赖于namespace的另一个会话开启后,会从二级缓存中读取。
开启二级缓存
保证全局开启缓存,即设置settings中的cacheEnabled属性为true。(该属性默认已开启)。
1
2
3<settings>
<setting name="cacheEnabled" value="true" />
</settings>在Mapper.xml文件中,增加
<cache />
标签1
2
3
4
5<mapper namespace="">
<cache />
<!--其他内容-->
</mapper>单个select语句也可以显示关闭/开启缓存,使用
useCache
属性1
2
3
4
5<mapper namespace="">
<cache />
<select id="" useCache="false"></select>
</mapper>
配置二级缓存
1 | <cache |
清除策略eviction
类别 | 中文 | 描述 |
---|---|---|
LRU(默认) | 最近最小使用 | 移除最长时间没使用的对象 |
FIFO | 先进先出 | 按对象进入缓存的顺序来移除 |
SOFT | 软引用 | |
WEAK | 弱引用 |
刷新间隔flushInterval
正整数,毫秒
可缓存的引用数目size
- 正整数,默认值1024
- 最多可缓存的缓存数量
readOnly
只读缓存会给所有调用者返回缓存对象相同的实例,这些对象不能被修改。
二级缓存-自定义缓存
工作缓存基本使用Redis!
一个举例:Ehcache
安装依赖
1
2
3
4
5
6
7<!--mybatis-encache-->
<!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.2.1</version>
</dependency>加入到mybatis的二级缓存中
1
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
Ehcache配置项
1
ehcache.xml
日志
MyBatis 内置日志工厂会基于【运行时检测信息】选择日志委托实现。它会按下面罗列的顺序,使用第一个查找到的实现。当没有找到这些实现时,将会禁用日志功能。
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j
- JDK logging
即,当项目中已经包含了 Commons Logging,在这种配置环境下,MyBatis 会默认把 Commons Logging 作为日志工具。
如果你的应用部署在一个类路径已经包含 Commons Logging 的环境中,而你又想使用其它日志实现,你可以通过在 MyBatis 配置文件 mybatis-config.xml 里面添加一项 setting 来选择其它日志实现。可选的值有:SLF4J、LOG4J、LOG4J2、JDK_LOGGING、COMMONS_LOGGING、STDOUT_LOGGING、NO_LOGGING,
如果使用内置的日志工具STDOUT_LOGGING(仅控制台查看,不能输出文件),只需如下即可。
1 | <settings> |
如果使用Log4J 等更高级日志工具,则需要以下步骤:
- 配置依赖
1 | <!-- https://mvnrepository.com/artifact/log4j/log4j --> |
- 配置log4j.properties文件
1 | ### 设置### |
- Mybatis配置
1 | <settings> |
- 使用。mybatis中的sql文件已经可以使用了。如果想在方法中使用日志,如下:
1 | import org.apache.log4j.Logger; |
分页
graph LR A(分页) --> B(RowBounds面向对象,陈旧) A --> C(sql limit手动,效率最高) A -->D(插件PageHelper) D --> D1(https://pagehelper.github.io/)
参考
官网:https://mybatis.org/mybatis-3/zh/index.html
maven依赖包官网:https://mvnrepository.com/