目录
文章目录
一、一级缓存
二、二级缓存
总结
MyBatis的一级缓存是SqlSession级别的缓存。如果同一个SqlSession对象多次执行完全相同的SQL语句时,在第一次执行完成后,MyBatis会将查询结果写入到一级缓存中,此后,如果程序没有执行插入、更新、删除操作,当第二次执行相同的查询语句时,MyBatis会直接读取一级缓存中的数据,而不会再去数据库查询,从而提高了数据库的查询效率。
根据图书id查询图书测试一级缓存
test类核心代码(通过id查询两次输出book信息)
@Test
public void findBookByIdTest1() {
// 2.使用session1查询id为1的图书的信息
Book book1 = sqlSession.selectOne("cn.edu.imust.mapper."
+ "BookMapper.findBookById", 1);
// 3.输出查询结果信息
System.out.println(book1.toString());
// 4.使用session1查询id为1的图书的信息
Book book2 = sqlSession.selectOne("cn.edu.imust.mapper."
+ "BookMapper.findBookById", 1);
// 5.输出查询结果信息
System.out.println(book2.toString());
}
通过下图观察,虽说用到了两次查询,但通过日志文件可看出只有一次通过数据库代码查询的结果,说明在第二次时调用的一级缓存的内容。而不是在去数据库中执行代码。提高了效率。
MyBatis如何防止程序误读
当程序对数据库执行了插入、更新、删除操作后,MyBatis会清空一级缓存中的内容,以防止程序误读。MyBatis一级缓存被清空之后,再次使用SQL查询语句访问数据库时,MyBatis会重新访问数据库。例如上面的例子,首先查询id为1的图书信息,然后使用更新语句对数据库中的图书信息进行更改,更改之后,再次对id为1的图书信息进行查询时,MyBatis依然会从数据库中查询。
新增一个更新功能进行测试(先查询,然后修改数据,然后再查询)
@Test
public void findBookByIdTest2() {
// 2.使用session查询id为1的图书的信息
Book book1 = sqlSession.selectOne("cn.edu.imust.mapper."
+ "BookMapper.findBookById", 1);
// 3.输出查询结果信息
System.out.println(book1.toString());
Book book2 = new Book();
book2.setId(3);
book2.setBookName("MySQL数据库入门");
book2.setPrice(50.0);
// 4.使用session更新id为3的图书的信息
sqlSession.update("cn.edu.imust.mapper."
+ "BookMapper.updateBook", book2);
Book book3 = sqlSession.selectOne("cn.edu.imust.mapper."
+ "BookMapper.findBookById", 1);
// 5.输出查询结果信息
System.out.println(book1.toString());
}
通过效果图可看出,第一次查询访问了数据库,第二次进行更改操作也是访问了数据库更新了数据库的数据。第三次的话因为第二次执行了更新操作,所以数据库内容变化需要再次进行访问数据库查询数据。这样才不会出现误读数据。
相同的Mapper类,相同的SQL语句,如果SqlSession不同,则两个SqlSession查询数据库时,会查询数据库两次,这样也会降低数据库的查询效率。为了解决这个问题,就需要用到MyBatis的二级缓存。MyBatis的二级缓存是Mapper级别的缓存,与一级缓存相比,二级缓存的范围更大,多个SqlSession可以共用二级缓存,并且二级缓存可以自定义缓存资源。
在MyBatis中,一个Mapper.xml文件通常称为一个Mapper,MyBatis以namespace区分Mapper,如果多个SqlSession对象使用同一个Mapper的相同查询语句去操作数据库,在第一个SqlSession对象执行完后,MyBatis会将查询结果写入二级缓存,此后,如果程序没有执行插入、更新、删除操作,当第二个SqlSession对象执行相同的查询语句时,MyBatis会直接读取二级缓存中的数据。
与MyBatis的一级缓存不同的是,MyBatis的二级缓存需要手动开启,开启二级缓存通常要完成以下两个步骤
1.需要在MyBatis的核心配置mybatis-config.xml文件中通过
2.修改映射文件BookMapper.xml,在映射文件的
测试核心代码(创建2个sqlsession,都读取一个方法)
/**
* 根据id查询图书信息—-二级缓存
*/
@Test
public void findBookByIdTest3() {
// 1.通过工具类生成SqlSession对象
SqlSession session1 = MyBatisUtils.getSession();
SqlSession session2 = MyBatisUtils.getSession();
// 2.使用session1查询id为1的图书的信息
Book book1 = session1.selectOne("cn.edu.imust.mapper."
+ "BookMapper.findBookById", 1);
// 3.输出查询结果信息
System.out.println(book1.toString());
// 4.关闭SqlSession1
session1.close();
// 5.使用session2查询id为1的图书的信息
Book book2 = session2.selectOne("cn.edu.imust.mapper."
+ "BookMapper.findBookById", 1);
// 6.输出查询结果信息
System.out.println(book2.toString());
// 7.关闭SqlSession2
session2.close();
}
通过下图可看出当第一个SqlSession对象session1执行查询时,Cache Hit Ratio(缓存命中率)为0,程序发送了SQL语句;当第二个SqlSession对象session2执行相同的查询时,Cache Hit Ratio为0.5,程序没有发出SQL语句,这就说明,程序直接从二级缓存中获取了数据。
多个SqlSession在同一个Mapper中执行
在实际开发中,经常会遇到多个SqlSession在同一个Mapper中执行操作,例如,SqlSession1执行查询操作,SqlSession2执行插入、更新、删除操作,SqlSession3又执行和SqlSession1相同的查询操作。当SqlSession1执行查询操作时,程序会将查询结果写入MyBatis二级缓存,当SqlSession2对数据库执行了插入、更新、删除操作后,MyBatis会清空二级缓存中的内容,以防止程序误读。当SqlSession3执行和SqlSession1相同的查询操作时,MyBatis会重新访问数据库。
测试方法(通过3个sqlsession,进行查询,更新,再查询操作)
@Test
public void findBookByIdTest4() {
// 1.通过工具类生成SqlSession对象
SqlSession session1 = MyBatisUtils.getSession();
SqlSession session2 = MyBatisUtils.getSession();
SqlSession session3 = MyBatisUtils.getSession();
// 2.使用session1查询id为1的图书的信息
Book book1 = session1.selectOne("cn.edu.imust.mapper."
+ "BookMapper.findBookById", 1);
// 3.输出查询结果信息
System.out.println(book1.toString());
// 4.关闭SqlSession
session1.close();
Book book2 = new Book();
book2.setId(2);
book2.setBookName("Java Web程序开发进阶");
book2.setPrice(45.0);
// 5.使用session2更新id为2的图书的信息
session2.update("cn.edu.imust.mapper."
+ "BookMapper.updateBook", book2);
session2.commit();
session2.close();
// 6.使用session3查询id为1的图书的信息
Book book3 = session3.selectOne("cn.edu.imust.mapper."
+ "BookMapper.findBookById", 1);
// 7.输出查询结果信息
System.out.println(book3.toString());
}
通过下图可看出第一次查询访问数据库,第二次更新操作也访问数据库,第三次查询时还是要进行访问数据库查询。
补充:
Cache Hit Ratio(缓存命中率)
终端用户访问缓存时,如果在缓存中查找到了要被访问的数据,就叫做命中。如果缓存中没有查找到要被访问的数据,就是没有命中。当多次执行查询操作时,缓存命中次数与总的查询次数(缓存命中次数+缓存没有命中次数)的比,就叫作缓存命中率,即缓存命中率=缓存命中次数/总的查询次数。
Mybatis缓存机制可大大提高查询效率,java代码访问数据库中最频繁的操作就是查询,所以可以有效地减轻数据库的工作量。对我们而言在学习Mybatis时了解这个缓存机制是必要的~