Mybatis缓存

Mybatis缓存

像大多数的持久化框架一样,Mybatis 也提供了缓存策略,通过缓存策略来减少数据库的查询次数,从而提
高性能。
Mybatis 中缓存分为一级缓存,二级缓存。

1. Mybatis一级缓存

1.1 证明一级缓存的存在
    一级缓存是session级别的缓存,只要sqlSession没有flush或者close,它就存在。无需配置

1.1.1 编写用户持久层接口

/**
 * 用户的持久层接口
 * Ceate By llb on 2019/8/5
 */
public interface UserMapper {
    /**
     * 根据id查询用户
     * @return
     */
    User findUserById(int id);

}

1.1.2 编写用户持久层映射文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.llb.dao.UserMapper">
    <!--useCache-->
    <select id="findUserById" resultType="user" parameterType="int" useCache="true">
        select * from user where id = #{id}
    </select>
</mapper>

1.1.3 编写测试方法

package com.llb.test;

/**
 * Ceate By llb on 2019/8/5
 */
public class UserTest {

    InputStream in = null;
    UserMapper mapper = null;
    SqlSession sqlSession = null;
    SqlSessionFactory factory = null;
    /**
     * 在测试方法执行前执行
     * @throws IOException
     */
    @Before
    public void init() throws IOException {
        //1.读取配置文件,生成字节流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取sqlSessionFactory对象
        factory = new SqlSessionFactoryBuilder().build(in);
        //3.获取sqlSession对象
        sqlSession = factory.openSession();
        //4.获取dao的代理对象
        mapper = sqlSession.getMapper(UserMapper.class);
    }

    /**
     * 测试方法执行后执行
     * @throws IOException
     */
    @After
    public void destory() throws IOException {
        sqlSession.commit();
        //6.释放资源
        sqlSession.close();
        in.close();
    }
    /**
     * 根据id查询用户,sqlSession一级缓存:
     *  当调用sqlSession的修改、添加、删除,commit() ,close(),clear()等方法时,会清空缓存
     */
    @Test
    public void findUserById(){
        User user = mapper.findUserById(41);

        User user2 = mapper.findUserById(41);

        System.out.println(user == user2);
    }

}

测试结果:



    我们虽然查询了两次,但最后只查询了一次数据库,这就是Mybatis提供给我们得一级缓存起作用。因为一级缓存的存在,导致第二次查询id为41的记录时,并没有发出sql语句从数据库中查询数据,而是从一级缓存中查询。

1.1.2 一级缓存的分析
      一级缓存是SqlSession范围的缓存,当调用SqlSession的修改、添加、删除时、commit、close等方法就会清空缓存。


第一次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,如果没有,从数据库查询用户信息。
  得到用户信息,将用户信息存储到一级缓存中。
  如果 sqlSession 去执行 commit 操作(执行插入、更新、删除),清空 SqlSession 中的一级缓存,这样做的目的为了让缓存中存储的是最新的信息,避免脏读。
  第二次发起查询用户 id 为 1 的用户信息,先去找缓存中是否有 id 为 1 的用户信息,缓存中有,直接从缓存中获取用户信息。
    2.1.3 测试一级缓存的清空

    /**
     * 根据id查询用户,sqlSession一级缓存:
     *  当调用sqlSession的修改、添加、删除,commit() ,close(),clear()等方法时,会清空缓存
     */
    @Test
    public void findUserById(){
        User user = mapper.findUserById(41);

        //关闭sqlSession会清空缓存
        sqlSession.close();
        sqlSession = factory.openSession();
        mapper = sqlSession.getMapper(UserMapper.class);

        //直接对sqlSession进行清空
//        sqlSession.clearCache();

        User user2 = mapper.findUserById(41);

        System.out.println(user == user2);
    }


当执行sqlSession.close()后,再次获取sqlSession并查询id=41的User对象时,又重新执行了sql 语句,从数据库进行了查询操作。导致两个用户的地址不一致。

2. Mybatis二级缓存(XML)

二级缓存是mapper映射级别的缓存,多个sqlSession去操作同一个mapper映射的sql语句,多个sqlSession可以共用二级缓存,二级缓存是跨sqlSession的。
  2.1 二级缓存的结构图

   首先开启Mybatis的二级缓存。

sqlSession1去查询用户信息,查询到用户信息会将查询数据存储到二级缓存中。
    当sqlSession2去查询同个用户信息时,首先会去二级缓存中查找是否存在数据,如果存在直接从缓存中取出数据。
    如果sqlSession3去执行相同mapper映射下sql,执行commit提交,将会清空该mapper映射下的二级缓存区域的数据。

2.2 二级缓存的开启与关闭
    2.2.1 第一步:在SqlMapConfig.xml文件开启二级缓存

<!--开启二级缓存,默认值为true,默认开启-->
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

2.2.2 第二部:配置相关的Mapper映射文件
     
<cache>标签表示当前这个mapper映射将使用二级缓存,区分就看namespace的值

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.llb.dao.UserMapper">

    <!--开启user支持二级缓存-->
    <cache/>
</mapper>
     2.2.3 第三步:配置statement上面的useCache属性
Mybatis缓存
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.llb.dao.UserMapper">

    <!--开启user支持二级缓存-->
    <cache/>
    <!--useCache,对该方法使用二级缓存-->
    <select id="findUserById" resultType="user" parameterType="int" useCache="true">
        select * from user where id = #{id}
    </select>
</mapper>
  

注意:针对每次查询都需要最新数据的sql,要设置成useCache=false,禁用二级缓存。
   2.2.3 二级缓存测试

package com.llb.test;
/**
 * Ceate By llb on 2019/8/5
 */
public class SecondLevelCache {

    InputStream in = null;
    SqlSessionFactory factory = null;
    /**
     * 在测试方法执行前执行
     * @throws IOException
     */
    @Before
    public void init() throws IOException {
        //1.读取配置文件,生成字节流
        in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.获取sqlSessionFactory对象
        factory = new SqlSessionFactoryBuilder().build(in);
    }

    /**
     * 测试方法执行后执行
     * @throws IOException
     */
    @After
    public void destory() throws IOException {

        in.close();
    }


    /**
     * 根据id查询用户,二级缓存:
     *      存放的是数据,而不是对象
     */
    @Test
    public void findUserById(){

        SqlSession sqlSession = factory.openSession();
        UserMapper mapper1 = sqlSession.getMapper(UserMapper.class);

        User user1 = mapper1.findUserById(41);

        System.out.println(user1);

        //关闭缓存
        sqlSession.close();

        SqlSession sqlSession2 = factory.openSession();
        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);

        User user2 = mapper2.findUserById(41);

        System.out.println(user2);

        System.out.println(user1 == user2);
    }
}

经过上面测试,我们发现执行了两次查询,并且在第一次查询后关闭了一级缓存,再去执行二级缓存时,我们发现并没有对数据库发出SQL语句,所以此时的数据就只能来自二级缓存
    2.2.4 二级缓存注意事项

当我们在使用二级缓存时,所缓存的类一定要实现java.io.Serializable接口,这种就可以使用序列化方式保存对象

package com.llb.domain;
import java.io.Serializable;

/**
 * Ceate By llb on 2019/8/5
 */
public class User implements Serializable{

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


3. 使用注解实现二级缓存

3.1 在SqlMapConfig中开启二级缓存支持

<!-- 配置二级缓存 --> 

<settings>
<!-- 开启二级缓存的支持 --> 

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

3.2 在持久层接口中使用注解配置二级缓存

package com.llb.dao;

import java.util.List;

//开启二级缓存
@CacheNamespace(blocking = true)
public interface UserMapper {

}

MyBatis注解开发

github:https://github.com/PopsiCola/SSM-mybatis/tree/cache
欢迎star~

博客园:https://www.cnblogs.com/liulebin/p/11342087.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容