MyBatis缓存

MyBatis缓存

先来回答几个问题:

  1. 什么是缓存?
    存在内存中的临时数据
  2. 为什么使用缓存
    减少和数据库的交互次数,提高执行效率
  3. 什么样的数据能使用缓存,什么样的数据不用缓存
    经常查询且不经常修改的数据适合使用缓存,经常修改的数据不适合使用缓存;由于缓存的使用会产生数据不一致的问题,所以数据的正确性对最终结果影响很大的也不适合使用缓存

MyBatis中的一级缓存和二级缓存

一级缓存

它是指MyBatis中的SqlSession对象的缓存
当我们执行查询之后,查询结果会同时存入到SqlSession为我们提供的一块区域中,该区域是一个map结构,当我们再次查询同样的数据,MyBatis先去SqlSession中查询是否已经存在,如果存在直接拿来使用.
当SQLSession对象消失,MyBatis的一级缓存也就消失了.
为了证明上面说的,我们给出代码例子:

User类

public class User implements Serializable {

    private Integer id;
    private String username;
    private String address;
    private String sex;
    private Date birthday;

    public void setId(Integer id) {
        this.id = id;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public Integer getId() {
        return id;
    }

    public String getUsername() {
        return username;
    }

    public String getAddress() {
        return address;
    }

    public String getSex() {
        return sex;
    }

    public Date getBirthday() {
        return birthday;
    }
}

这里并没有重写toString()方法,主要是为了一会方便观察现象.

dao中方法:

    /**
     * 根据id查询用户信息
     * @param userId
     * @return
     */
    User findById(Integer userId);

xml文件内容:

    <!--根据id查询用户信息-->
    <select id="findById" parameterType="Integer" resultType="org.zjb.domain.User">
        select * from user where id=#{id};
    </select>

测试类:

    /**
     * 测试一级缓存
     */
    @Test
    public void testFirstLevelCache() throws IOException {
        init();
        User user1 = userDao.findById(41);
        System.out.println(user1);
        User user2 = userDao.findById(41);
        System.out.println(user2);
        System.out.println("user1是否等于user2?  " + (user1 == user2));
        destroy();
    }

测试类中,我们分别使用两个对象来接受两次查询的结果,最后比较两个对象的地址.

日志结果如下:
日志文件
从这个结果可以看出,我们调用了两次方法,但是sql语句只执行了一次,而且两个对象的地址一样,这说明两个对象是同一个,缓存起作用了!

好,现在我们修改一下测试类,将SQLSession对象重新创建,来证明一级缓存的消息
test测试类修改为如下:

    /**
     * 测试清理一级缓存
     */
    @Test
    public void testFirstLevelCache() throws IOException {
        init();
        User user1 = userDao.findById(41);
        System.out.println(user1);

        // 关闭缓存,测试一级缓存清理的情况
        sqlSession.close();
        // 再次获取SqlSession对象
        sqlSession = factory.openSession();
        // 重新获取代理对象
        userDao = sqlSession.getMapper(IUserDao.class);
        User user2 = userDao.findById(41);
        System.out.println(user2);
        System.out.println("user1是否等于user2?  " + (user1 == user2));
        destroy();
    }

执行结果如下:
日志结果
可以看见,sqlSession对象重新创建之后,缓存确实消失了,sql语句执行了两次.

除了上面的主动close之外,SqlSession提供了专门的方法清空缓存,为sqlSession.clearCache();
下面给出演示代码:
test测试代码

    /**
     * 测试调用方法清空缓存
     */
    @Test
    public void testFirstLevelCache() throws IOException {
        init();
        User user1 = userDao.findById(41);
        System.out.println(user1);

        // 主动调用方法清空缓存对象
        sqlSession.clearCache();

        User user2 = userDao.findById(41);
        System.out.println(user2);
        System.out.println("user1是否等于user2?  " + (user1 == user2));
        destroy();
    }

这个方法的执行结果如下:
日志信息
可以看见和上面的主动释放资源是一致的.

一级缓存大概搞明白了,现在来了一个新的问题:
如果数据库的数据跟我们的一级缓存不一致了,MyBatis是如何做到数据同步的呢?
我们现在直接看例子:
test测试方法:

    /**
     * 测试刷新缓存
     * @throws IOException
     */
    @Test
    public void testClearCache() throws IOException {
        init();
        // 根据id查询用户
        User user1 = userDao.findById(41);
        System.out.println(user1);

        // 更新用户新
        user1.setUsername("娃哈哈");
        user1.setAddress("陕西省");
        userDao.updateUser(user1);
        
        // 再次查询id为41的用户
        User user2 = userDao.findById(41);
        System.out.println(user2);
        System.out.println("user1和user2是否相等?   " + (user1 == user2));
        destroy();

    }

运行结果为:
日志信息
可以看见user1和user2并不相等,我们在更新了数据库信息之后,MyBatis似乎是有某种机制帮助我们更新是缓存.
实际上,当调用sqlSession的修改,添加,删除,commit(), close()等方法时,MyBatis为我们主动清空一级缓存.

二级缓存

它是指MyBatis中SqlSessionFactory对象的缓存,同一个SqlSessionFactory对象创建的SqlSession共享其缓存.
二级缓存的使用步骤如下:

  1. 让MyBatis框架支持二级缓存,需要在主配置文件中配置(在SqlMapConfig.xml中配置)
    <!--配置二级缓存-->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>
  1. 让当前的映射文件支持二级缓存(在对应dao的xml文件中配置)
    mapper标签中加入下面的标签
    <!--开启user支持二级缓存-->
    <cache/>
  1. 让当前的操作支持二级缓存(在Select标签中配置)
    <!--根据id查询用户信息-->
    <!--标签开启二级缓存-->
    <select id="findById" useCache="true" parameterType="Integer" resultType="org.zjb.domain.User">
        select * from user where id=#{id};
    </select>

在完成了上面的操作之后,我们来运行下面的测试代码:
test测试方法

    /**
     * 测试二级缓存
     */
    @Test
    public void testSecondLevelCache() throws IOException {
        init();
        SqlSession sqlSession1 = factory.openSession();
        IUserDao dao1 = sqlSession1.getMapper(IUserDao.class);
        User user1 = dao1.findById(41);
        System.out.println(user1);
        sqlSession1.close();// 一级缓存消失

        SqlSession sqlSession2 = factory.openSession();
        IUserDao dao2 = sqlSession2.getMapper(IUserDao.class);
        User user2 = dao2.findById(41);
        System.out.println(user2);
        sqlSession2.close(); // 一级缓存消失

        System.out.println("user1是否等于user2?  " + (user1 == user2));
        destroy();
    }

上面的factory对象是同一个,所以即使创建出多个sqlSession对象,只要他们查询相同的东西都会走缓存,这里user1和user2的地址应该是相同的.
我们看看结果:
日志信息
可以看见日志信息中,确实只执行了一次sql语句,但是为什么两个对象==的结果为false呢?
实际上MyBatis的二级缓存中存放的并不是一个对象,而是类似于文本的字符串,如果再次需要才会通过反序列化的手段转换为对象,user1和user2内容是一样的,而且确实是一次sql执行的结果,只不过反序列化的过程又构造了新的对象,导致这两个对象实际上内存地址不同,所以是false.由于涉及到序列化和反序列化,需要实现Serializable接口.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值