Mybatis高级应用 查询缓存

mybatis提供了查询缓存功能,用于减轻数据压力,提高数据库性能。mybatis提供了一级缓存和二级缓存,如下图所示:

从上图可以看出:

一级缓存是存在于一个sqlSession中的,而sqlSession就是操作数据库的一个会话对象,在对象中实际存储了一个hashMap的数据结构用于存储缓存数据。不同的sqlSession之间的缓存数据互不影响。Mybatis默认开启一级缓存。

二级缓存是mapper级别的缓存,当多个sqlSession操作同一个mapper配置中的SQL语句时,多个sqlSession可以共用二级缓存,二级缓存是跨sqlSession的。

>一级缓存

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

如下图所示:

(1)第一次执行查询id为1的用户的信息操作时,先在缓存中查找,如果没有,则执行数据库查询操作,并将从数据库中查询到的用户数据信息,并放入到一级缓存。mybatis内部存储缓存使用一个HashMap,key为hashCode+sqlId+Sql语句。value为从查询出来映射生成的java对象。

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

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

测试代码:

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

Debug日志:只有第一次执行时,对数据库发出了sql请求;

DEBUG [main] - ==>  Preparing: select * from user where user_id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [userId=1, userName=zhangsan, password=123456, roleCode=plain, cname=张三, telphone=17864195335, address=山东济南, isLogin=0]
User [userId=1, userName=zhangsan, password=123456, roleCode=plain, cname=张三, telphone=17864195335, address=山东济南, isLogin=0]

当两次查询中间插入事务操作:

@Test
	public void queryUserMapper() throws Exception{
		Integer userId=1;
		UserMapper mapper=session.getMapper(UserMapper.class);
		User user = mapper.queryUserById(userId);
		System.out.println(user);
		User user2=new User(null, "xinxinxiangrong", "1234598", "1", "huang", "1789999999", "山东威海", "0");
		mapper.insertUser(user2);
		session.commit();
		User user3 = mapper.queryUserById(userId);
		System.out.println(user3);
		session.close();
	}

Debug日志:向数据库发出了两次查询sql

DEBUG [main] - ==>  Preparing: select * from user where user_id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [userId=1, userName=zhangsan, password=123456, roleCode=plain, cname=张三, telphone=17864195335, address=山东济南, isLogin=0]
DEBUG [main] - ==>  Preparing: insert into user values(?,?,?,?,?,?,?,?) 
DEBUG [main] - ==> Parameters: null, xinxinxiangrong(String), 1(String), 1234598(String), huang(String), 0(String), 1789999999(String), 山东威海(String)
DEBUG [main] - <==    Updates: 1
DEBUG [main] - Committing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@769e7ee8]
DEBUG [main] - ==>  Preparing: select * from user where user_id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [userId=1, userName=zhangsan, password=123456, roleCode=plain, cname=张三, telphone=17864195335, address=山东济南, isLogin=0]

注意:在正式开发中,是将mybatis和Spring进行整合开发,事务是在service层进行控制,一个service方法中包含很多mapper方法的调用。

在service方法开始执行时,就会开启事务,在开启事务的过程中就会创建SqlSession对象,因此期间不管是调用哪个mapper的方法,都是走一级缓存,有就从缓存中取,没有就从数据库中查询。方法结束时,事务关闭,同时清空一级缓存。

因此在同一service方法中是走一级缓存,如果是两次调用service方法,不走一级缓存,因为方法结束,SqlSession也就关闭,一级缓存就会被清空。

>二级缓存

在mybatis中允许多个SqlSession对象共享一个缓存区域,只不过这个缓存区域并一定在内存中,也可能是存储硬盘空间内,这个共享区域就是mybatis的二级缓存。mybatis同样适用hashMap这种数据结构来存储二级缓存中保存的数据。 如下图所示:

从上图中可以看出,mybatis的二级缓存是根据mapper配置文件的namespace命名空间进行划分的,相同namespace的查询数据操作放在同一个缓存区中。通常开发中,一个Mapper对应一个功能模块,即用户的查询操作的数据是放在UserMapper的缓存区中,订单查询操作的数据是放在OrderMapper的缓存区中。

如果两个用户的SqlSession会话都是执行同一个UserMapper接口中的方法,并且都去查询用户数据,每次查询都会先从缓存区中查找,如果找不到从数据库中查询,查询到的数据写入到二级缓存。

任何一个用户的SqlSession 执行insert、update、delete等操作commit提交后都会清空该mapper下的二级缓存区域。

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

(1)在SqlMapConfig.xml全局配置文件中加入setting信息:

(2)在需要开启的二级缓存的mapper.xml中加入cache标签:

只要加入cache标签就表示开启二级缓存

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

在SqlSession会话中,如果会话没有结束,数据只会存储于一级缓存中,如果此SqlSession的会话结束并且此命名空间的mapper开启了二级缓存,这时数据才会写入到二级缓存中。

测试程序:

@Test
	public void queryUserMapper() throws Exception{
		Integer userId=1;

		SqlSession session1=factory.openSession();
		UserMapper mapper1=session1.getMapper(UserMapper.class);
		User user1 = mapper1.queryUserById(userId);
		System.out.println(user1);
		session1.close();

		SqlSession session2=factory.openSession();
		UserMapper mapper2=session2.getMapper(UserMapper.class);
		User user2 = mapper2.queryUserById(userId);
		System.out.println(user2);
		session2.close();
	}

Debugr日志:仅发起了一次Sql查询

DEBUG [main] - ==>  Preparing: select * from user where user_id=? 
DEBUG [main] - ==> Parameters: 1(Integer)
DEBUG [main] - <==      Total: 1
User [userId=1, userName=zhangsan, password=123456, roleCode=plain, cname=张三, telphone=17864195335, address=山东济南, isLogin=0]
DEBUG [main] - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@75329a49]
DEBUG [main] - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@75329a49]
DEBUG [main] - Returned connection 1966250569 to pool.
DEBUG [main] - Cache Hit Ratio [com.langsin.mapper.UserMapper]: 0.5
User [userId=1, userName=zhangsan, password=123456, roleCode=plain, cname=张三, telphone=17864195335, address=山东济南, isLogin=0]

注意:二级缓存需要查询结果映射的pojo对象实现java.io.Serializable接口实现序列化和反序列化操作,否则报错:

猜你喜欢

转载自blog.csdn.net/AhaQianxun/article/details/92213672