Mybatis的一级缓存和二级缓存的理解以及用法

  程序中为什么使用缓存?
  先了解一下缓存的概念:原始意义是指访问速度比一般随机存取存储器快的一种 RAM,通常它不像系统主存那样使用 DRAM 技术,而使用昂贵但较快速的 SRAM 技术。对于我们编程来说,所谓的缓存,就是将程序
或系统经常要调用的对象(临时数据)存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例。这样做可以减少系统的开销,提高效率。
  对缓存有了一定的了解以后就知道了使用缓存是为了减少和数据库的交互次数,提高执行效率。那么下一个问题来了。什么样的数据能使用缓存,什么样的数据不能使用?
  这是我们使用缓存必须要明确的事情,实际上适用于缓存的数据:经常查询并且不经常改变的,并且的数据的正确与否对最终结果影响不大的、不适用于缓存的数据:经常改变的数据,数据的正确与否对最终
结果影响很大的。
  Mybatis 中的一级缓存和二级缓存到底缓存了什么,缓存了以后又有什么效果,缓存的数据什么时候会被清空?
  一级缓存:它指的是 Mybatis 中 sqlSession 对象的缓存,当我们执行查询以后,查询的结果会同时存入到 SqlSession 为我们提供的一块区域中,该区域的结构是一个 Map,当我们再次查询同样的数据,mybatis 会
先去 sqlsession 中查询是否有,的话直接拿出来用,当 SqlSession 对象消失时,mybatis 的一级缓存也就消失了,同时一级缓存是 SqlSession 范围的缓存,当调用 SqlSession 的修改、添加、删除、commit(),close 等
方法时,就会清空一级缓存。
  二级缓存:他值得是 Mybatis 中 SqlSessionFactory 对象的缓存,由同一个 SqlSessionFactory 对象创建的 SqlSession 共享其缓存,但是其中缓存的是数据而不是对象,所以从二级缓存再次查询出得结果的对象与
第一次存入的对象是不一样的。
  通过简单的例子来加深理解一级缓存和二级缓存。

 一级缓存

  1. 用户类

public class User implements Serializable{
    private Integer id;
    private String username;
    private Date birthday;
    private String sex;
    private String address;
get和set方法省略.....

}

    2.Dao 层

public interface UserDao {
    /**
     * 查询所有的用户
     *
     * @return
     */
    List<User> findAll();
    /**
     * 根据 Id 查询用户
     *
     * @return
     */
    User findById(Integer id);
    /**
     * 更新用户
     * @param user
     */
    void updateUser(User user);
}

  3.UserDao.xml 映射文件

<mapper namespace="com.example.dao.UserDao">
    <select id="findAll" resultType="com.example.domain.User">
        SELECT * FROM USER;
    </select>
    <select id="findById" resultType="com.example.domain.User" parameterType="INT">
        SELECT * FROM  USER  WHERE ID = #{ID}
    </select>
    <update id="updateUser" parameterType="com.example.domain.User">
        update USER
        <set>
            <if test="username != null">username=#{username},</if>
            <if test="password != null">birthday=#{birthday},</if>
            <if test="sex != null">sex=#{sex},</if>
            <if test="address != null">address=#{address},</if>
        </set>
        where id=#{id}
    </update>
</mapper>

  在以上三步中这是 mybatis 的单表操作,下面通过根据用户 ID 查询用户的操作来观察一级缓存生效与否的区别  

  4. 测试
    (1)  命中一级缓存的情况
    测试代码:

@Test
    public void findByIdTest(){
        session = factory.openSession();
        userDao = session.getMapper(UserDao.class);
        //第一次获取该用户
        User user1 = userDao.findById(45);
        System.out.println(user1);
        第二次获取该用户
        User user2 = userDao.findById(45);
        System.out.println(user2);
        System.out.println(user1 == user2);
        session.close();}  

测试结果:

  

  (2)对 SqlSession 进行清除缓存的操作,即清楚一级缓存,然后再次进行测试。

 @Test
    public void findByIdTest(){
        session = factory.openSession();
        userDao = session.getMapper(UserDao.class);
        User user1 = userDao.findById(45);
        System.out.println(user1);
 //       session.commit(); 调用 SqlSession 的 commit 方法清空缓存

        user1.setUsername("更新用户");
        user1.setAddress("更新地址");
        userDao.updateUser(user1);//通过更新 SqlSession 清空缓存
        User user2 = userDao.findById(45);
        System.out.println(user2);
        System.out.println(user1 == user2);
        session.close();}

  清空缓存的操作很多,可以都试试。测试结果:

  二级缓存  

  再看一下 Mybatis 二级缓存是如何使用的,第一步让 Mybatis 框架支持二级缓存(在 Mybatis 的主配置文件中配置),第二步让当前的映射文件支持二级缓存(在 Dao.xml 映射文件中配置),第三步让当前的方法支持二级缓存(在标签中配置)。根据这个步骤将上面的查询用户的接口通过配置改造为可以支持二级缓存的方法。
  1. 配置 Mybatis 框架支持二级缓存

<setting name="cacheEnabled" value="true"/>

  2. 配置 UserDao.xml 支持二级缓存

 <cache/>

  3. 配置查询的方法支持二级缓存

<select id="findById" resultType="com.example.domain.User" parameterType="INT" useCache="true">
        SELECT * FROM  USER  WHERE ID = #{ID}
   </select>

  4. 测试

@Test
    public void findByIdTest(){
        //第一次查询 并更新二级缓存
        SqlSession session1 = factory.openSession();
        UserDao userDao1 = session1.getMapper(UserDao.class);
        User user1 = userDao1.findById(45);
        System.out.println(user1);
        session1.commit(); //commit() 方法提交二级缓存 同时清空一级缓存
        session1.close();//

// user1.setUsername("更新用户");
// user1.setAddress("更新地址");
// userDao.updateUser(user1);//通过更新 SqlSession 清空缓存
//第二次查找命中二级缓存
SqlSession session2 = factory.openSession();
UserDao userDao2
= session2.getMapper(UserDao.class);
User user2
= userDao2.findById(45);
session2.commit();
//commit() 方法提交二级缓存 同时清空一级缓存
session2.close();//
System.out.println(user2);
System.out.println(user1
== user2);
}

  测试结果:

 

 总结:mybatis 的的一级缓存是 SqlSession 级别的缓存,一级缓存缓存的是对象,当 SqlSession 提交、关闭以及其他的更新数据库的操作发生后,一级缓存就会清空。二级缓存是 SqlSessionFactory 级别的缓存,同一个 SqlSessionFactory 产生的 SqlSession 都共享一个二级缓存,二级缓存中存储的是数据,当命中二级缓存时,通过存储的数据构造对象返回。查询数据的时候,查询的流程是二级缓存 > 一级缓存 > 数据库