详解mybatis的一级缓存二级缓存

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_44936331/article/details/100030346

详解mybatis的一级缓存二级缓存

mybatis的查询缓存

在这里插入图片描述
mybatis提供了查询缓存功能,用于减轻数据压力,提高数据库性能。mybatis提供了一级缓存和二级缓存,
一级缓存是存在于一个sqlSession中的,而sqlSession就是操作数据库的一个会话对象,在对象中实际存储了一个hashMap的数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据互不影响。
二级缓存是mapper级别的缓存,当多个sqlSession操作同一个mapper配置中的SQL语句时,多个sqlSession可以共用二级缓存,二级缓存是跨sqlSession的。

一级缓存

在默认情况下,mybatis默认开启一级缓存,mybatis一级缓存的作用域是同一个sqlSession,在同一个sqlSession中两次执行相同的sql语句,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次会从缓存中获取数据将不再从数据库查询,从而提高查询效率。当一个sqlSession结束后该sqlSession中的一级缓存也就不存在了。
如下图所示:
在这里插入图片描述
注意:在正式开发中,是将mybatis和Spring进行整合开发,事务是在service层进行控制,一个service方法中包含很多mapper方法的调用。
在service方法开始执行时,就会开启事务,在开启事务的过程中就会创建SqlSession对象,因此期间不管是调用哪个mapper的方法,都是走一级缓存,有就从缓存中取,没有就从数据库中查询。方法结束时,事务关闭,同时清空一级缓存。
因此在同一service方法中是走一级缓存,如果是两次调用service方法,不走一级缓存,因为方法结束,SqlSession也就关闭,一级缓存就会被清空。
一级缓存的生命周期

1.MyBatis在开启一个数据库会话时,会 创建一个新的SqlSession对象,SqlSession对象中会有一个新的Executor对象。Executor对象中持有一个新的PerpetualCache对象;当会话结束时,SqlSession对象及其内部的Executor对象还有PerpetualCache对象也一并释放掉。

2.如果SqlSession调用了close()方法,会释放掉一级缓存PerpetualCache对象,一级缓存将不可用。

3.如果SqlSession调用了clearCache(),会清空PerpetualCache对象中的数据,但是该对象仍可使用。

4.SqlSession中执行了任何一个update操作(update()、delete()、insert()) ,都会清空PerpetualCache对象的数据,但是该对象可以继续使用
 
代码实现
定义一个UserMapper接口


public interface UserMapper {
	
	public User queryUserById(Integer userId) throws Exception;
	public void updateUser(User user) throws Exception;
}

UserMapper.xml中的配置

<resultMap type="user" id="userMap">
		<id property="userId" column="user_id"/>
		<result property="userName" column="user_name"/>
	</resultMap>
	<select id="queryUserById" parameterType="int" resultMap="userMap">
		select * from user where user_id = #{value}
	</select>
	

JUnit测试

@Test
	public void queryUserById() throws Exception{
		UserMapper mapper = session.getMapper(UserMapper.class);
		Integer userId =1;
		User user = mapper.queryUserById(userId);
		System.out.println(user);
		User user2 = mapper.queryUserById(userId);
		System.out.println(user2);
		session.close();
	}

执行结果

DEBUG [main] - Opening JDBC Connection
Fri Aug 23 09:36:07 GMT+08:00 2019 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
DEBUG [main] - Created connection 1125381564.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4313f5bc]
DEBUG [main] - ==>  Preparing: select * from user where user_id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [userId=1, userName=你好, address=山东省, telephone=15566887766]
DEBUG [main] - Cache Hit Ratio [com.langsin.mapper.UserMapper]: 0.0
User [userId=1, userName=你好, address=山东省, telephone=15566887766]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4313f5bc]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4313f5bc]
DEBUG [main] - Returned connection 1125381564 to pool.

从上面的执行结果可以看出,select语句只执行了一次,
测试代码2

@Test
	public void updatetest() throws Exception{
		UserMapper mapper = session.getMapper(UserMapper.class);
		Integer userId =1;
		User user = mapper.queryUserById(userId);
		System.out.println(user);
		User user1 = new User();
		user1.setUserName("哈哈哈哈哈");
		mapper.updateUser(user1);
		User user2 = mapper.queryUserById(userId);
		System.out.println(user2);
		session.close();
	}

执行结果

DEBUG [main] - Opening JDBC Connection
Fri Aug 23 09:45:23 GMT+08:00 2019 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
DEBUG [main] - Created connection 1125381564.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4313f5bc]
DEBUG [main] - ==>  Preparing: select * from user where user_id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [userId=1, userName=你好, address=山东省, telephone=15566887766]
DEBUG [main] - ==>  Preparing: update user set user_name=? where user_id=? 
DEBUG [main] - ==> Parameters: 哈哈哈哈哈(String), null
DEBUG [main] - <==    Updates: 0
DEBUG [main] - Cache Hit Ratio [com.langsin.mapper.UserMapper]: 0.0
DEBUG [main] - ==>  Preparing: select * from user where user_id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [userId=1, userName=你好, address=山东省, telephone=15566887766]
DEBUG [main] - Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@4313f5bc]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4313f5bc]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4313f5bc]
DEBUG [main] - Returned connection 1125381564 to pool.

从上面的执行结果可以看出来,进行了更新操作之后,select语句执行了两次。
一级缓存总结:
1.第一次执行查询id为1的用户的信息操作时,先在缓存中查找,如果没有,则执行数据库查询操作,并将从数据库中查询到的用户数据信息,并放入到一级缓存。mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象

2.如果sqlSession执行insert、update、delete等操作commit提交后会清空一级缓存,这样做的目的是为了让缓存中存储的数据是最新的信息,避免脏读。

3.第二次执行查询id为1的用户的信息操作时,先去缓存中查找,如果缓存中有,则直接从缓存中获取用户信息。

二级缓存

在mybatis中允许多个SqlSession对象共享一个缓存区域,只不过这个缓存区域并一定在内存中,也可能是存储硬盘空间内,这个共享区域就是mybatis的二级缓存。mybatis同样适用hashMap这种数据结构来存储二级缓存中保存的数据。 如下图所示:
在这里插入图片描述
从上图中可以看出,mybatis的二级缓存是根据mapper配置文件的namespace命名空间进行划分的,相同namespace的查询数据操作放在同一个缓存区中。即用户的查询操作的数据是放在UserMapper的缓存区中,订单查询操作的数据是放在OrderMapper的缓存区中。
如果两个用户的SqlSession会话都是执行同一个UserMapper接口中的方法,并且都去查询用户数据,每次查询都会先从缓存区中查找,如果找不到从数据库中查询,查询到的数据写入到二级缓存。
任何一个用户的SqlSession 执行insert、update、delete等操作commit提交后都会清空该mapper下的二级缓存区域。

在mybatis中二级缓存是默认关闭的,如果要开启mybatis的二级缓存,配置如下:

1、在sqlMapConfig.xml全局配置文件中加入setting信息

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

2、在需要开启的二级缓存的mapper.xml中加入cache标签
只要加入cache标签就表示开启二级缓存

二级缓存需要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作,注意如果存在父类、成员pojo都需要实现序列化接口。因为mybatis实现的二级缓存,数据并不一定存储在内存中,也有可能是其他位置,比如硬盘、网络空间等。

在SqlSession会话中,如果会话没有结束,数据只会存储于一级缓存中,如果此SqlSession的会话结束并且此命名空间的mapper开启了二级缓存,这时数据才会写入到二级缓存中。
注意:User必须实现Serializable 序列化接口
测试代码:

@Test
	public void updatecatchtest() throws Exception{
		SqlSession session1 = this.factory.openSession();
		UserMapper mapper = session1.getMapper(UserMapper.class);
		Integer userId = 1;
		User user1 = mapper.queryUserById(userId);
		session1.close();
		System.out.println(user1);
		System.out.println("第二条");
		SqlSession session2 = this.factory.openSession();
		UserMapper mapper2 = session2.getMapper(UserMapper.class);
		User user2 = mapper2.queryUserById(userId);
		session2.close();
		System.out.println(user2);
	}

执行结果:

DEBUG [main] - Opening JDBC Connection
Fri Aug 23 09:49:41 GMT+08:00 2019 WARN: Establishing SSL connection without server's identity verification is not recommended. According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if explicit option isn't set. For compliance with existing applications not using SSL the verifyServerCertificate property is set to 'false'. You need either to explicitly disable SSL by setting useSSL=false, or set useSSL=true and provide truststore for server certificate verification.
DEBUG [main] - Created connection 665726928.
DEBUG [main] - Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@27ae2fd0]
DEBUG [main] - ==>  Preparing: select * from user where user_id = ? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@27ae2fd0]
DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@27ae2fd0]
DEBUG [main] - Returned connection 665726928 to pool.
User [userId=1, userName=你好, address=山东省, telephone=15566887766]
第二条
DEBUG [main] - Cache Hit Ratio [com.langsin.mapper.UserMapper]: 0.5
User [userId=1, userName=你好, address=山东省, telephone=15566887766]

猜你喜欢

转载自blog.csdn.net/weixin_44936331/article/details/100030346