缓存详解

一级缓存

首先做一个测试,创建一个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之后,才会把其中一级缓存数据写入二级缓存。

缓存配置

  • 关闭刷新

在默认情况下,当sqlsession执行commit后会刷新缓存,但是也可以强制设置为不刷新,在不需要刷新的标签中加入

flushCache="false"

<select id="findUsers" resultType="cn.elinzhou.mybatisTest.pojo.User" flushCache="false">

那么,无论是否执行commit,缓存都不会刷新了。但是这样会造成脏读,只有在特殊情况下才使用

  • 自动刷新

有些情况下,需要设置自动刷新缓存,那么需要配置对应mapper中的cache标签。

flushInterval="10000"

该属性表示每隔10秒钟自动刷新一遍缓存

时间: 2024-08-07 21:20:24

缓存详解的相关文章

Cocos2d-X研究之v3.x纹理缓存详解

 概述 在游戏中需要加载大量的纹理图片,这些操作都是很耗内存和资源的. 当游戏中有个界面用到的图片非常多,第一次点进这界面时速度非常慢(因为要加载绘制很多图片)出现卡顿,我们可以使用TextureCache提前异步加载纹理,等加载结束,进入到这个界面再使用这些图片速度就会非常快.对精灵缓存不清楚的看Cocos2d-X研究之v3.x精灵帧缓存 Texture2D: 纹理,即图片加载入内存后供CPU和GPU操作的贴图对象. TextureCache(纹理缓存),用于加载和管理纹理.一旦纹理加载完

Hibernate之缓存详解

hibernate中提供了两级缓存,一级缓存是Session级别的缓存,它属于事务范围的缓存,该级缓存由hibernate管理,应用程序无需干预:二级缓存是SessionFactory级别的缓存,该级缓存可以进行配置和更改,并且可以动态加载和卸载,hibernate还为查询结果提供了一个查询缓存,它依赖于二级缓存: 一,缓存的概念 缓存是位于应用程序和永久性数据存储源之间用于临时存放复制数据的内存区域,缓存可以降低应用程序之间读写永久性数据存储源的次数,从而提高应用程序的运行性能: hibern

HTML5 离线缓存详解(转)

离线缓存是html5新特性之一,简单理解就是第一次加载后将数据缓存,在没有清除缓存前提下,下一次没有网络也可以加载,用在静态数据的网页或游戏比较好用.当然,Html5新的特性都不是所有浏览器都能支持的,离线缓存也一样.反正IE9(包括)及IE9以下的浏览器目前是不支持的.如果用在移动端,应该都能支持.检测是否支持离线缓存也是比较简单的. if(window.applicationCache){ alert("支持离线缓存"); } else{ alert("不支持离线缓存&q

Yii2页面缓存详解

class TestController extends Controller{ // 该方法会在其他方法之前执行 public function behaviors() { // 声明缓存配置 return [ // 需要注意的这里是二维数组 [ 'class' => 'yii\filters\PageCache', // 设置需要加载的缓存文件 'only' => ['index'], // 设置需要缓存的控制器 'duration' => 100, // 设置过期时间 'depen

Yii2片段缓存详解

片段缓存 1 // ..../view/site/index.php页面 2 <?php 3 if($this->beginCache('cache')) { 4 echo "<p class='cache'>这里待会会被缓存</p>"; 5 $this->endCache(); 6 } 7 echo "<p class='no_cache'>这里不会被缓存</p>"; 8 ?> 片段缓存--过

hibernate缓存详解

为什么要用hibernate缓存? hibernate是一个持久层框架,经常访问物理数据库.为了降低应用程序对物理数据源访问的次数,从而提高应用程序的运行性能,我们想到使用hibernate缓存机制.缓存内的数据是对物理数据源中的数据的复制,应用程序在运行时从缓存读写数据,在特定的时刻或事件会同步缓存和物理数据源的数据. hibernate缓存的原理 缓存的主要作用是查询. hibernate缓存包括三大类:hibernate一级缓存.hibernate二级缓存和hibernate查询缓存. 一

Hibernate一级缓存和二级缓存详解

一.一级缓存二级缓存的概念解释 (1)一级缓存就是Session级别的缓存,一个Session做了一个查询操作,它会把这个操作的结果放在一级缓存中,如果短时间内这个 session(一定要同一个session)又做了同一个操作,那么hibernate直接从一级缓存中拿,而不会再去连数据库,取数据: (2)二级缓存就是SessionFactory级别的缓存,顾名思义,就是查询的时候会把查询结果缓存到二级缓存中,如果同一个sessionFactory 创建的某个session执行了相同的操作,hib

【转载】浏览器缓存详解:expires cache-control last-modified

最近在对CDN进行优化,对浏览器缓存深入研究了一下,记录一下,方便后来者 画了一个草图: 每个状态的详细说明如下: 1.Last-Modified 在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记(HttpReponse Header)此文件在服务期端最后被修改的时间,格式类似这样: Last-Modified:Tue, 24 Feb 2009 08:01:04 GMT 客户端第二次请求此URL时,根据HTTP协议

基于Ehcache的Spring缓存详解

一 简介 缓存,通过将数据保存在缓冲区中,可以为以后的相同请求提供更快速的查询,同时可以避免方法的多次执行,从而提高应用的性能. 在企业级应用中,为了提升性能,Spring提供了一种可以在方法级别上进行缓存的缓存抽象.通过使用AOP原则,Spring对使用缓存的方法自动生成相应代理类,如果已经为提供的参数执行过该方法,那么就不必重新执行实际方法而是直接返回被缓存的结果.在基于Spring的Web应用中,为了启用缓存功能,需要使用缓存注解对待使用缓存的方法进行标记. Spring缓存仅仅提供了一种