Mybatis一级、二级缓存

 

一级缓存

首先做一个测试,创建一个mapper配置文件和mapper接口,我这里用了最简单的查询来演示。

<mapper namespace="cn.elinzhou.mybatisTest.mapper.UserMapper">

    <select id="findUsers" resultType="cn.elinzhou.mybatisTest.pojo.User">
SELECT * FROM user
</select>
</mapper>
public interface UserMapper {
List<User> findUsers()throws Exception;
}

然后编写一个单元测试

public class UserMapperTest {

    SqlSession sqlSession = null;
@Before
public void setUp() throws Exception {
// 通过配置文件获取数据库连接信息
Reader reader = Resources.getResourceAsReader("cn/elinzhou/mybatisTest/config/mybatis.xml");
// 通过配置信息构建一个SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
// 通过sqlSessionFactory打开一个数据库会话
sqlSession = sqlSessionFactory.openSession();
} @Test
public void testFindUsers() throws Exception {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.findUsers();
System.out.println(users);
}
}

运行,可以看到控制台输出(先配好log4j)为类似如下图日志

日志说明了该操作执行的sql语句已经查询的内容,最后一行是我手动通过System.out.printf输出的结果。

然后再加一条语句

users = userMapper.findUsers();

之前的单元测试就变成了这个样子

也就是在执行完userMapper.findUsers();后立刻再执行一遍userMapper.findUsers(); 可以想象,其实这两个操作执行的sql是完全相同的,而且在这期间没有对数据库进行过其他操作。然后执行该单元测试,发现效果跟上面执行一条的时候完全相 同,也就是执行第二次userMapper.findUsers();操作的时候没有对数据库进行查询,那么得到的数据是从哪里来的?答案是一级缓存。

mybatis一级缓存是指在内存中开辟一块区域,用来保存用户对数据库的操作信息(sql)和数据库返回的数据,如果下一次用户再执行相同的请求,那么直接从内存中读数数据而不是从数据库读取。
其中数据的生命周期有两个影响因素。

  1. 对sqlsession执行commit操作时

对sqlsession执行commit操作,也就意味着用户执行了update、delete等操作,那么数据库中的数据势必会发生变化,如果用
户请求数据仍然使用之前内存中的数据,那么将读到脏数据。所以在执行sqlsession操作后,会清除保存数据的HashMap,用户在发起查询请求时
就会重新读取数据并放入一级缓存中了。

上述测试就是在第一查询完后执行了commit操作,再进行查询。与之前的测试不同的是,这次测试控制台打印了两组查询结果,说明在commit之后mybatis对数据重新进行了查询。

  1. 关闭sqlsession

一般在mybatis集成spring时,会把SqlSessionFactory设置为单例注入到IOC容器中,不把sqlsession也设置
为单例的原因是sqlsession是线程不安全的,所以不能为单例。那也就意味着其实是有关闭sqlsession的过程的。其实,对于每一个
service中的sqlsession是不同的,这是通过mybatis-spring中的
org.mybatis.spring.mapper.MapperScannerConfigurer创建sqlsession自动注入到
service中的。
而一级缓存的设计是每个sqlsession单独使用一个缓存空间,不同的sqlsession是不能互相访问数据的。当然,在sqlsession关闭后,其中数据自然被清空。

二级缓存

在使用二级缓存之前,先测试之前提到过的关闭sqlsession后会清空缓存的问题,把junit代码修改一下

@Test
public void testFindUsers() throws Exception {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.findUsers();
//关闭sqlsession
sqlSession.close(); //通过sqlsessionFactroy创建一个新的sqlsession
sqlSession = sqlSessionFactory.openSession();
//获取mapper对象
userMapper = sqlSession.getMapper(UserMapper.class);
users = userMapper.findUsers();
System.out.println(users);
}

这段代码在第一次查询完后关闭sqlsession,然后创建新的sqlsession和mapper来重新执行一次查询操作,可以预见,执行结果如图

说明关闭了sqlsession后的确把之前的缓存数据清空了,之后再执行同样的查询操作也会再访问一遍数据库。为了解决这个问题,需要使用二级缓存

一级缓存的作用域仅限于一个sqlsession,但是二级缓存的作用域是一个namespace。但并不是意味着同一个namespace创建的mapper可以互相读取缓存内容,这里的原则是,如果开启了二级缓存,那么在关闭sqlsession后,会把该sqlsession一级缓存中的数据添加到namespace的二级缓存中。

接下测试,先需要开启二级缓存。

1.打开二级缓存总开关
打开总开关,只需要在mybatis总配置文件中加入一行设置

<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
</settings>

2.打开需要使用二级缓存的mapper的开关

在需要开启二级缓存的mapper.xml中加入caceh标签

<cache/>

3.POJO序列化

让需要使用二级缓存的POJO类实现Serializable接口,如

public class User implements Serializable {

通过之前三步操作就可以使用二级缓存了,接下来测试。添加一个Junit方法

@Test
public void testFindUsersCache() throws Exception {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
List<User> users = userMapper.findUsers();
//关闭sqlsession
sqlSession.close(); //通过sqlsessionFactroy创建一个新的sqlsession
sqlSession = sqlSessionFactory.openSession();
//获取mapper对象
userMapper = sqlSession.getMapper(UserMapper.class);
users = userMapper.findUsers();
System.out.println(users);
}

执行后可以发现,控制台值输出了一次查询过程,也可以证明二级缓存开启成功。

还有一个问题,之前说了,即使开启了二级缓存,不同的sqlsession之间的缓存数据也不是想互访就能互访的,必须等到sqlsession关闭了以后,才会把其一级缓存中的数据写入二级缓存。为了测试这个,把上述代码中的

sqlSession.close();

注释,那么之前的代码就变成了

再执行,发现控制太又输出了两次的查询过程,所以可以印证,只有关闭了sqlsession之后,才会把其中一级缓存数据写入二级缓存。

Mybatis一级、二级缓存的更多相关文章

  1. 延迟加载以及mybatis一级二级缓存

    延迟加载 延迟加载:在真正使用数据时才发起查询,不用的时候不查询,又叫按需查询(懒加载) 立即加载:不管用不用,只要调用方法,直接发起查询 表关系:一对多     多对一     一对一        ...

  2. mybatis一级二级缓存

    一级缓存分析 不需要任何配置 ,mybatis本身带有 一级缓存是SqlSession范围的缓存,当调用SqlSession的修改,添加,删除,commit(),close()等方法时,就会清空一级缓 ...

  3. Mybatis五(一级二级缓存、第三方缓存)

    一级缓存 Mybatis对缓存提供支持,但是在没有配置的默认情况下,它只开启一级缓存,一级缓存只是相对于同一个SqlSession而言.所以在参数和SQL完全一样的情况下,我们使用同一个SqlSess ...

  4. 使用Redis做MyBatis的二级缓存

    使用Redis做MyBatis的二级缓存 通常为了减轻数据库的压力,我们会引入缓存.在Dao查询数据库之前,先去缓存中找是否有要找的数据,如果有则用缓存中的数据即可,就不用查询数据库了. 如果没有才去 ...

  5. MyBatis:二级缓存原理分析

    MyBatis从入门到放弃七:二级缓存原理分析 前言 说起mybatis的一级缓存和二级缓存我特意问了几个身边的朋友他们平时会不会用,结果没有一个人平时业务场景中用. 好吧,那我暂且用来学习源码吧.一 ...

  6. Mybatis的二级缓存注意点

    --声明:一下内容都不一定是正确的,只是自己测试的结果,请自己的动手操作得出自己的结论 1.开启Mybatis的二级缓存,不仅要在SqlMapConfig.xml中进行开启总开关,还要在对应的XXXM ...

  7. mybatis开启二级缓存小记

    mybatis开启二级缓存小记 1.开启二级缓存 和一级缓存默认开启不一样,二级缓存需要我们手动开启 首先在全局配置文件 mybatis-configuration.xml 文件中加入如下代码: &l ...

  8. 《深入理解mybatis原理7》 MyBatis的二级缓存的设计原理

    <深入理解mybatis原理> MyBatis的二级缓存的设计原理 MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能.本文将全面分 ...

  9. 《深入理解mybatis原理》 MyBatis的二级缓存的设计原理

    MyBatis的二级缓存是Application级别的缓存,它可以提高对数据库查询的效率,以提高应用的性能.本文将全面分析MyBatis的二级缓存的设计原理. 如上图所示,当开一个会话时,一个SqlS ...

  10. Redis实现Mybatis的二级缓存

    一.Mybatis的缓存 通大多数ORM层框架一样,Mybatis自然也提供了对一级缓存和二级缓存的支持.一下是一级缓存和二级缓存的作用于和定义. 1.一级缓存是SqlSession级别的缓存.在操作 ...

随机推荐

  1. android 入门-android自定义控件

    第一种:继承View 实现自己的属性 <com.cc.imagewithmarkersample.MyView android:id="@+id/myviewid" andr ...

  2. ThinkPHP中getField( )和field( )

    做数据库查询的时候,比较经常用到这两个,总是查手册,记不住,现在把它总结下,希望以后用的时候不查手册了. 不管是用select 查询数据集,还是用find 查询数据,常配合连贯操作where.fiel ...

  3. 如何通过阅读C标准来解决C语言语法问题

    有时候必须非常专注地阅读ANSI C标准才能找到某个问题的答案.一位销售工程师把下面这段代码作为测试用例发给Sun的编译小组. foo(const char **p) {} int main(int ...

  4. ok6410按键中断编程,linux按键裸机

    6410按键中断编程 一.流程分析 外部中断控制寄存器(s3c6410x  359页) 1.EINTxCONy: 外部中断组x的第y个控制器.这个就是设置中断的触发方式.有5种触发方式. 2.EINT ...

  5. VS2010下配置OpenMesh

    从www.openmesh.org下载最新版的安装包或者源代码,注意下载与自己系统匹配的版本,我下的是VS2010预编译版的,下载源码自己编译是一样的.安装好Visual Studio. 安装Open ...

  6. ubuntu12.04 安装eclipse

    1:去官网下载最新版的eclipse for linux; 2:cd  /usr/local 用命令 sudo mkdir eclipse 建立一个Eclipse的目录 3:将下载的文件copy到ec ...

  7. 如果 if - 迈克.杰克逊的墓志铭

    引用http://www.duwenzhang.com/wenzhang/yingyuwenzhang/20110215/171059.html IF you can keep your head w ...

  8. WebAPI接口调用身份验证

    Common public interface ICacheWriter { void AddCache(string key, object value, DateTime expDate); vo ...

  9. 2016.8.27 JavaScript入门之四

    1.比较运算符“>”也可以,用数字和字符串进行比较: 2.比较运算符“&&”,表示并且: 3.比较运算符“||”,表示或者: 4.顺序是重要的,循环和if判断的顺序决定了,程序的 ...

  10. vim使用02

    编辑 剪切光标所在的字符: <x>; 剪切并插入: <s> 撤销操作:撤销至上一个命令之间的修改: <u> 恢复上一次撤销操作: <C r> 剪切光标所 ...