MyBatis3框架详解(六)

一、缓存机制

介绍:

  • MyBatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率
  • MyBatis系统中默认定义了两级缓存。

一级缓存和二级缓存:

  • 默认情况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启
  • 二级缓存需要手动开启和配置,它是基于namespace级别的缓存
  • 为了提高扩展性,MyBatis定义了缓存接口Cache,用户可以通过实现Cache接口来定义二级缓存。

两级缓存:

一级缓存(本地缓存):将数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库。

二级缓存(全局缓存):

 

二、一级缓存

说明:

  • 一级缓存(local cache),即本地缓存,作用域默认为sqlSession。当Session flush或close后,该Session中的所有Cache将被清空(每个sqlSession对应一个一级缓存
  • 本地缓存不能被关闭,是一直打开的,但可以调用clearCache()来清空本地缓存,或者改变缓存的作用域
  • 同一次会话期间只要查询过得数据都会保存在当前sqlSession的一个Map中

一级缓存失效的情况:(没有使用当前一级缓存的情况,效果就是还需要再向数据库发出查询)

  • sqlSession不同。(即不同的sqlSession对应各自的一级缓存)
  • sqlSession相同,但查询条件不同。(即当前一级缓存中没有新的查询对应的结果)
  • sqlSession相同,但两次查询之间执行了增删改操作。(即当前增删改可能对当前的数据有影响)
  • sqlSession相同,但手动清除了一级缓存。(即当前一级缓存已清空)

示例:

package com.csu.marden;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Test {
	public static void main(String[] args) throws IOException {
		
		//1.根据xml配置文件(全局配置文件,即mybatis-config.xml)创建一个SqlSessionFactory对象
		String resources="mybatis-config.xml";
		InputStream inputStream=Resources.getResourceAsStream(resources);
		SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
		
		//2.根据SqlSessionFactory创建SqlSession,每个SqlSession代表和数据库的一次会话
		SqlSession openSession=sqlSessionFactory.openSession();
		try{
			//3.获取接口的实现类对象(mybatis会为接口自动创建一个代理对象,代理对象会执行增删改查)
			EmployeeMapper mapper0=openSession.getMapper(EmployeeMapper.class);
			EmployeeMapperPlus mapper1=openSession.getMapper(EmployeeMapperPlus.class);
			DepartmentMapper mapper2=openSession.getMapper(DepartmentMapper.class);
			EmployeeMapperDynamicSQL mapper3=openSession.getMapper(EmployeeMapperDynamicSQL.class);
			//4.通过接口的实现类对象调用接口的查询方法
			Employee result1 = mapper0.getEmpById(1);
			System.out.println(result1.hashCode());
			
			//手动清除一级缓存
			openSession.clearCache();
			
			Employee result2 = mapper0.getEmpById(1);
			System.out.println(result2.hashCode());
			openSession.commit();
		}finally{
			openSession.close();
		}
	}
}

 

三、二级缓存

说明:

  • 二级缓存(second level cache),即全局作用域缓存。是基于namespace级别的缓存(一个sql映射文件对应一个namespace,即对应一个二级缓存)
  • 二级缓存默认不开启,需要手动配置。
  • MyBatis提供二级缓存的接口以及实现,缓存实现要求POJO实现Serializable接口。
  • 查处的数据默认先放在一级缓存中,二级缓存在SqlSession关闭或提交之后才会生效!!!

工作机制:

  • 一个会话查询一条数据,这个数据就会被放在当前的一级缓存中。
  • 如果这个会话关闭,一级缓存中的数据会被保存到二级缓存中。新的会话查询同样的信息,就可以参照二级缓存中的内容。
  • 例如一个sqlSession,通过EmployeeMapper查询了Employee对象,也通过DepartmentMapper查询了Department对象。但是由于两个Mapper映射文件对应两个namespace,不同的namespace查出的数据会放在自己对应的二级缓存中。

使用步骤:

  • 开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>
  • 在每个sql映射文件(即mapper.xml)中配置使用二级缓存: <cache></cache>
  • 注意:POJO需要实现Serializable接口

示例:

第一步:在全局配置文件中,开启二级缓存配置

<?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="dbconfig.properties"></properties>
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
		<setting name="lazyLoadingEnabled" value="true"/>
		<setting name="aggressiveLazyLoading" value="false"/>
		<setting name="cacheEnabled" value="true"/>
	</settings>
	
	<typeAliases>
		<typeAlias type="com.csu.marden.Employee" />
		<typeAlias type="com.csu.marden.Department"/>
	</typeAliases>

	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" /> 
			</dataSource>
		</environment>
	</environments>


	<!-- 将sql映射文件(EmployeeMapper.xml)注册到全局配置文件(mybatis-config.xml)中 -->
	<mappers>
		<mapper resource="EmployeeMapper.xml"/>
		<mapper resource="EmployeeMapperPlus.xml" />
		<mapper resource="DepartmentMapper.xml"/>
		<mapper resource="EmployeeMapperDynamicSQL.xml"/>
	</mappers>
</configuration>

第二步:在相应的sql映射文件(Mapper.xml)中配置使用二级缓存

<?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="com.csu.marden.EmployeeMapper">

	<!-- 映射文件中配置使用二级缓存 -->
	<cache eviction="LRU" flushInterval="6000" readOnly="false" size="1024"></cache>
	
	
	<!-- 返回多条记录的Map集合 ,其中id作为map的主键-->
	<select id="getEmpByIdLastNameLikeReturnIdMap" resultType="employee">
		select * from tbl_employee where last_name like #{lastName}
	</select>
	
	<!-- 返回多条记录的Map集合 ,其中lastName作为map的主键-->
	<select id="getEmpByIdLastNameLikeReturnLastNameMap" resultType="employee">
		select * from tbl_employee where last_name like #{lastName}
	</select>
	
	<!-- 返回一条记录的Map集合,resultType应该写Map类型 -->
	<select id="getEmpByIdReturnMap" resultType="map">
		select * from tbl_employee where id=#{id}
	</select>

	<!-- 返回List集合 -->
	<!-- 如果返回值是一个集合,resultType应该写集合中元素的类型 -->
	<select id="getEmpByLastNameLike" resultType="employee" >
		select * from tbl_employee where last_name like #{lastName}
	</select>

	<!-- 多个参数使用Map传入的查询方法 -->
	<select id="getEmpByMap" resultType="employee">
		select * from tbl_employee where id=#{id} and last_name=#{lastName}
	</select>

	<!-- 多个参数查询方法 -->
	<select id="getEmpByIdAndLastName" resultType="employee">
		select * from  tbl_employee where id = #{emp.id} and last_name=#{emp.lastName}
	</select>

	<!-- 单个参数查询方法 -->
	<select id="getEmpById" resultType="employee" >
		select   * from tbl_employee where id = #{id}
	</select>
	
	<!--插入方法 -->
	<insert id="addEmp" parameterType="employee" useGeneratedKeys="true" keyProperty="id" >
		insert into tbl_employee (last_name,email,gender) values (#{lastName},#{email},#{gender})
	</insert>
	

	<!-- 更新方法 -->
	<update id="updateEmp" parameterType="employee" >
		update tbl_employee set last_name=#{lastName},email=#{email},gender=#{gender} where id=#{id}
	</update>
	
	<!-- 删除方法 -->
	<delete id="deleteEmpById" parameterType="integer">
		delete from tbl_employee where id=#{id}
	</delete>
	
</mapper>

第三步:JavaBean实现Serializable接口

package com.csu.marden;

import java.io.Serializable;

public class Employee implements Serializable{
	private Integer id;
	private String lastName;
	private String email;
	private String gender;
	private Department dept;
	
	public Employee() {
	}

	public Employee(Integer id, String lastName, String email, String gender) {
		this.id = id;
		this.lastName = lastName;
		this.email = email;
		this.gender = gender;
	}
	
	

	public Employee(Integer id, String lastName, String email, String gender, Department dept) {
		this.id = id;
		this.lastName = lastName;
		this.email = email;
		this.gender = gender;
		this.dept = dept;
	}

	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public Department getDept() {
		return dept;
	}
	public void setDept(Department dept) {
		this.dept = dept;
	}



	@Override
	public String toString() {
		return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + "]";
	}
}

第四步:编写测试类

创建两个sqlSession对象,每个sqlSession对象查询一次(若没有开启二级缓存,则需查询两次)

package com.csu.marden;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
public class Test {
	public static void main(String[] args) throws IOException {
		
		//1.根据xml配置文件(全局配置文件,即mybatis-config.xml)创建一个SqlSessionFactory对象
		String resources="mybatis-config.xml";
		InputStream inputStream=Resources.getResourceAsStream(resources);
		SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
		
		//2.根据SqlSessionFactory创建SqlSession,每个SqlSession代表和数据库的一次会话
		SqlSession openSession1=sqlSessionFactory.openSession();
		SqlSession openSession2=sqlSessionFactory.openSession();
		try{
			
			//3.获取接口的实现类对象(mybatis会为接口自动创建一个代理对象,代理对象会执行增删改查)
			EmployeeMapper mapper1=openSession1.getMapper(EmployeeMapper.class);
			EmployeeMapper mapper2=openSession2.getMapper(EmployeeMapper.class);
			
			//4.通过接口的实现类对象调用接口的查询方法
			Employee result1=mapper1.getEmpById(1);
			System.out.println(result1);
			openSession1.close();
			Employee result2=mapper2.getEmpById(1);
			System.out.println(result2);
			openSession2.close();
		
		}finally{
			
		}
	}
}


1. 缓存相关属性:

eviction属性:缓存回收策略

  • LRU –最近最少使用的:移除最长时间不被使用的对象。
  • FIFO –先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT –软引用:移除基于垃圾回收器状态和软引用规则的对象。
  • WEAK –弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
  • 默认的是LRU。

flushInterval属性:缓存刷新间隔

  • 缓存多久清空一次,默认不清空,可以设置一个毫秒值

readOnly属性:是否只读

  • ture:只读缓存,mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,但速度快。
  • false:读写缓存,mybatis认为获取的数据可能会被修改,mybatis会利用序列化与反序列化的技术,克隆一份新的数据。安全,但速度慢。(默认

size属性:缓存中存放多少个元素

  • 代表缓存最多可以存储多少个对象,太大容易导致内存溢出

2. 缓存相关设置

  • 全局配置文件中,setting标签的cacheEnable属性,功能:配置二级缓存的开关,若cacheEnable置为false,则二级缓存关闭,一级缓存仍然可用。
  • sql映射文件中,select标签中的useCache属性,功能:配置这个select标签是否使用二级缓存,若useCache置为false,则二级缓存关闭,一级缓存仍然可用。
  • sql映射文件中,insert,update,delete标签中的flushCache属性,功能:配置当前标签执行完增删改操作后,是否清空一级缓存和二级缓存。若flushCache置为true,一级缓存和二级缓存都会被清空。
  • sqlSession调用clearCache()方法,功能:用于清除一级缓存。
  • 全局配置文件中,setting标签的localCacheScope属性,功能:设置本地缓存作用域。默认为SESSION,即当前会话的所有数据保存在会话缓存汇总。若localCacheScope置为STATEMENT可以禁用一级缓存。

 

三、缓存原理示意图

缓存原理机制:

  • 首先查找二级缓存
  • 接下来查找一级缓存
  • 最后查询数据库

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值