mybatis

关于MyBatis的增删改查实现

在MyBatis系列(一)中涉及了如何创建一个入门级的mybatis测试。

但是只涉及了查询单行数据,接下来 我把它补全。

上次采用的是MySQL数据库 ,这次我采用的是Oracle。从官网下到了Oracle6的一个jar.

环境:

eclipse 2019-12

Oracle 11g

apache tomcat 9.0.3

mybatis 3.5.4.jar

Oracle.jar

在Oracle中建个表

create table (stuno number,stuname varchar(20),stuage number,graname varchar(20));,

随变插入几行数据,此处就不放图了🈚。

在eclipse里构建这个类

此处省略代码,详见MyBatis系列(一)。

映射

新建一个studentMapper.xml(当然是要映射了🍔)。

新建conf.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
27
28
29
30
31
32
33
34
35
36
37
<configuration>
<environments default="development">
<!-- 通过default选择 数据库环境(说白了就是运行哪个数据库) -->
<environment id="development">
<!-- transactionManager事物的提交方式
JDBC 利用JDBC处理事务,手动(commit rollback,close)
MANAGED:将事务交由其他组件(spring,jobss)去托管 ,默认使用完之后会关闭连接
<property name="closeConnection" value="false" />这样就默认不关闭
-->
<transactionManager type="JDBC" />
<!-- 数据源类型:
POOLED:使用数据链接池
UNPOOLED:不用连接池,传统JDBC,每次访问数据库均需要打开和关闭数据库
JNDI:从tomcat中获取一个内置的数据库链接池(数据库链接池-数据源)
-->
<dataSource type="POOLED">
<property name="driver" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:ORCL" />
<property name="username" value="scott" />
<property name="password" value="tiger" />
</dataSource>
</environment>
<!--因为我远端也有个数据库所以我可以再配一个环境-->
<environment id="cloud">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://eshyee,top:3306/test?useSSL=false&amp;serverTimezone=UTC" />
<property name="username" value="我的用户名" />
<property name="password" value="我的密码" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="top/eshyee/entity/studentMapper.xml" />
</mappers>
</configuration>

关于Mapper.xml的新增代码,无非就是增删改查✨

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<select id="queryStudentBystuNum" resultType="top.eshyee.entity.Student" parameterType="int">
select * from student where stuno= #{stuno}
</select>
<select id="queryAllStudent" resultType="top.eshyee.entity.Student">
select * from student
</select>
<insert id="addStudent" parameterType="top.eshyee.entity.Student" >
insert into student(stuno,stuname,stuage,graname) values(#{stuNo},#{stuName},#{stuAge},#{graName})
</insert>
<update id="upDateStuByNo" parameterType="top.eshyee.entity.Student">
update student set stuname=#{stuName},stuage=#{stuAge},graname=#{graName} where stuno=#{stuNo}
</update>
<delete id="deleteStuByNo" parameterType="int">
delete from student where stuno=#{stuno}
</delete>

因为查之前说了,这里就拿改学生信息来做个小测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 修改学生--按学号
public static void updateStuByNo() throws IOException {
Reader reader = Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sessionfactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionfactory.openSession();
String statement = "top.eshyee.entity.studentMapper.upDateStuByNo";
Student stu=new Student(2,"小牛",33,"二年级");
int count = session.update(statement, stu);
session.commit();
System.out.println("修改成功," + count + "行受影响");
session.close();
}

public static void main(String[] args) throws IOException {
selectAll();
updateStuByNo();
selectAll();
}

测试,得到结果,注意要提交。


mapper动态代理(MyBatis接口开发)

原则:约定优于配置

配置方式:

​ abc.xml

硬编码方式

​ abc.java

约定:默认值就是myProject

与传统配置的不同之处:省略掉statement,即根据约定直接定位出sql语句。

约定:

1
2
3
4
5
/*
* 1.方法名和mapper.xml文件中的id值相同
* 2.方法名的输入参数和mapper.xml文件中的标签parameterType类型一致
* 3.方法的返回值和mapper.xml文件中的resultType一致
*/

接口和mapper一一对应:namespace的value=接口的全类名

构建接口

所以说新建一个接口,根据前一个篇的mapper.xml的文件基础上改,注:我这里是把接口建在了mapper包下

1
2
3
4
5
Student queryStudentBystuNum(int stuno);
List<Student> queryAllStudent();
int addStudent(Student student);
int upDateStuByNo(Student student);
int deleteStuByNo(int stuno);

注意这里的接口要遵循上面的约定。

更改xml文件

再mapper.xml file中,namespace也应该改成接口的名字

1
<mapper namespace="top.eshyee.mapper.StudentMapper">

接下来,就是修改测试类,曾经使用的statement的地方,目前就不用使用了。

使用接口

这次拿删除学生举例,首先要从session中获取到这个接口,使用getMapper方法。然后从这个接口中要我刚刚写的那个删除的方法deleteStuByNo(),这里我写死删除no为3的 倒霉孩子

1
2
3
4
5
6
7
8
Reader reader = Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sessionfactory = new SqlSessionFactoryBuilder().build(reader);
SqlSession session = sessionfactory.openSession();
StudentMapper stuMapper = session.getMapper(StudentMapper.class);
int count = stuMapper.deleteStuByNo(3);
session.commit();
System.out.println("删除成功," + count + "行受影响");
session.close();

测试一下

3号学生被删除并想你抛了一行log🌚

总结

约定相对于配置来说,出错率更少了,因为不用写statement,而是通过调用接口去实现xml的id的调用,对于写statement的String容易手残的老哥来说,是不错的选择。


关于一些优化🍔

数据库连接配置

config.xml中如果配置了很多的东西,这时在想去更改数据库配置的一些参数显得尤为麻烦,这是可以新建一个properties来写一些kv对,再在config中使用<properties>标签来传值貌似就会简化一部分操作。

因此在src下新建一个db.properties文件。如下配置:

1
2
3
4
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@127.0.0.1:1521:ORCL
username=scott
password=tiger

config.xml中添加

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

数据源中配置:

1
2
3
4
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />

如此,每次想对数据源做更改的时候只需改变kv对即可。

mybatis的全局参数设置更改

谨慎更改👨‍🔧

1
2
3
<settings>
<setting name="" value=""/>
</settings>

设置别名

top.eshyee.entity.Student太长了🤦‍♀️,想要改别名。

(大小写不敏感)

选择两种方式:

设置单个别名
1
2
3
<typeAliases>
<typeAlias type="top.eshyee.entity.Student" alias="student"/>
</typeAliases>
批量设置别名
1
2
3
<typeAliases>
<package name="top.eshyee.entity"/>
</typeAliases>

还有一些内置别名。

小节

怎么方便怎么来呗🏊‍♀️


关于类型处理器和resultmap

类型处理器(类型转换器)

  1. MyBatis自带一些常见的类型处理器

  2. 也可以自定义Mybatis类型处理器

JAVA 数据类型 –数据库(数据类型)

比如:

实体类 Student :Boolean stuSex true:男 /false:女

表中Student : number stuSex 1:男 / 0:女

自定义类型转换器

假设

我要在Student表里面新建一个性别列 男生用1 表示 女生用0表示(number类型),

此时实体类中我男生用的true 女生用的false(Boolean,仅仅是举个例子)。

数据类型不匹配此时数据类型不匹配。

创建类型转换器

需要实现TypeHandler接口 此接口有一个实现类 BaseTypeHandler

因此实现转换器有两种方法 实现 接口TypeHandler 和 继承BaseTypeHandler(简单)

所以这里采用后者,去extendthis method。

准备工作

新建一个Boolean的属性 叫做stuSex 。

private Boolean stuSex形成get set方法

转换器

新建一个转换器继承BaseTypeHandler,编译器会自动生成三个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//	get是DB数据-->java数据
public Boolean getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getInt(columnName)==1?true:false;
}

public Boolean getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getInt(columnIndex)==1?true:false;
}

public Boolean getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getInt(columnIndex)==1?true:false;
}

// set是java数据-->DB数据
public void setNonNullParameter(PreparedStatement ps, int i, Boolean parameter, JdbcType jdbcType) throws SQLException {

if(parameter) {
ps.setInt(i, 1);
}else {
ps.setInt(i, 0);
}
}

其中,

ps:PreparedStatement对象
i:PreparedStatement对象操作的参数的位置
parameter:Java值
jdbcType:jdbc操作的数据类型

这里用到了三元运算符,想起了之前一个写个人网站用到的一个三元运算符🤦‍♂️(更)。

conf配置

在config中加上转换器

1
2
3
<typeHandlers>
<typeHandler handler="转换器类名" javaType="Boolean" jdbcType="INTEGER"/>
</typeHandlers>

这样就算是好了 测试一下

mapper配置

使用了转换器的查询
1如果类中属性和表中的字段类型都能合理识别(String-varchar2),则可以使用resultType resultType=”top.eshyee.entity.Student”
否则(boolean-integer)使用resultMap
2如果类中的属性名和表中的字段名都能够合理识别(stuNo-stuno)则可以使用resultType
否则(id-stuno)使用resultMap

1
2
3
4
5
6
7
8
9
10
<select id="queryStudentBystuNumConverter" resultMap="studentResult" parameterType="int">
select * from student where stuno= #{stuno}
</select>
<resultMap type="top.eshyee.entity.Student" id="studentResult">
<id property="stuNo" column="stuno"/>
<result property="stuName" column="stuname"/>
<result property="stuAge" column="stuage"/>
<result property="graName" column="graname"/>
<result property="stuSex" column="stusex" javaType="boolean" jdbcType="INTEGER"/>
</resultMap>

resultMap

resultMap可以实现两个功能:
1 类型转换
2 属性-字段名之间的映射关系

分为主键id和非主键result
如果实体类中的属性跟数据库中那个的字段名叫法不一样,从下面的映射关系中也可以更改 。

这样就好了,可以在test.java中测试一下了。🥐

小结

以前用过别的方法来转换数据库数据与java数据 但是这个更系统一些吧。


输入参数和输出参数

输入参数parameterType

简单类型

${}#{}比较

#{任意值}或者${value}(标识符只能是value)

#{}会自动给String加上’’(自动类型转换)

${}会原样输出,但是适合于动态排序(动态字段)

#{}可以防止sql注入

${}不可以

${}#{}相同之处

都可以获取对象的值,(嵌套类型对象)

对象类型

#{属性名}或者${属性名}(对象的属性名例如stuNo)

1
2
3
4
<select id="queryStudentByAddress" resultType="top.eshyee.entity.Student" parameterType="address">
select stuno,stuname,stuage from student
where homeaddress=#{homeAddress} or schooladdress='${schoolAddress}'
</select>

嵌套类型输入参数为级联属性

1
2
3
4
5
6
7
<select id="queryStudentByAddress" resultType="top.eshyee.entity.Student" parameterType="Student">
select stuno,stuname,stuage from student where homeaddress=#{address.homeAddress} or schooladdress='${address.schoolAddress}'
</select>
<select id="queryStudentBystuNameOrAge" resultType="top.eshyee.entity.Student" parameterType="Student">
select stuno,stuname,stuage from student
where stuname like '%${stuName}%' or stuage=#{stuAge}
</select>

传入为hashmap

1
2
3
4
<select id="queryStudentBystuNameOrAgewithHashmap" resultType="top.eshyee.entity.Student" parameterType="HashMap">
select stuno,stuname,stuage from student
where stuname like '%${stuName}%' or stuage=#{stuAge}
</select>

调用存储过程

存储过程的传入参数在mybatis中用map来传递(Hashmap)
CALLABLE设置sql 的调用方式是存储过程
输出参数通过map的get方法获取

查询某个年级的所有学生总数

数据库里
1
2
3
4
5
6
create or replace procedure queryCountByGradeWithProcadure(gName in varchar,scount out number )
as
begin
select count(1) into scount from student where graname=gname;
end;
/
mapper中
1
2
3
4
5
<select id="queryCountByGradeWithPrecadure" statementType="CALLABLE" parameterType="HashMap">
{CALL queryCountByGradeWithProcadure(
#{gName,jdbcType=VARCHAR,mode=IN},
#{sCount,jdbcType=INTEGER,mode=OUT} )}
</select>

根据学号删除学生

数据库里
1
2
3
4
5
6
create or replace procedure deleteStuBystunoWithProcedure(sno in number)
as
begin
delete from student where stuno=sno;
end;
/
mapper中
1
2
3
<delete id="deletestuByStunoWithPrecadure" statementType="CALLABLE" parameterType="HashMap">
{CALL deleteStuBystunoWithProcedure(#{sno,jdbcType=INTEGER,mode=IN})}
</delete>

输出参数

resultType

简单类型,实体对象类型,实体对象类型的集合之前有提到过,此处不再赘述。

输出为HashMap

通过别名作为map的key

1
2
3
<select id="queryStudentWithHash" resultType="HashMap">
select stuno "no",stuname "name" from student
</select>

resultMap

解决实体类型属性与数据表字段名不一致(前面有用到)

也可以使用HashMap+resultType


关于动态sql

if

按姓名和年龄查询

1
2
3
4
5
6
7
8
9
10
11
<select id="qStuByNOrAWithSQLTag" parameterType="top.eshyee.entity.Student" resultType="top.eshyee.entity.Student">
select stuno ,stuname from student where 1=1
<if test="stuName!=null and stuName!= ''">
student有stuname属性且不为null
and stuname=#{stuName}
</if>
<if test="stuAge!=null and stuAge!=0">
student有stunage属性且不为null
and stuage=#{stuAge}
</if>
</select>

这里and会出问题

where

where:处理第一个and

1
2
3
4
5
6
7
8
9
10
11
<select id="qStuByNOrAWithSQLTag" parameterType="top.eshyee.entity.Student" resultType="top.eshyee.entity.Student">
select stuno ,stuname from student
<where>
<if test="stuName!=null and stuName!= ''">
and stuname=#{stuName}
</if>
<if test="stuAge!=null and stuAge!=0">
and stuage=#{stuAge}
</if>
</where>
</select>

foreach

查询学号为1 2 4的学生学号信息

迭代的类型:数组、对象数组、集合、属性

数组

数组固定写法:array 这是约定

1
2
3
4
5
6
7
8
9
10
<select id="queryStuwithNoWitharray" resultType="top.eshyee.entity.Student" parameterType="int[]">
select * from student
<where>
<if test="array!=null and array.length>0">
<foreach collection="array" open="and stuno in(" close=")" item="stuNo" separator=",">
#{stuNo}
</foreach>
</if>
</where>
</select>

放入对象的属性中

1
2
3
4
5
6
7
8
9
10
<select id="queryStuwithNoInGra" resultType="top.eshyee.entity.Student" parameterType="top.eshyee.entity.Grade">
select * from student
<where>
<if test="stuNos!=null and stuNos.size>0">
<foreach collection="stuNos" open="and stuno in(" close=")" item="stuNo" separator=",">
#{stuNo}
</foreach>
</if>
</where>
</select>

集合

1
2
3
4
5
6
7
8
9
10
<select id="queryStuwithNowithlist" resultType="top.eshyee.entity.Student" parameterType="top.eshyee.entity.Grade">
select * from student
<where>
<if test="list!=null and list.size>0">
<foreach collection="list" open="and stuno in(" close=")" item="stuNo" separator=",">
#{stuNo}
</foreach>
</if>
</where>
</select>

对象数组

必须使用Object[]

1
2
3
4
5
6
7
8
<select id="queryStuwithNowithObjArr" 
resultType="top.eshyee.entity.Student" parameterType="Object[]">
select * from student
<include refid="objectArraStuno"></include>
<!-- 如果sql片段不在一个文件
<include refid="top.eshyee.mapper.StudentMapper.objectArraStuno"></include>
-->
</select>

sql片段

重复使用的提取出来

1
2
3
4
5
6
7
8
9
<sql id="objectArraStuno">
<where>
<if test="array!=null and array.length>0">
<foreach collection="array" open="and stuno in(" close=")" item="student" separator=",">
#{student.stuNo}
</foreach>
</if>
</where>
</sql>

关联查询

关于关联查询主要重点是配置好mapper

一对一

a.业务扩展类

核心:用resultType指定的类的属性包含多表查询的所有字段

b. resultMap

通过属性成员将2个类建立起联系

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!-- 利用业务扩展类实现一对一 -->
<select id="qSByNWithO2O" resultType="top.eshyee.entity.StudentBussiness" parameterType="int">
select s.*,c.* from student s inner join studentcard c
on s.cardid =c.cardid
where s.stuno=#{stuNo}
</select>
<!-- 利用resultmap实现一对一 -->
<select id="qSByNWithMapO2O" resultMap="student_card_map" parameterType="int">
select s.*,c.* from student s inner join studentcard c
on s.cardid =c.cardid
where s.stuno=#{stuNo}
</select>
<resultMap type="student" id="student_card_map">
<id property="stuNo" column="stuNo"/>
<result property="stuName" column="stuName"/>
<result property="stuAge" column="stuAge"/>
<result property="graName" column="graName"/>
<result property="stuSex" column="stuSex"/>
<!-- 一一映射用association,一对多用collection -->
<association property="card" javaType="StudentCard">
<id property="cardId" column="cardId"/>
<result property="cardInfo" column="cardInfo"/>
</association>
</resultMap>

一对多(多对一)

(MyBatis:多对一,多对多的本质就是一对多的变化)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 查询1班的班级信息 和所有学生的信息 建立一对多关系 -->
<select id="qCAS" resultMap="class_student_classid" parameterType="int">
select c.*,s.* from student s
inner join studentclass c
on c.classid=s.classid
where c.classid=#{classId}
</select>
<resultMap type="top.eshyee.entity.StudentClass" id="class_student_classid">
<id property="classId" column="classId" />
<result property="className" column="className"/>
<!-- 属性类型用javatype 属性的元素类型用oftype -->
<collection property="student" ofType="top.eshyee.entity.Student">
<id property="stuNo" column="stuNo"/>
<result property="stuName" column="stuName"/>
<result property="stuAge" column="stuAge"/>
<result property="graName" column="graName"/>
<result property="stuSex" column="stuSex"/>
</collection>
</resultMap>

多对多

多对多 可由两个多对一等方法实现!


log4j和延迟加载

在之前下载的mybatis包中找到log4j并导入到项目

开启日志

如果不指定,Mybatis就会根据以下顺序寻找日志
SLF4J →Apache Commons Logging →Log4j 2→Log4j → JDK logging
日志级别:
DEBUG< INFO< WARN< ERROR
如果设置为info, 则只显示info及以上级别的信息;
建议:
在开发时设置debug,在运行时设置为info或以上。

1
2
3
<settings>
<setting name="logImpl" value="LOG4J" />
</settings>

每次运行就会出现诸如此类的debug

1
2
3
4
5
6
7
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 961160488.
DEBUG [main] - Setting autocommit to false on JDBC Connection [oracle.jdbc.driver.T4CConnection@394a2528]

配置延迟加载

嵌套查询多用于延迟加载的设计

具体如下

一对一:查学生和学生卡信息

在xml中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!-- 延迟加载 -->
<select id="qSByNWithMapO2OLazy" resultMap="student_card_Lazyload_map" parameterType="int">
<!-- 先查学生 -->
select * from student
</select>
<resultMap type="student" id="student_card_Lazyload_map">
<id property="stuNo" column="stuNo"/>
<result property="stuName" column="stuName"/>
<result property="stuAge" column="stuAge"/>
<result property="graName" column="graName"/>
<result property="stuSex" column="stuSex"/>
<!-- 此次采用延迟加载:在查询学生时,并不立即加载学生证信息
通过select在需要的时候再查学生证
-->
<association property="card" javaType="StudentCard" select="top.eshyee.mapper.StudentCardMapper.querycardbyid" column="cardid">
<!-- <id property="cardId" column="cardId"/>
<result property="cardInfo" column="cardInfo"/> -->
</association>
</resultMap>

新建Cardmapper

1
2
3
4
5
<!-- 查询学生信息 -->

<select id="querycardbyid" parameterType="int" resultType="top.eshyee.entity.StudentCard">
select * from studentCard where cardid=#{cardId}
</select>

一对多:查班级和学生信息

新建班级mapper

1
2
3
4
5
6
7
8
9
10
11
12
13
<select id="qCASLazy" resultMap="class_student_classid_lazy" parameterType="int">
select c.* from studentclass c
</select>

<resultMap type="top.eshyee.entity.StudentClass" id="class_student_classid_lazy">
<id property="classId" column="classId" />
<result property="className" column="className"/>
<!-- 属性类型用javatype 属性的元素类型用oftype -->
<!-- 延迟加载 -->
<collection property="student" ofType="top.eshyee.entity.Student" select="top.eshyee.mapper.StudentMapper.queryStuByClassId" column="classId">

</collection>
</resultMap>

在studentmapper中

1
2
3
4
<!-- 延迟加载一对多 查询班级中的所有学生 -->
<select id="queryStuByClassId" parameterType="int" resultType="top.eshyee.entity.Student">
select * from student where classId=#{classId}
</select>

一对多和一对一的延迟加载配置方法相同


mybatis 缓存

查询缓存

一级缓存:同一个SqlSession对象

MyBatis默认开启一级缓存,如果用同样的SqlSession对象查询相同的数据,则只会在第一次查询时向数据库发送SQL语句,并将查询的结果放入到SQLSESSION中(作为缓存在) ;后续再次查询该同样的对象时则直接从缓存中查询该对象即可(即省略了数据库的访问)。

二级缓存

Mybatis自带二级缓存: [同一个namespace]生成的mapper对象

MyBati s默认情况没有开启二级缓存,需要手工打开。

conf.xml中

1
2
<!-- 开启二级缓存 -->
<setting name="cacheEnable" value="true"/>

mapper中

1
2
<!-- 声明此namespace开启二级缓存 -->
<cache/>

异常提示:NotSerializableException可知,MyBatis的二级缓存是将对象放入硬盘文件中

序列化:内存->硬盘

反序列化:硬盘->内存

准备缓存的对象,必须实现了序列化接口( 如果开启的缓存Namespace="top.eshyee.mapper.StudentMapper" 可知序列化对象为Student,因此需要将Student序列化(序 列化Student类,以及Student的级联属性、和父类)

触发将对象写入二级缓存的时机: SqlSession对象的close()方法

回顾: namespace的值 就是接口的全类名(包名.类名) ,通过接口可以产生代理对象(Mapper对象)

–>namespace决定了Mapper对象的产生

结论:只要产生的xxxMapper对象来自于同一个namespace,则这些对象共享二级缓存。

如果是同一个SqISession对象进行多次相同的查询,则直接进入一级缓存查询;

如果不是同一个SqISession对象进行多次相同的查询(但是均来自同一个namespace)则进入二级缓存查询

如果一个namespace, 如果有多个xxMapper. xml的namespace值相同, 则通过这些xxxMapper. xml产生的xxMapper,仍然共享二级缓存。

禁用:

在需要禁用的select标签中的usecache属性改为false

清理:与清理一级缓存的方法相同,一般执行增删改时会清理掉缓存 ,设计的原因是为了防止脏数据

commit();

commit会清理一级和二级缓存;但是清理二级缓存时,不能是查询自身的commit()

改标签

在select标签中设置flushCache= "true"

命中率:
1 :0% 0.0

2:50% 0.5

3:2/3 0.666

4:3/4 0.75

三方提供的二级缓存:

ehicache、memcache

要想整合三方提供的二级缓存(或者自定义二级缓存) ,必须实现org.apache.ibatis.cache.Cache接口,该接口的默认实现类是PerpetualCache

整合ehcache二级缓存:

Ehcache-core.jar
mybatis- Ehcache.jar
slf4j-api.jar

编写ehcache配置文件Ehcache.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
27
28
29
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect" dynamicConfig="true">
<!-- 当二级缓存的对象超过内存限制时(缓存对象的个数>maxElements InMemory
),存放入的硬盘路径 -->
<diskStore path="D:\Ehcache" />
<!-- maxElementsInMemory:设置在内存中缓存对象的个数
maxElementsOnDisk:设置在硬盘中缓存对象的个数
eternal: 设置缓存是否永远不过期
overflowToDisk:当内存中缓存的对象个数超过maxElementsInMemory的时候,是否转移到硬盘中
timeToIdleSeconds:当2次访问超过该值的时候,将缓存对象失效
timeToliveSeconds:一个缓存对象最多存放的时间(生命周期)
diskExpiryThreadIntervalSeconds:设置每隔多长时间,通过一个线程来清理硬盘中的缓存
memoryStioreEvictionPolicy: 当超过缓存对象的最大值时,处理的策略 LRU,FIFO,LFU
-->

<defaultCache

maxElementsInMemory="1000"
maxElementsOnDisk="1000000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="100"
timeToLiveSeconds="100"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>

</ehcache>

配置mapper

1
2
3
4
<cache type= "org.mybatis.caches.ehcache.EhcacheCache">
<property name= "maxELementsInMemory" value= "2000" />
可以覆盖掉默认值
</cache>