Mybatis

一、Mybatis 概述

  • 支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。
  • 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
  • MyBatis 可以对配置和原生 Map 使用简单的 XM 或注解,将接口和 JavaPOJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。

1.1.功能架构

  • API接口层:提供给外部使用的接口 API,开发人员通过这些本地API来操纵数据库。接口层一接收到调用请求就会调用数据处理层来完成具体的数据处理。
  • 数据处理层:负责具体的 SQL 查找、SQL 解析、SQL 执行和执行结果映射处理等。它主要的目的是根据调用的请求完成一次数据库操作。
  • 基础支撑层:负责最基础的功能支撑,包括连接管理、事务管理、配置加载和缓存处理,这些都是共用的东西,将他们抽取出来作为最基础的组件。为上层的数据处理层提供最基础的支撑。

1.2.优点

  • 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个 jar 文件+配置几个 sql 映射文件易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。
  • 灵活:mybatis 不会对应用程序或者数据库的现有设计强加任何影响。 sql 写在 xml 里,便于统一管理和优化。通过 sql 基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。
  • 解除 sql 与程序代码的耦合:通过提供 DAL 层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql 和代码的分离,提高了可维护性。
  • 提供映射标签,支持对象与数据库的 orm 字段关系映射
  • 提供对象关系映射标签,支持对象关系组建维护
  • 提供 xml 标签,支持编写动态 sql

1.3.缺点

  • 编写 SQL 语句时工作量很大,尤其是字段多、关联表多时,更是如此。
  • SQL 语句依赖于数据库,导致数据库移植性差,不能更换数据库。
  • 框架还是比较简陋,功能尚有缺失,虽然简化了数据绑定代码,但是整个底层数据库查询实际还是要自己写的,工作量也比较大,而且不太容易适应快速数据库修改。
  • 二级缓存机制不佳

二、简单示例

2.1.pom.xml 导入依赖

jdbc

1
2
3
4
5
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
</dependency>

mybatis

1
2
3
4
5
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>

junit

1
2
3
4
5
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>

2.2.编写 Mybatis 核心配置文件

该文件名称不限,但是要放在 src/main/resource 文件夹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="驱动"/>
<property name="url" value="jdbc:mysql://localhost:3306/数据库名"/> # & 要用 &amp;
<property name="username" value="用户名"/>
<property name="password" value="密码"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="映射文件路径.xml"/>
</mappers>
</configuration>
1
2
Cying 的数据库 url
jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&amp;useSSL=false&amp;serverTimezone=UTC&amp;rewriteBatchedStatements=true

2.3.编写一个实体类

实体类中的属性都应私有,并生成 SetterGetter 方法

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
public class 实体类名 {

private int id;
private String name;
private String pass;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getPass() {
return pass;
}

public void setPass(String pass) {
this.pass = pass;
}
}

2.4.编写获取会话的工具类

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
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.InputStream;

public class 工具类名 {

private static SqlSessionFactory ssf;

static {
try {
InputStream iStream = Resources.getResourceAsStream("核心配置文件名");
ssf = new SqlSessionFactoryBuilder().build(iStream);
} catch (Exception e) {
e.printStackTrace();
}
}

public static SqlSession 获取会话方法名() {
return ssf.openSession();
}

}

2.5.编写会话接口

1
2
3
4
5
6
import java.util.List;

public interface 会话接口名 {
List<类名> 接口方法名();
}

2.6.编写实体类映射文件

1
2
3
4
5
6
7
8
9
10
<?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="会话接口全路径">
<select id="接口方法名" resultType="实体类全路径">
select * from mybatis.user;
</select>
</mapper>

2.7.编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;

public class UserTest {
@Test // @Test 注解来自 junit,标在方法上用于测试某个方法
public void Test() {
SqlSession session = 工具类名.获取会话方法名();
会话接口名 mapper = session.getMapper(会话接口名.class);
List<实体类> list = mapper.接口方法名();
for (实体类 i : list) {
System.out.println(i);
}
session.close();
}
}

2.8.增删改查

对应标签

  • select
  • insert: 插入完成之后记得要提交事务 会话.commit();
  • update: 插入完成之后记得要提交事务 会话.commit();
  • delete: 插入完成之后记得要提交事务 会话.commit();

公用属性

  • id: 对应接口中的方法名
  • resultType: 返回值的类型,如果是自定义实体类,记得写全路径,只有 <insert> 有该属性
  • parameterType: 参数类型
    • 如果是基本类型,在 sql 语句中用 #{属性名} 即可获取到方法里的属性
    • 如果是一个对象,记得写该对象的全路径,在 sql 语句中用 #{属性名} 即可获取该对象的属性

Map插入

1
2
Map<String, Object> map = new HashMap<String, Object>();
map.put("属性1", 3);
1
2
3
<select id="方法名" parameterType="map" resultType="对象全路径">
select * from 数据库.数据表 where 字段1 = #{属性1};
</select>

三、配置解析

3.1.核心配置文件

  • properties
  • setting
  • typeAliases
  • typeHandlers
  • objectFactory
  • plugins
  • environments
    • environment
      • transactionManager
      • dataSource
  • databaseProvider
  • mappers

3.2.环境配置

  • MyBatis 可以配置成适应多种环境,但每个 SqlSession 实例只能选择一种化境
  • 默认的事务管理器是 JDBC
  • 默认的连接池是 POOLED

3.3.属性

  • 可以通过 properties 属性来实现引用配置文件
  • 这些属性都是可以通过外部配置动态替换的,既可以在典型的 Java 文件中配置,也可以用 properties 元素的子元素来传递
  • 当有了 properties 之后,就可以用 ${属性} 来获取属性的值,比如将驱动、url 、用户名、密码独立成配置
  • 当内嵌式和引入式都有同一个属性时,只有引入文件的属性有效

3.3.1.内嵌式

1
2
3
4
<properties>
<property name="key1" value="value1"/>
<property name="key2" value="value2"/>
</properties>

3.3.2.引入式

src/main/resource 文件夹新建 db.properties

1
2
key1: value1
key2: value2

在核心配置文件中

1
<properties resource="db.properties"/>

3.4.别名

3.4.1.设置具体类

1
2
3
<typeAliases>
<typeAlias type="类的全路径" alias="别名"/>
</typeAliases>

3.4.2.设置包

1
2
3
4
5
<typeAliases>
<typeAlias type="包的全路径"/>
</typeAliases>

#实体类的别名就是实体类的首字母小写

3.4.3.注解

  • 在实体类上面加 @Alias("别名") 的注解

3.5.设置

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
<settings>
<setting name="cacheEnabled" value="true"/>
#该配置影响的所有映射器中配置的缓存的全局开关
<setting name="lazyLoadingEnabled" value="true"/>
#延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态
<setting name="multipleResultSetsEnabled" value="true"/>
#是否允许单一语句返回多结果集(需要兼容驱动)。
<setting name="useColumnLabel" value="true"/>
#使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。
<setting name="useGeneratedKeys" value="false"/>
#允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。
<setting name="autoMappingBehavior" value="PARTIAL"/>
#指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
<setting name="defaultExecutorType" value="SIMPLE"/>
#配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。
<setting name="defaultStatementTimeout" value="25"/>
#设置超时时间,它决定驱动等待数据库响应的秒数。
<setting name="defaultFetchSize" value="100"/>
#Sets the driver a hint as to control fetching size for return results. This parameter value can be override by a query setting.
<setting name="safeRowBoundsEnabled" value="false"/>
#允许在嵌套语句中使用分页(RowBounds)。
<setting name="mapUnderscoreToCamelCase" value="false"/>
#是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。
<setting name="localCacheScope" value="SESSION"/>
#MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。
<setting name="jdbcTypeForNull" value="OTHER"/>
#当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
#指定哪个对象的方法触发一次延迟加载。
</settings>

3.6.映射器

1
2
3
<mappers>
<mapper resource="映射文件路径"/>
</mappers>
1
2
3
<mappers>
<package name="映射文件的包路径"/>
</mappers>

3.7.生命周期

image-20200220140219592

3.7.1.SqlSessionFactoryBuilder

  • 一旦创建了SqlSessionFactory, 就不再需要它了

3.7.2.SqlSessionFactory

  • 可以想象为:数据库连接池
  • SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在, 没有任何理由丢弃它或重新创建
    另-一个实例。
  • 因此 SqISessionFactory的最佳作用域是应用作用域。
  • 最简单的就是使用单例模式或者静态单例模式

3.7.3.SqlSession

  • 连接到连接池的一一个请求
  • SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域
  • 用完之后需要赶紧关闭,否则资源被占用

四、resultMap结果集映射

设有表

id pwd
1 123

设有实体类

1
2
3
4
5
6
public class Info {

private int id;
private String password;

}

如果按照普通的方式,查询出来的 password 肯定为 null,因为表中没有 password 这个字段

**解决方法1: ** 取别名

1
2
3
<select id="方法名" resultType="实体类类名" parameterType="?">
select id, pwd as password from 数据库名.数据表名;
</select>

**解决方法2: ** resultMap

1
2
3
4
5
6
7
8
<resultMap id="自定义Map名" type="实体类类名">
<result column="id" property="id"/>
<result column="pwd" property="password"/>
</resultMap>

<select id="方法名" resultMap="自定义Map名" parameterType="?">
select * from 数据库.数据表;
</select>

五、分页

5.1.Limit 分页

1
2
3
<select id="方法名" parameterType="?" resultType="?">
select * from 数据库.数据表 limit #{start}, #{size};
</select>

5.2.RowBounds 分页

六、注解开发

  • 在接口的方法上面直接写 sql 表达式
  • 当接口的方法有多个参数,则要在每个参数前加上 @Param("名字") 便于获取
  • 但是在核心配置文件中要去映射到接口

6.1.@Select()

1
@Select("select * from 表名")
1
@Select("select * from 表名 where #{参数}")

6.2.@Insert()

1
@Insert("insert into 表名 values(#{...}, #{...})")

6.3.@Update()

1
@Update("update 表名 set 字段名=#{...}")
1
@Update("update 表名 set 字段名=#{...} where 条件")

6.4.@Delete()

1
@Delete("delete from 表名 where 条件")

6.5.@Param()

  • 基本类型的参数或者 String 类型需要加上
  • 引用类型不需要
  • 如果只有一个基本类型可以忽略

七、Lombok

7.1.安装插件

7.2.导入依赖

1
2
3
4
5
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>

7.3.使用注解

  • @Getter/@Setter 作用类上,生成所有成员变量的 getter/setter 方法;作用于成员变量上,生成该成员变量的 getter/setter 方法。可以设定访问权限及是否懒加载等
  • @ToString 作用于类,覆盖默认的 toString() 方法,可以通过 of 属性限定显示某些字段,通过 exclude 属性排除某些字段
  • @EqualsAndHashCode 作用于类,覆盖默认的 equalshashCode
  • @NonNull 主要作用于成员变量和参数中,标识不能为空,否则抛出空指针异常
  • @NoArgsConstructor、**@RequiredArgsConstructor@AllArgsConstructor** 作用于类上,用于生成构造函数。有 staticNameaccess 等属性
  • @NoArgsConstructor 生成无参构造器
  • @RequiredArgsConstructor 生成包含 final@NonNull 注解的成员变量的构造器
  • @AllArgsConstructor 生成全参构造器
  • @Data 作用于类上,是以下注解的集合 @ToString、**@EqualsAndHashCode@Getter@Setter@RequiredArgsConstructor**
  • @Builder 作用于类上,将类转变为建造者模式
  • @Log 作用于类上,生成日志变量。针对不同的日志实现产品,有不同的注解
  • @Cleanup 自动关闭资源,针对实现了 java.io.Closeable 接口的对象有效
  • @SneakyThrows 可以对受检异常进行捕捉并抛出
  • @Synchronized 作用于方法级别,可以替换 synchronize 关键字或 lock 锁,用处不大

八、复杂查询

8.1.多对一查询

8.1.1.按查询嵌套处理

1
2
3
4
5
6
7
8
9
10
<select id="方法名" resultMap="映射Map">
select * from student
</select>
<resultMap id="映射Map" type="实体类">
<result property="属性名" column="字段名"/>
<association property="属性名" column="字段名" javaType="属性类型" select="调用的方法名"/>
</resultMap>
<select id="方法名" resultType="实体类">
select * from teacher where id = #{id}
</select>

8.1.2.按结果嵌套处理

1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="方法名" resultMap="映射Map" >
select s.id sid, s.name sname , t.name tname
from student s,teacher t
where s.tid = t.id
</select>

<resultMap id="映射Map" type="实体类">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tname"/>
</association>
</resultMap>

8.2.一对多查询

8.2.1.按查询嵌套处理

1
2
3
4
5
6
7
8
9
<select id="getTeacher2" resultMap="映射Map">
select * from teacher where id = #{id}
</select>
<resultMap id="映射Map" type="Teacher">
<collection property="students" javaType="ArrayList" ofType="Student" column="id" select="子查询"/>
</resultMap>
<select id="子查询" resultType="Student">
select * from student where tid = #{id}
</select>

8.2.2.按结果嵌套处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<select id="getTeacher" resultMap="映射Map">
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>
<resultMap id="映射Map" type="Teacher">
# 如果查询出来的字段和属性名一致,则无需处理,这里着重处理不相同的情况以及一对多的情况
# collection 是获取集合
# javaType 用来标识实体类类型
# ofType 用来标识 List 集合的类型
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>

九、动态 SQL

9.1.IF

  • 当方法里的某个属性不为空,即代表需要追加条件(如果不追加那就不要传这个属性)
1
2
3
4
5
6
<select id="方法名" parameterType="?" resultType="?">
select * from ?
<if test = "某个属性 != null">
and 对应字段 = #{某个属性}
</if>
</select>

9.2.Where

Where-if

相当于正常的 Java 中的 if 语句,如果有多个条件组合判断的话用 andor 连接

1
2
3
4
5
6
7
8
9
10
11
<select id="方法名" parameterType="?" resultType="?">
select * from ?
<where>
<if test="属性1 != null">
对应字段 = #{属性1}
</if>
<if test="属性2 != null">
and 对应字段 = #{属性2}
</if>
</where>
</select>

Where-choose-when-otherwise

  • choose 就好像是switch
  • when 相当于 case ,可以有一种属性的对个判断,但不能同时去判断多个属性。只要有一个 when 满足,则 break
  • otherwise 就好像是 default,如果前面的 when 都不满足,则进入 otherwise
1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="方法名" resultType="?" parameterType="?">
select * from ?
<where>
<choose>
<when test="条件">
and ? = ?
</when>
<otherwise>
and ? = ?
</otherwise>
</choose>
</where>
</select>

9.3.Set

1
2
3
4
5
6
7
8
9
10
11
12
<update id="?" parameterType="?">
update ?
<set>
<if test="条件">
? = #{?},
</if>
<if test="条件">
? = #{?}
</if>
</set>
where ? = #{?};
</update>

9.4.Sql-Include

1
2
3
4
5
6
7
8
9
10
11
12
<sql id="自定义名称">
<if>
...
</if>
</sql>

<select id="方法名" parameterType="?" resultType="?">
select * from ?
<where>
<include refid="自定义名称"></include>
</where>
</select>

9.5.Foreach

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
public void testQueryBlogForeach(){
SqlSession session = 工具类名.获取会话方法名();
? mapper = session.getMapper(?.class);
HashMap map = new HashMap();

List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);

map.put("ids",ids);

List<?> list = mapper.方法名(map);
System.out.println(list);
session.close();
}
1
2
3
4
5
6
7
8
9
10
<select id="方法名" parameterType="map" resultType="?">
select * from ?
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id=#{id}
</foreach>
</where>
</select>

=> select * from ? where (id = ? or id = ? or id = ?)

十、缓存

  • MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地定制和配置缓存。缓存可以极大的提升查询效率。
  • MyBatis 系统中默认定义了两级缓存:一级缓存二级缓存
    • 默认情况下,只有一级缓存开启。( SqlSession 级别的缓存,也称为本地缓存)
    • 二级缓存需要手动开启和配置,他是基于 namespace 级别的缓存。
    • 为了提高扩展性,MyBatis 定义了缓存接口 Cache。我们可以通过实现 Cache 接口来自定义二级缓存

10.1.一级缓存

  • 一级缓存是 SqlSession 级别的缓存(默认是支持一级缓存,不需要再配置文件中配置一级缓存),在操作数据库时
  • 每个 SqlSession 类的实例对象中有一个数据结构(HashMap)可以用来存储缓存数据,不同的 SqlSession 类的实例对象缓存的数据区域(HashMap)是互不影响的。
  • 当在同一个 SqlSession 中执行两次相同的 sql 语句时,第一次执行完毕会将数据写到内存中,第二次查询不执行 sql 直接从内存中获取。

10.2.二级缓存

  • 二级缓存是 Mapper 级别的缓存,多个 SqlSession 类的实例对象操作同一个 Mapper 配置文件中的 sql 语句,多个 SqlSession 类的实例对象可以共用二级缓存,二级缓存是跨 SqlSession 的。
  • 一个 Mapper 有一个自己的二级缓存区域(按照 namespace 划分),两个 Mappernamespace 如果相同,那么这两个Mapper 执行的 sql 查询会被缓存在同一个二级缓存中。
  • 要开启二级缓存需要在配置文件中设置 cacheEnabled属性为 true

开启二级缓存

在核心配置文件配置

1
2
3
<setting name="cacheEnabled" value="true"/>

# 这个默认是开启,只不过一般会显式的给写出来

Mapper 中配置

1
<cache/>

效果

  • 映射语句文件中的所有 select 语句的结果将会被缓存。
  • 映射语句文件中的所有 insertupdatedelete 语句会刷新缓存。
  • 缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
  • 缓存不会定时进行刷新(也就是说,没有刷新间隔)。
  • 缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
  • 缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。

添加配置

1
2
3
4
5
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>

讲解

  • eviction 清除策略
    • LRU – 最近最少使用:移除最长时间不被使用的对象。
    • FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
    • SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。
    • WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。
  • flushInterval(刷新间隔)属性可以被设置为任意的正整数,设置的值应该是一个以毫秒为单位的合理时间量。 默认情况是不设置,也就是没有刷新间隔,缓存仅仅会在调用语句时刷新。
  • size(引用数目)属性可以被设置为任意正整数,要注意欲缓存对象的大小和运行环境中可用的内存资源。默认值是 1024。
  • readOnly(只读)属性可以被设置为 true 或 false。只读的缓存会给所有调用者返回缓存对象的相同实例。 因此这些对象不能被修改。这就提供了可观的性能提升。而可读写的缓存会(通过序列化)返回缓存对象的拷贝。 速度上会慢一些,但是更安全,因此默认值是 false。

10.3.自定义缓存

pom.xml 导入依赖

1
2
3
4
5
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>

mapper.xml 配置

1
<cache type = “org.mybatis.caches.ehcache.EhcacheCache” />

src/main/resources 文件夹新建 ehcache

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。参数解释如下:
user.home – 用户主目录
user.dir – 用户当前工作目录
java.io.tmpdir – 默认临时文件路径
-->
<diskStore path="./tmpdir/Tmp_EhCache"/>

<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>

<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
/*
defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。
只能定义一个。
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统当机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。
仅当eternal=false对象不是永久有效时使用
可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。
最大时间介于创建时间和失效时间之间。
仅当eternal=false对象不是永久有效时使用
默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据
Whether the disk store persists between restarts of the Virtual Machine.
The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。
默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。
默认策略是LRU(最近最少使用)。
你可以设置为FIFO(先进先出)
或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
memoryStoreEvictionPolicy:可选策略有:
LRU(最近最少使用,默认策略)、
FIFO(先进先出)、
LFU(最少访问次数)。
FIFO,first in first out,这个是大家最熟的,先进先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。
如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,
而又需要腾出地方来缓存新的元素的时候,
那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
*/

</ehcache>