mybatis学习四之一级缓存、二级缓存

一级、二级缓存介绍:

1、一级缓存
1.1 mybatis的一级缓存基于SqlSession级别,默认是开启且无法关闭(但是我们可以手动设置不使用缓存,useCahe,flushCahe参数)。在同一个sqlSession中执行多次一样的查询,可以发现第后续多次查询并没有去查询数据库,而是直接命中了第一次的缓存。一级缓存可以
减少数据库压力,但可能会查询到脏数据(第一次查询后数据被修改了)。但是mybatis官方
仍然会这样设计的原因是什么?因为真实开发中,几乎没人会使用同一个sqlSession连续执行多次相同的查询。。。
1.2 一级缓存清除策略:遇到upate、insert、delete

2、二级缓存
2.1 二级缓存基于namespace级别,二级缓存默认关闭,也不建议使用。多个sqlSession对同一个namespace下的sql查询,后续查询命中第一次的缓存。
2.2 二级缓存问题:a、脏数据问题 b、数据失效问题
**a、**二级缓存开启时关联查询时,可能会有脏数据问题。如查询Blog时关联查询了Author,mybatis会在BLOG的namespace下缓存blog查询结果,同时会缓存一份Author的副本。这时如果Author的数据更新了,mybatis只刷新Author的namespace下缓存数据,而不会刷新Blog下的author缓存数据。
**b、**二级缓存开启时,如果遇到缓存刷新,mybatis会被该namespace下的所有缓存清除。如namespace下有10条sql,现在update了第一条sql触发了二级缓存刷新动作,二级缓存清空了这10条sql的缓存,这显然是不合理的。
2.3 二级缓存清除策略:遇到upate、insert、delete

缓存验证:

数据源

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <typeHandlers>
        <typeHandler handler="com.study.mybatis.handler.ConverHandler"></typeHandler>
    </typeHandlers>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test"/>
                <property name="username" value="root"/>
                <property name="password" value="root123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/UserMapper.xml"/>
    </mappers>
</configuration>

@Mapper
public interface UserMapper extends BaseMapper<User> {

    User queryUser(User user);
    
    @Select("select * from user where id=#{id}")
    User findById(int id);
}

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

<!-- 指明当前xml对应的Mapper -->
<mapper namespace="com.study.mybatis.dao.UserMapper">
    <select id="queryUser" parameterType="com.study.mybatis.pojo.User" resultType="com.study.mybatis.pojo.User">
        select * from user where 1=1
        <if test="id !=null and id !=''">and id = #{id}</if>
        <if test="name !=null and name !=''">and name = #{name}</if>
        <if test="password !=null and password != ''">and password = #{password}</if>
    </select>
</mapper>

一级缓存验证demo

public class MybatisSourceDemo {

    public static void main(String[] args) throws IOException {
        User user = new User();
        user.setId("1");

        SqlSessionFactory sqlSessionFactory = getSqlSession();
        SqlSession session1 = sqlSessionFactory.openSession();

        try {
            UserMapper mapper1 = session1.getMapper(UserMapper.class);
            //第一次查询
            User user1 = mapper1.queryUser(user);
            System.out.println("user1="+user1);
           //第二次查询
            User user2 = mapper1.queryUser(user);
            System.out.println("user2="+user2);
           //第三次查询
            User user3 = mapper1.queryUser(user);
            System.out.println("user3="+user3);
        }catch (Exception e){
        }finally {
            session1.commit();
            session1.close();
        }
    }

    private static SqlSessionFactory getSqlSession() throws IOException {
        String resource = "com/study/mybatis/source/mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory =
                new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }
}

日志输出如下,可以看到同一个sqlSession执行多次相同查询,只执行了一次select,后续查询直接命中缓存。

14:23:54.627 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==>  Preparing: select * from user where 1=1 and id = ? 
14:23:54.646 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==> Parameters: 1...(String)
14:23:54.666 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - <==      Total: 1
user1=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)
user2=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)
user3=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)

手动配置“不使用”一级缓存,使用useCache=“false” flushCahe=“true”,这两个要一起使用才会生效。

 <select id="queryUser" parameterType="com.study.mybatis.pojo.User" resultType="com.study.mybatis.pojo.User" useCache="false" flushCache="true">
        select * from user where 1=1
        <if test="id !=null and id !=''">and id = #{id}</if>
        <if test="name !=null and name !=''">and name = #{name}</if>
        <if test="password !=null and password != ''">and password = #{password}</if>

    </select>

验证输出结果,发现一级缓存已失效。

14:24:44.956 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==>  Preparing: select * from user where 1=1 and id = ? 
14:24:44.979 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==> Parameters: 1...(String)
14:24:44.994 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - <==      Total: 1
user1=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)
14:24:44.995 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==>  Preparing: select * from user where 1=1 and id = ? 
14:24:44.995 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==> Parameters: 1...(String)
14:24:44.996 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - <==      Total: 1
user2=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)
14:24:44.996 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==>  Preparing: select * from user where 1=1 and id = ? 
14:24:44.996 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==> Parameters: 1...(String)
14:24:44.997 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - <==      Total: 1
user3=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)

二级缓存验证demo

未开启二级缓存日志如下,可以发现执行了三次查询

查询demo

public class MybatisSourceDemo {

    public static void main(String[] args) throws IOException {
        User user = new User();
        user.setId("1");

        SqlSessionFactory sqlSessionFactory = getSqlSession();//注意这里的sqlSession,一定要是同一个factory生成的
        SqlSession session1 = sqlSessionFactory.openSession();
        SqlSession session2= sqlSessionFactory.openSession();
        SqlSession session3 = sqlSessionFactory.openSession();

        try {
            UserMapper mapper1 = session1.getMapper(UserMapper.class);
            User user1 = mapper1.queryUser(user);
            System.out.println("user1="+user1);

//            User user2 = mapper1.queryUser(user);
//            System.out.println("user2="+user2);
//
//            User user3 = mapper1.queryUser(user);
//            System.out.println("user3="+user3);
        }catch (Exception e){
        }finally {
            session1.commit();//一定要先提交事务后,后面的查询才有缓存可用
            session1.close();
        }

        try {
            UserMapper mapper2 = session2.getMapper(UserMapper.class);
            User user2 = mapper2.queryUser(user);
            System.out.println("user2=" + user2);
        }catch (Exception e){
        }finally {
            session2.commit();
            session2.close();
        }

        try{
            UserMapper mapper3 = session3.getMapper(UserMapper.class);
            User user3 = mapper3.queryUser(user);
            System.out.println("user3="+user3);
        }catch (Exception e){
        }finally {
            session3.commit();
            session3.close();
        }
    }

    private static SqlSessionFactory getSqlSession() throws IOException {
        String resource = "com/study/mybatis/source/mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory =
                new SqlSessionFactoryBuilder().build(inputStream);
        return sqlSessionFactory;
    }

}

14:25:54.632 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==>  Preparing: select * from user where 1=1 and id = ? 
14:25:54.652 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==> Parameters: 1...(String)
14:25:54.668 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - <==      Total: 1
user1=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)
14:25:54.669 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5bd03f44]
14:25:54.669 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5bd03f44]
14:25:54.669 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 1540374340 to pool.
14:25:54.669 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
14:25:54.670 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Checked out connection 1540374340 from pool.
14:25:54.670 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5bd03f44]
14:25:54.670 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==>  Preparing: select * from user where 1=1 and id = ? 
14:25:54.670 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==> Parameters: 1...(String)
14:25:54.671 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - <==      Total: 1
user2=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)
14:25:54.671 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5bd03f44]
14:25:54.673 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5bd03f44]
14:25:54.673 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 1540374340 to pool.
14:25:54.673 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Opening JDBC Connection
14:25:54.673 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Checked out connection 1540374340 from pool.
14:25:54.673 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5bd03f44]
14:25:54.673 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==>  Preparing: select * from user where 1=1 and id = ? 
14:25:54.674 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==> Parameters: 1...(String)
14:25:54.674 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - <==      Total: 1
user3=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)

开启二级缓存
mybatis配置文件添加

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

mapper.xml中添加

<mapper namespace="com.study.mybatis.dao.UserMapper">

      <cache></cache>
      
      <select id="queryUser" parameterType="com.study.mybatis.pojo.User" resultType="com.study.mybatis.pojo.User">
          select * from user where 1=1
          <if test="id !=null and id !=''">and id = #{id}</if>
          <if test="name !=null and name !=''">and name = #{name}</if>
           <if test="password !=null and password != ''">and password = #{password}</if>
      </select>
    </mapper>

再次测试,日志如下,可以发现Cache Hit Ratio [com.study.mybatis.dao.UserMapper这句日志,表示查询命中了xxx namespace下的缓存,命中率为xxx。这句日志是二级缓存才有的日志。

14:31:07.437 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==>  Preparing: select * from user where 1=1 and id = ? 
14:31:07.457 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - ==> Parameters: 1...(String)
14:31:07.483 [main] DEBUG com.study.mybatis.dao.UserMapper.queryUser - <==      Total: 1
user1=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)
14:31:07.489 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c30a9b0]
14:31:07.496 [main] DEBUG org.apache.ibatis.transaction.jdbc.JdbcTransaction - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@5c30a9b0]
14:31:07.496 [main] DEBUG org.apache.ibatis.datasource.pooled.PooledDataSource - Returned connection 1546693040 to pool.
14:31:07.498 [main] DEBUG com.study.mybatis.dao.UserMapper - Cache Hit Ratio [com.study.mybatis.dao.UserMapper]: 0.5
user2=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)
14:31:07.499 [main] DEBUG com.study.mybatis.dao.UserMapper - Cache Hit Ratio [com.study.mybatis.dao.UserMapper]: 0.6666666666666666
user3=User(id=null, name=老王, password=12341234, remark=老王是各好邻居)

总结:
1、一级缓存是sqlSession级别的,默认开启且无法关闭,但是我们可以使用useCahe和flushCahe配置不使用一级缓存。
2、二级缓存是namespace级别的,默认开闭需要手动开启。如果要写demo测试二级缓存,要保证多个sqlsession为同一个工厂产出,同时要保证第一次的查询结果的缓存已提交事务。即使用二级缓存的的前提是它要先存在。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值