mybatis

简介

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 进行关闭。

  1. 绝不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。

  2. 绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。 如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。 换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。

安装

根据官网要求,去maven依赖管理网站,找到依赖,放到pom.xml文件中。

1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>

配置

SqlSessionFactory

SqlSessionFactory 实例是基于 MyBatis 应用的核心。

SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。SqlSessionFactoryBuilder 从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

目前构建方式有两种:

  1. 【推荐】使用xml + java配置文件构建
  2. 只使用java配置文件构建(只用于简单的配置)
【推荐】使用xml + java配置文件构建

XML 配置文件中包含了对 MyBatis 系统的核心设置,具体为:

  • 数据源DataSource
  • 决定事务作用域和控制方式的事务管理器TransactionManager
mybatis-config.xml

一个完整的配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>

<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">

<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
1
2
3
4
#config.properties

driver=org.postgresql.Driver
url=jdbc:postgresql://xxx.xxx.xxx.xxx:5432/fgm

说明:

  1. environment :包含事务管理和连接池的配置。

  2. mappers :包含一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。

  3. 配置会先从.xml文件内部的properties读取,而后再读取外部配置文件中的配置(优先级最高)

  4. 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>

<!-- 自动进行驼峰命名与数据库下划线字段的转换 -->
<setting name="mapUnderscoreToCamelCase" value="false"/>

<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>

<!-- 打印sql日志 -->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

别名:

在sql语句,指定返回类型时候,需要带自定义类型的全路径。可以使用别名进行简化。

1
2
3
4
5
别名简化前
<select id="getKpiList" resultType="domain.blog.Blog">

别名简化后
<select id="getKpiList" resultType="Blog">

当别名配置后,Blog 可以直接使用到任何使用 domain.blog.Blog 的地方。

1
2
3
4
5
6
7
8
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean

1
2
3
<typeAliases>
<package name="domain.blog"/>
</typeAliases>

每一个在包 domain.blog 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 domain.blog.Author 的别名为 author

1
2
3
4
别名简化前
<select id="getKpiList" resultType="domain.blog.Blog">
别名简化后
<select id="getKpiList" resultType="blog">

若有注解,则别名为其注解值。见下面的例子:

1
2
3
4
@Alias("author")
public class Author {
...
}

映射器(mappers)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>

<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
MyBatisConfig.java
1
2
3
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
只使用java配置文件构建

其实就是把xml文件的配置信息用java编写

1
2
3
4
5
6
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

SqlSession

真正去调用sql映射文件,并执行。

映射器是一些绑定映射语句的接口,映射器接口的实例从 SqlSession 中获得。映射器实例应该在调用它们的方法中被获取,使用完毕之后即可丢弃。 最好将映射器放在方法作用域内。

1
2
3
4
5
try (SqlSession session = sqlSessionFactory.openSession()) {
// 映射器
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}

xml映射文件

Mybatis接口方法与真实SQL关联

方法1:注解

  • 适合简单SQL语句
  • 自动提交事务
  • 底层通过反射实现
1
2
@Select("select * from user where id=#{id}")
List<User> getUsers()

方法2:mapper.xml

  • 适合复杂SQL语句

  • 手动提交事务

通常使用mapper.xml的方式。具体如下:

https://mybatis.org/mybatis-3/zh/sqlmap-xml.html

mapper.java

1
2
3
4
package org.mybatis.example;
public interface BlogMapper {
Blog selectBlog(int id);
}

mapper.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>

SQL 映射文件只有很少的几个顶级元素(按照如下顺序列出):

  • cache – 该命名空间的缓存配置。
  • cache-ref – 引用其它命名空间的缓存配置。
  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
  • sql – 可被其它语句引用的可重用语句块。
  • insert – 映射插入语句。
  • update – 映射更新语句。
  • delete – 映射删除语句。
  • select – 映射查询语句。
增删改查
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
List<FgmKpi> getKpiList();                
<select id="getKpiList" resultType="fgmKpi">
select * from t_fg_pm_kpi limit 10
</select>


Boolean insertKpi(FgmKpi fgmKpi);
<insert id="insertKpi">
insert into t_fg_pm_kpi
(uuid, pid, en_name, ch_name, definition, unit)
values
(#{id}, #{pId},#{enName}, #{chName}, #{definition}, #{unit})
</insert>

Boolean updateKpi(Map fgmKpi);
<update id="updateKpi">
update t_fg_pm_kpi
<set>
<if test="definition != null">
definition = #{definition},
</if>
<if test="unit != null">
unit = #{unit}
</if>
</set>
<where>
<if test="id != null">
uuid=#{id}
</if>
</where>
</update>

Boolean deleteKpi(@Param("id") String id);
<delete id="deleteKpi">
delete from t_fg_pm_kpi where uuid=#{id}
</delete>
参数的传递
  1. 基本类型:注解传参。
  2. 引用类型:
    • 使用自定义的Map,SQL会自动解析Map对象的key和value
    • 使用自定义的类,SQL会自动解析类的属性字段key和value
参数的接收
  1. 参数类型

MyBatis 可以通过类型处理器(TypeHandler)自行推断出具体传入语句的参数。
为了可读性,可以使用parameterType指定参数类型:

  1. parameterType属性可选
  2. 默认值为未设置(unset)
  3. 如果有值,则需要类的全路径名,或起别名。
1
2
3
4
<insert
id="insertAuthor"
parameterType="domain.blog.Author"
/>
  1. 参数值的接收
1
2
3
4
5
6
7
8
9
#{id}
#{middleInitial,jdbcType=VARCHAR}

几乎总是可以根据参数对象的类型确定 javaType,除非该对象是一个 HashMap
#{id,javaType=int,jdbcType=NUMERIC}
#{height,javaType=double,jdbcType=NUMERIC,numericScale=2}

HashMap
#{age,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}
返回值

返回值类型,resultType 和 resultMap 只能同时使用一个。

resultType
  1. 返回值的类型,通常 MyBatis 可以推断出来。但是为了更加准确,可以使用。
  2. 任何基本类型都可以,包括字符串。
  3. 若简单返回多个列,则可使用包含期望属性的 Object 或 Map。
  4. 若要返回指定自定义类,则需要返回类的全路径名,或别名。
  5. 如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。
1
2
3
4
<select id="selectUsers" resultType="map" />

<select id="selectUsers" resultType="com.someapp.model.User" />
<select id="selectUsers" resultType="User" /> <!-- 别名 -->
resultMap

resultMap是 MyBatis 最强大的特性。

resultType本质上是由MyBatis 在幕后自动创建一个 ResultMap,再根据属性名来映射列到 JavaBean 的属性上。

当列名和属性名不匹配,有2种方式,且这2种方式可以混用——提供了显式指定之后,未显式指定的字段,仍然采用自动映射。

  1. 自动映射:可以采用原生sql的as + resultType
1
2
3
4
5
6
7
8
<select id="selectUsers" resultType="User">
select
user_id as "id",
user_name as "userName",
hashed_password as "hashedPassword"
from some_table
where id = #{id}
</select>
  1. 显式映射:可以采用resultMap显式指定
1
2
3
4
5
6
7
8
9
10
11
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>

<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>
  1. 混用
1
2
3
4
5
6
7
8
9
10
11
<resultMap id="userResultMap" type="User">
<result property="password" column="hashed_password"/>
</resultMap>
<select id="selectUsers" resultType="User">
select
user_id as "id",
user_name as "userName",
hashed_password
from some_table
where id = #{id}
</select>

resultMap更深入的用法——处理 表关联 的返回值类型

无论是association,还是collection,一定要显式映射字段,否则将不能展示。

  1. association:关联

案例:每个学生关联一位老师。

1
2
3
4
5
6
7
8
9
10
public class Student {
private int id;
private String name;
// !!! Student的属性中,包含Teacher
private Teacher teacher;
}
public class Teacher {
private int id;
private String name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
<resultMap id="StudentTeacher" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<!-- 类型为Teacher的属性teacher,关联具体内容-->
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>
<select id="getStudents" resultMap="StudentTeacher" >
select s.id sid, s.name sname, t.name tname
from student s,teacher t
where s.tid = t.id
</select>
  1. collection:集合

案例:每个老师关联一批学生。

1
2
3
4
5
6
7
8
9
10
public class Teacher {
private int id;
private String name;
// !!! Teacher的属性中,包含List<Student>
private List<Student> students;
}
public class Student {
private int id;
private String name;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<resultMap id="TeacherStudent" type="Teacher">
<result property="name" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="id" column="sid" />
<result property="name" column="sname" />
<result property="tid" column="tid" />
</collection>
</resultMap>

<select id="getTeacher" resultMap="TeacherStudent">
select
s.id sid,
s.name sname,
t.name tname,
t.id tid
from student s,teacher t
where s.tid = t.id and t.id=#{id}
</select>
  • JavaType是用来指定pojo(java bean)中属性的类型
  • ofType指定的是映射到list集合属性中pojo的类型

动态sql标签

https://mybatis.org/mybatis-3/zh/dynamic-sql.html

本质还是sql,只是增加了逻辑判断

where

  1. 如后续没有条件,则不会插入where
  2. 如开头语句为and或or,则自动删除where后的and或or
    1
    2
    3
    4
    select * from blog
    <where>
    and title like #{title}
    </where>

set

  1. 如果没有set后的条件语句,则不会插入set
  2. 删除多余逗号
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    update blog
    <set>
    <if test="title != null">
    title = #{title},
    </if>
    <if test="author != null">
    author = #{author}
    </if>
    </set>
    where id=#{id}

trim

  1. 增加前缀
  2. 前面符号覆盖
  3. 增加后缀
  4. 后面符号覆盖
    1
    <trim prefix="" prefixOverrides="" suffix="" suffixOverrides=""></trim>

替代where

1
<trim prefix="where" prefixOverrides="and | or"></trim>

替代set

1
<trim prefix="set" suffixOverrides="," />

if

  1. if分支,是否显示当前分支
  2. test中书写表达式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    select * from blog
    <where>
    <if test="author != null">
    author like #{author}
    </if>
    <if test="title != null">
    and title like #{title}
    </if>
    </where>

test

如何判断等于条件?
  1. 常量需要==加.toString()来转换,更稳定,推荐。
  2. 其他方式如.equal() 等,都存在风险。
如何判断使用=还是==?

根据变量值的类型来判断。注意char和String不是同一类型。

=一个等号,会把=两边的值自动拆箱成基础数据类型。

  1. 只要变量的值是【基础数值类型】就用“=”。因此只要变量值是【数值型】就用“=”
  2. 当传参的类型是Object的情况下,当Object的值为【单个的大小写字母或一些特殊字符串】会被转换成ASCII码,此时也用”=“。因此只要变量值是单个大写或小写字母就用“=”。

==

  1. 只要变量值是【字符串型】就用==
1
2
3
4
5
6
7
8
9
<if test="circuitType == 1 or circuitType == 2 or circuitType == 3">

<if test="circuitType == '1'.toString() or circuitType == '2'.toString() or circuitType == '3'.toString() or circuitType == '4'.toString()">

<if test="username != null and 'hello'.toString() == username.toString()"></if>

<if test="username != null and username.indexOf('ji') == 0"> </if> <!-- 是否以什么开头 -->
<if test="username != null and username.indexOf('ji') >= 0"> </if> <!-- 是否包含某字符 -->
<if test="username != null and username.lastIndexOf('ji') > 0"></if> <!-- 是否以什么结尾 -->

choose,when,otherwise

  1. switch分支,只选一分支
  2. test中书写表达式
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    select * 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层面操作】

  1. 可遍历任何可迭代对象(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
2
3
4
5
6
7
8
9
10
11
12
// 使用在多个or或and拼接中
select * from blog
<where>
<foreach
collection="list" item="id"
open="and ("
close=")"
separator="or"
>
id=#{id}
</foreach>
</where>

sql + include

  1. 抽取公共片段
  2. 复用

注意:

  1. 最好基于单表来定义sql片段,方便复用
  2. 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
2
3
4
5
<select id="selectBlogsLike" resultType="Blog">
<bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />

SELECT * FROM BLOG WHERE title LIKE #{pattern}
</select>

script

使用场景不多,主要用于注解sql。在带注解的映射器接口类中使用动态 SQL

1
2
3
4
5
6
7
8
9
10
11
12
 @Update({"<script>",
"update Author",
" <set>",
" <if test='username != null'>username=#{username},</if>",
" <if test='password != null'>password=#{password},</if>",
" <if test='email != null'>email=#{email},</if>",
" <if test='bio != null'>bio=#{bio}</if>",
" </set>",
"where id=#{id}",
"</script>"})

void updateAuthorValues(Author author);

占位符

${column} 不能防止sql注入。会被直接替换

#{value} 防止sql注入。会使用 ? 预处理【创建 PreparedStatement 参数占位符,并通过占位符设置参数(就像 ? )】

${}

1
2
3
4
5
6
@Select("select * from user where ${column} = #{value}")

而不是写三个
@Select("select * from user where email = #{email}")
@Select("select * from user where name = #{name}")
@Select("select * from user where id = #{id}")
模糊查询示例

由于$是参数直接注入的,导致大括号里面不能注明jdbcType

1
2
3
4
5
错误的用法
like '%${name, jdbcType=VARCHAR}%'

正确的用法
like '%${name}%'

#{}

模糊查询示例

因为#{…}解析成sql语句时候,会在变量外侧【自动加单引号’ ‘】,所以这里 % 需要使用双引号” “,不能使用单引号 ‘ ‘,不然会查不到任何结果。

1
2
3
4
5
like "%"#{name, jdbcType=VARCHAR}"%"

或者

like CONCAT('%', #{name, jdbcType=VARCHAR},'%')

缓存

  • cache – 该命名空间的缓存配置。
  • cache-ref – 引用其它命名空间的缓存配置。

简介

用途:将查询结果临时存在内存中,减少数据库交互,提高效率

适合:经常查询且稳定不易变的数据

Mybatis缓存:内置事务性查询Select缓存机制。

缓存顺序:

  1. 先查询数据库拿到数据
  2. 存到一级缓存,一级缓存供一次会话中调用
  3. 一次会话结束,一级缓存转存到二级缓存

调用顺序:

  1. 先查询二级缓存
  2. 再查一级缓存
  3. 查询数据库

一级缓存(默认开启)

  1. sqlSession会话级缓存
    • 即,从sqlSession.open到sqlSession.close,同一sql只执行一次
  2. 缓存失效:
    • 只要遇到增删改insert/delete/update,必定缓存失效
    • 手动清理缓存,sqlSession.clearCache()
    • 会自动使用LRU Least Recently Used算法,来清除不必要的缓存

二级缓存

  1. namespace级别缓存(基于mapper.xml)
  2. 当会话关闭,一级缓存消失,才会将一级缓存对象保存到二级缓存中。【转存】
  3. 此时,依赖于namespace的另一个会话开启后,会从二级缓存中读取。

开启二级缓存

  1. 保证全局开启缓存,即设置settings中的cacheEnabled属性为true。(该属性默认已开启)。

    1
    2
    3
    <settings>
    <setting name="cacheEnabled" value="true" />
    </settings>
  2. 在Mapper.xml文件中,增加<cache />标签

    1
    2
    3
    4
    5
    <mapper namespace="">
    <cache />

    <!--其他内容-->
    </mapper>
  3. 单个select语句也可以显示关闭/开启缓存,使用useCache属性

    1
    2
    3
    4
    5
    <mapper namespace="">
    <cache />

    <select id="" useCache="false"></select>
    </mapper>

配置二级缓存

1
2
3
4
5
6
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"
/>
清除策略eviction
类别 中文 描述
LRU(默认) 最近最小使用 移除最长时间没使用的对象
FIFO 先进先出 按对象进入缓存的顺序来移除
SOFT 软引用
WEAK 弱引用
刷新间隔flushInterval

正整数,毫秒

可缓存的引用数目size
  1. 正整数,默认值1024
  2. 最多可缓存的缓存数量
readOnly

只读缓存会给所有调用者返回缓存对象相同的实例,这些对象不能被修改。

二级缓存-自定义缓存

工作缓存基本使用Redis!

一个举例:Ehcache
  1. 安装依赖

    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>
  2. 加入到mybatis的二级缓存中

    1
    <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
  3. 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
2
3
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

如果使用Log4J 等更高级日志工具,则需要以下步骤:

  1. 配置依赖
1
2
3
4
5
6
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
  1. 配置log4j.properties文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
### 设置###
log4j.rootLogger = debug,stdout,D,E

### 输出信息到控制抬 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=E://logs/error.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = .//logs/log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =.//logs/error.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

# MyBatis 日志配置
#log4j.logger.org.mybatis.example.BlogMapper=TRACE

#为了实现更细粒度的日志输出,你也可以只打印特定语句的日志。以下配置将只打印语句 selectBlog 的日志:
#log4j.logger.org.mybatis.example.BlogMapper.selectBlog=TRACE

#你也可以打印一组映射器的日志,只需要打开映射器所在的包的日志功能即可
#log4j.logger.org.mybatis.example=DEBUG
  1. Mybatis配置
1
2
3
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
  1. 使用。mybatis中的sql文件已经可以使用了。如果想在方法中使用日志,如下:
1
2
3
4
5
6
7
8
9
import org.apache.log4j.Logger;
public class FgmKpiMapperTest {
static Logger logger = Logger.getLogger(FgmKpiMapperTest.class);
public void getKpiList() {
logger.info("--------info------");
logger.debug("--------debug------");
logger.error("--------error------");
}
}

分页

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/

-------------Keep It Simple Stupid-------------
0%