MyBatis 二(2)之缓存

本文深入解析MyBatis的一级缓存和二级缓存工作原理,包括缓存的触发条件、作用范围及如何配置。通过示例代码展示缓存如何提高查询效率,避免脏数据,并减少数据库访问。

缓存:

  1. 缓存是将用户经常需要查询的数据放入到内存(缓存)中,用户不需要经常访问磁盘,提高查询效率,解决并发性能问题;
  2. MyBatis 持久层缓存
    MyBatis 提供一级缓存和二级缓存:
    ① MyBatis 一级缓存是一个 sqlSession级别的,sqlSession对象只能访问自己的一级缓存;
    ② MyBatis 二级缓存是 mapper级别的;对于 mapper级别的缓存,不同的 sqlSession是可以共享的;
    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

1. 一级缓存:

原理:
第一次发出查询,写入 sqlSession缓存区,同一个 sqlSession再次发出相同的 sql,从缓存中取数据;
如果两次操作中间有 commit操作,一级缓存区全部清空,下次再要查询,先操作数据库,再写入缓存;
目的:防止查询出脏数据;(eg:执行 update操作,数据库中数据已改,但缓存区中未改,如果清空缓存区,直接再次从缓存区读取数据,会查询出脏数据)
一级缓存在 MyBatis中默认支持,无需配置;
如果缓存中存在数据,则直接返回;如果缓存中不存在数据,查数据库

UserMapper.java

public interface UserMapper {
    public User findUserById(int id) throws Exception;
    public void updateUser(User user) throws Exception;
}

AppTest1.java

public class AppTest {
    SqlSessionFactory sqlSessionFactory=null;
    @Before
    public void init() throws IOException {
        InputStream inputStream=Resources.getResourcesAsStream("SqlMapConfig.xml");
        sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public  void test() throws Exception(){
        SqlSession sqlSession=sqlSessionFactory.openSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        //第一次
        User user=userMapper.findUserById(1);
        System.out.println(user.getId()+","+user.getUsername()+","+user.getAge());
        //第二次
        User user2=userMapper.findUserById(1);
        System.out.println(user2.getId()+","+user2.getUsername()+","+user2.getAge());
        sqlSession.close();
    }   
}

运行结果:
(两次都查询 id=1的用户,sql 语句只执行一次,第二次是从缓存中获取)
在这里插入图片描述
AppTest2.java

public class AppTest {
    SqlSessionFactory sqlSessionFactory=null;
    @Before
    public void init() throws IOException {
        InputStream inputStream=Resources.getResourcesAsStream("SqlMapConfig.xml");
        sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public  void test() throws Exception(){
        SqlSession sqlSession=sqlSessionFactory.openSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        //第一次
        User user=userMapper.findUserById(1);
        System.out.println(user.getId()+","+user.getUsername()+","+user.getAge());
        //第二次
        User user2=userMapper.findUserById(2);
        System.out.println(user2.getId()+","+user2.getUsername()+","+user2.getAge());
        sqlSession.close();
    }   
}

运行结果:
(两次查询 id不同的用户,sql 语句肯定执行两次)
在这里插入图片描述
AppTest3.java

public class AppTest  {
    SqlSessionFactory sqlSessionFactory=null;
    @Before
    public void init() throws IOException {
        InputStream inputStream=Resources.getResourcesAsStream("SqlMapConfig.xml");
        sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public  void test() throws Exception(){
        SqlSession sqlSession=sqlSessionFactory.openSession();
        UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
        //第一次
        User user=userMapper.findUserById(1);
        System.out.println(user.getId()+","+user.getUsername()+","+user.getAge());
        //添加一个commit操作
        user.setUsername("jay");
        userMapper.updateUser(user);
        sqlSession.commit();
        //第二次
        User user2=userMapper.findUserById(1);
        System.out.println(user2.getId()+","+user2.getUsername()+","+user2.getAge());
        sqlSession.close();
    }   
}

运行结果:
(两次都查询 id=1的用户,中间执行了 update操作,清空了缓存区,sql 语句执行两次)
在这里插入图片描述

1.二级缓存:

二级缓存场景:
对于查询频率高,变化频率低,使用二级缓存;
二级缓存降低对数据库的访问,提高访问速度。
局限性:
对于数据量较多的查询缓存,不方便。
原理:
1. 二级缓存的范围: Mapper 级别(同一个命名空间),Mapper以命名空间为单位,创建缓存区域;
2. 二级缓存的配置:
1. 全局配置文件中 开启全局缓存

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

2. mapper映射文件中添加一行,开启 mapper映射文件的二级缓存

<cache></cache>

3. 查询结果映射到 POJO, POJO类实现Serializable接口;(二级缓存可以将内存的数据写入磁盘,对象进行序列化和反序列化)

3. 二级缓存(开启后对某些 statement)的禁用
statement 设置禁用 useCache=“false”(默认为 true)

<mapper namespace="com.mdd.mapper.UserMapper">
    <cache></cache>
    
    <select id="findUserById" parameterType="int" resultType="user" useCache="false">
        select * from users where id=#{id}
    </select>
</mapper>

4. 刷新缓存(二级缓存的刷新):
如果 sqlSession有 commit操作,会默认对二级缓存进行刷新 (默认为 true);若对对应的 statement设置 flushCache=“false” 则关闭了二级缓存的刷新。

<mapper namespace="com.mdd.mapper.UserMapper">
    <cache></cache>
    <!-- 关闭了二级缓存的刷新(修改用户id一定要有值) -->
    <update id="updateUser" parameterType="com.mdd.po.User" flashCache="false">
    	update users set username=#{username},age=#{age} where id=#{id}
    </update>
    <select id="findUserById" parameterType="int" resultType="user" useCache="false">
        select * from users where id=#{id}
    </select>
</mapper>

SqlMapConfig.xml

<configuration>
    <settings> //开启全局缓存
	    <setting name="cacheEnabled" value="true"/>
    </settings>
    
    <typeAliase> //别名定义
        <package name="com.iotek.po"/>
    </typeAliase>
    <properties resource="db.prperties"></properties> //加载jdbc连接属性文件
    <environments default="mysql"> //环境变量配置
        <environment id="mysql">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    
    <mappers>//mapper映射文件配置
        <package name="com.iotek.mapper"/>            
    </mappers>
</configuration>

UserMapper.xml

<mapper namespace="com.mdd.mapper.UserMapper">
    <cache></cache> //开启mapper映射文件的二级缓存
    
    //关闭mapper映射文件中某些statement的二级缓存(在对应statement后加useCache="false")
    <select id="findUserById" parameterType="int" resultType="user" useCache="false"> 
        select * from users where id=#{id}
    </select>
</mapper>

User.java

public class User implements Serializable{//实现Serializable接口
	private int id;
	private String username;
	private int age;
	set、get();
}

AppTest.java

public class AppTest {
    SqlSessionFactory sqlSessionFactory=null;
    @Before
    public void init() throws IOException {
        InputStream inputStream=Resources.getResourcesAsStream("SqlMapConfig.xml");
        sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public  void testFindUserList() throws Exception(){
        SqlSession sqlSession1=sqlSessionFactory.openSession();
        SqlSession sqlSession2=sqlSessionFactory.openSession();
        UserMapper userMapper1=sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2=sqlSession2.getMapper(UserMapper.class);
        //第一次
        User user1=userMapper1.findUserById(1);
        System.out.println(user1.getId()+","+user1.getUsername());
        sqlSession1.close();
        //第二次
        User user2=userMapper2.findUserById(1);
        System.out.println(user2.getId()+","+user2.getUsername());
        sqlSession2.close();
    }
}

运行结果:
(第一次查找缓存命中率为0,所以从数据库中查找;第二次直接在缓存中查找)在这里插入图片描述
运行结果(禁用 id="findUserById"的 statement后):
(禁用后没有二级缓存,每次查询都要从数据库中查询,所以执行两次 SQL语句)
在这里插入图片描述
开启或关闭了二级缓存的刷新后:
AppTest.java

public class AppTest {
    SqlSessionFactory sqlSessionFactory=null;
    @Before
    public void init() throws IOException {
        InputStream inputStream=Resources.getResourcesAsStream("SqlMapConfig.xml");
        sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public  void testFindUserList() throws Exception(){
        SqlSession sqlSession1=sqlSessionFactory.openSession();
        SqlSession sqlSession2=sqlSessionFactory.openSession();
        UserMapper userMapper1=sqlSession1.getMapper(UserMapper.class);
        UserMapper userMapper2=sqlSession2.getMapper(UserMapper.class);
        //第一次
        User user1=userMapper1.findUserById(1);
        System.out.println(user1.getId()+","+user1.getUsername());
        sqlSession1.close();
        //commit操作
        SqlSession sqlSession3=sqlSessionFactory.openSession();
        UserMapper userMapper3=sqlSession3.getMapper(UserMapper.class);
        user1.setUsername("Steve");
        userMapper3.updateUser(user);
        sqlSession1.commit();
        sqlSession1.close();
        //第二次
        User user2=userMapper2.findUserById(1);
        System.out.println(user2.getId()+","+user2.getUsername());
        sqlSession2.close();
    }
}

开启了二级缓存的刷新即是默认状态或 mapper映射文件中对应的 statement添加 flushCache=“true”
(更新进行 commit操作,刷新了缓存;第二次查找,缓存命中率为0,从数据库中查找)
运行结果:
在这里插入图片描述关闭了二级缓存的刷新即不是默认状态,mapper映射文件中对应的 statement添加 flushCache=“false”
(更新进行 commit操作,没有刷新缓存;第二次查找,缓存命中率为0.5,查找的是未更新之前的数据)
运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值