“进阶:查询缓存”的版本间差异

来自Wikioe
跳到导航 跳到搜索
第234行: 第234行:


== 整合ehcache ==
== 整合ehcache ==
EhCache 是一个纯Java的进程内缓存框架,是一种广泛使用的开源Java分布式缓存,具有快速、精干等特点,是Hibernate中默认的CacheProvider。<br/>
而 Mybatis 的特长是sql操作,而非缓存数据的管理,为了提高缓存的性能将mybatis和第三方的缓存数据库整合,比如ehcache、memcache、redis等。
=== 整合原理 ===
mybatis提供二级缓存Cache接口:
: [[File:mybatis二级缓存接口Cache.png|200px]]
它的默认实现类:
: [[File:mybatis二级缓存接口Cache的默认实现类.png|200px]]
通过实现Cache接口,可以实现mybatis与其它缓存数据库整合。
=== 整合步骤 ===
==== 依赖 ====
==== EhCache配置文件 ====
==== 开启EhCache ====
=== 应用场景 ===
=== 局限性 ===

2020年10月12日 (一) 12:01的版本


关于

Mybatis缓存.png

Mybatis一级缓存的作用域是同一个SqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。
Mybatis默认开启一级缓存。

Mybatis二级缓存是多个SqlSession共享的,其作用域是mapper的同一个namespace,不同的sqlSession两次执行相同namespace下的sql语句且向sql中传递参数也相同即最终执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。
Mybatis二级缓存默认关闭。

一级缓存

Mybatis一级缓存.png

  1. 一级缓存区域是根据SqlSession为单位划分的。
  2. 每次查询会先从缓存区域找,如果找不到再从数据库查询,并将查询到的数据写入缓存。
  3. Mybatis内部存储缓存使用一个HashMap
    • key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象;
  4. sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。

示例

@Test
public void testCache1() throws Exception{
	SqlSession sqlSession = sqlSessionFactory.openSession();//创建代理对象
	UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
	
	//以下查询使用一个SqlSession
	
	//第一次发起请求,查询id为1的用户
	User user1 = userMapper.findUserById(1);
	System.out.println(user1);
	
	//第二次发起请求,查询id为1的用户
	//与user1执行的是相同sql,可以使用一级缓存
	User user2 = userMapper.findUserById(1);
	System.out.println(user2);
	
	//在同一个session执行更新
	//执行commit操作,清空一级缓存,避免脏读。
	User user_update = new User();
	user_update.setId(1);
	user_update.setUsername("李奎");
	userMapper.updateUser(user_update);
	session.commit();
	
	//再次发起请求,查询id为1的用户
	//缓存无相应内容,需要查询数据库
	User user3 = userMapper.findUserById(1);
	System.out.println(user3);
	
	//关闭sqlSession
	sqlSession.close();
}

二级缓存

Mybatis二级缓存.png

  1. 二级缓存区域是根据mapper的namespace划分的,相同namespace的mapper查询数据放在同一个区域
    • 如果使用mapper代理方法每个mapper的namespace都不同,此时可以理解为二级缓存区域是根据mapper划分;
  2. 每次查询会先从缓存区域找,如果找不到再从数据库查询,并将查询到的数据写入缓存。
  3. Mybatis内部存储缓存使用一个HashMap
    • key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象;
  4. sqlSession执行insert、update、delete等操作commit提交后会清空缓存区域。

设置

SqlMapConfig.xml

设置 描述 允许值 默认值
cacheEnabled 对在此配置文件下的所有cache 进行全局性开/关设置。 true | false true

Mybatis的二级缓存默认是不开启的,需要时在SqlMapConfig.xml中加入:

	<setting name="cacheEnabled" value="true"/>

Mapper.xml

开启二级缓存不光需要进行全局设置,同时必须在需要的mapper.xml映射文件中启用二级缓存

	<cache />
	<cache type="" />
  • type:指定cache接口的实现类的类型
    1. 指定cache接口的实现类的类型
    2. Mybatis和ehcache整合,需要配置type为ehcache实现cache接口的类型

如:

<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">

	<!-- 开启本mapper的namespace下的二缓存 -->
	<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
    
   ...
Cache参数
设置 描述 允许值 默认值
flushInterval 缓存刷新间隔(单位毫秒)。
  • 默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
任意正整数 Not set
size 设置缓存结果对象或列表的引用大小。 任意正整数 1024
readOnly 设置缓存只读。只读缓存会给所有调用者返回缓存对象的相同实例,因此这些对象不能被修改。
  1. 在不同线程中的调用者之间修改它们会导致冲突;
  2. 可读写的缓存会返回缓存对象的拷贝(通过序列化),慢,安全(因此该属性默认false);
true | false false
eviction 缓存收回策略。
  1. LRU:最近最少使用的策略,移除最长时间不被使用的对象
  2. FIFO:先进先出测试,按对象进行缓存的顺序来移除他们
  3. SOFT:软引用策略,移除基于垃圾回收器状态和软引用规则的对象
  4. WEAK:弱引用策略,更积极的移除基于垃圾收集器状态和弱引用规则的对象
LRU

如:

	<cache  eviction="FIFO"  flushInterval="60000"  size="512"  readOnly="true"/>
使用了一个“以FIFO进行回收,并每隔 60 秒刷新,缓存Hashmap有512个引用,而且返回只读对象”的二级缓存。

statement

禁用缓存

在statement中设置useCache=false可以禁用当前statement(即当前select语句)的二级缓存,即每次查询都会发出sql去查询。

  1. 默认情况是true,即该sql使用二级缓存;
  2. 针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存;

如:

	<select id="findOrderListResultMap" resultMap="ordersUserMap" useCache="false">
刷新缓存

在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。

  1. 默认情况下为true(即刷新缓存),如果改成false则不会刷新。
  2. 一般下执行完commit操作都需要刷新缓存,以避免数据库脏读。
  3. (使用缓存时,如果手动修改数据库表中的查询数据会出现脏读)。

如:

	<insert id="insertUser" parameterType="cn.itcast.mybatis.po.User" flushCache="true">

po

二级缓存需要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作

  • 注意如果存在父类、成员pojo都需要实现序列化接口。
	public class Orders implements Serializable
	public class User implements Serializable
   ...

如:

public class User implements Serializable {
	
	//属性名和数据库表的字段对应
	private int id;
	private String username;// 用户姓名
	private String sex;// 性别
	private Date birthday;// 生日
	private String address;// 地址
	
	//用户创建的订单列表
	private List<Orders> ordersList;

示例

@Test
public void testCache2() throws Exception {
	SqlSession sqlSession1 = sqlSessionFactory.openSession();
	SqlSession sqlSession2 = sqlSessionFactory.openSession();
	SqlSession sqlSession3 = sqlSessionFactory.openSession();
	SqlSession sqlSession4 = sqlSessionFactory.openSession();
	
	// 创建代理对象
	UserMapper userMapper1 = sqlSession1.getMapper(UserMapper.class);
	// 第一次发起请求,查询id为1的用户
	User user1 = userMapper1.findUserById(1);
	System.out.println(user1);
	//这里执行关闭操作,将sqlsession中的数据写到二级缓存区域
	sqlSession1.close();
	
	
	UserMapper userMapper2 = sqlSession2.getMapper(UserMapper.class);
	// 第二次发起请求,查询id为1的用户
	// 与sqlSession2请求相同,读取二级缓存,而不查询数据库
	User user2 = userMapper2.findUserById(1);
	System.out.println(user2);
	//这里关闭操作,并将sqlsession中的数据写到二级缓存区域
	sqlSession2.close();
	
	
	//使用sqlSession3执行commit()操作
	UserMapper userMapper3 = sqlSession3.getMapper(UserMapper.class);
	User user  = userMapper3.findUserById(1);
	user.setUsername("张明明");
	userMapper3.updateUser(user);
	//执行提交,清空UserMapper下边的二级缓存
	sqlSession3.commit();
	sqlSession3.close();
	

	UserMapper userMapper4 = sqlSession4.getMapper(UserMapper.class);
	// 再一次发起请求,查询id为1的用户,
	// 由于commit操作导致缓存被清空,这里重新发出sql操作
	User user4 = userMapper4.findUserById(1);
	System.out.println(user4);
	//这里关闭操作,并将sqlsession中的数据写到二级缓存区域
	sqlSession4.close();
}

整合ehcache

EhCache 是一个纯Java的进程内缓存框架,是一种广泛使用的开源Java分布式缓存,具有快速、精干等特点,是Hibernate中默认的CacheProvider。
而 Mybatis 的特长是sql操作,而非缓存数据的管理,为了提高缓存的性能将mybatis和第三方的缓存数据库整合,比如ehcache、memcache、redis等。

整合原理

mybatis提供二级缓存Cache接口:

Mybatis二级缓存接口Cache.png

它的默认实现类:

Mybatis二级缓存接口Cache的默认实现类.png

通过实现Cache接口,可以实现mybatis与其它缓存数据库整合。

整合步骤

依赖

EhCache配置文件

开启EhCache

应用场景

局限性