• MyBatis 缓存配置之一级缓存


    什么是一级缓存

    一般提到MyBatis缓存的时候,都是指二级缓存。一级缓存 (也叫本地缓存)默认会启用,并且不能控制,因此很少会提到。

    MyBatis 的一级缓存机制

    MyBatis 的一级缓存存在于 SqlSession 的生命周期中,在同一个 SqlSession 中查询时,MyBatis 会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存放如一个 Map 对象中。如果同一个 SqlSession 中执行的方法和参数完全一致,那么通过算法会生成相同的键值,当 Map 缓存对象中已经存在该键值时,则会返回缓存中的对象。

    一级缓存

    例1:一级缓存的效果演示

    测试代码

    /**

     * 一级缓存

     */

    @Test

    public void testFirstLevelCache() {

        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

     

        User user1 = mapper.selectUserByPrimaryKey(2L);

        System.out.println(user1);

     

        User user2 = mapper.selectUserByPrimaryKey(2L);

        System.out.println(user2);

       

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

    }

    运行结果

    DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ?

    DEBUG [main] - ==> Parameters: 2(Long)

    TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time

    TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52

    DEBUG [main] - <==      Total: 1

    user1:User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

    user2:User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

     

    Process finished with exit code 0

    从上面的测试代码中可以看出,我对id为2的User对象进行了二次查询。第一次执行 selectUserByPrimaryKey 方法获取 User 数据时,真正执行了数据库查询,得到了 user1 的结果。第二次执行获取 user2 的时候,从日志可以看到,只有一次查询,也就是说第二次查询并没有执行数据库操作。

    例2:使用一级缓存需要注意点

    测试代码

    /**

     * 一级缓存注意点

     */

    @Test

    public void testL1Cache() {

        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

     

        User user1 = mapper.selectUserByPrimaryKey(2L);

        System.out.println(user1);

        user1.setName("xd");

        User user2 = mapper.selectUserByPrimaryKey(2L);

        System.out.println(user2);

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

     

        sqlSession.close();

    }

    运行结果

    DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ?

    DEBUG [main] - ==> Parameters: 2(Long)

    TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time

    TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52

    DEBUG [main] - <==      Total: 1

    User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

    User(id=2, name=xd, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

    true

     

    Process finished with exit code 0

    从测试代码来看,获取 user1 后重新设置了 name 的值,之后没有进行任何更新数据库的操作。在获取 user2 对象后,发现 user2 对象的 name 值竟然和 user1 重新设置后的值一样。在往下可以发现,原来 user1 和 user2 竟然是同一个对象,之所以这样就是因为 MyBatis 的一级缓存。

    在使用 MyBatis 的过程中,要避免在使用如上代码中的 user2 时出现的错误。我们可能以为获取的 user2 应该是数据库中的数据,却不知道 user1 的一个重新赋值会影响到 user2。

    如果不想让 selectUserByPrimaryKey 方法使用一级缓存,可以做如下修改。

    <select id="selectUserByPrimaryKey" resultMap="BaseResultMap" parameterType="long" flushCache="true">

        select

          <include refid="base_column"/>

        from

          `db_user`

        where

          id = #{key}

    </select>

    该修改在原来方法的 <select> 标签中添加 flushCache="true" 属性,当这个属性配置为 true 后,会在查询数据前清空当前一级缓存。因此该方法每次查询都会从数据库中获取数据,这时 user1 和 user2 就是两个不同的对象,可以避免上面的问题。但是由于这个方法清空了一级缓存,会影响当前 SqlSession 中所有缓存的查询,因此在需要反复查询获取只读数据的情况下,会增加数据库的查询次数,所以要避免这么使用。

    一级缓存失效

    例1:不是同一个SqlSession。

    测试代码

    /**

     * 一级缓存失效:不是同一个SqlSession

     */

    @Test

    public void testCacheMissForOne() {

        SqlSession sqlSession1 = MyBatisUtils.getSqlSession();

        SqlSession sqlSession2 = MyBatisUtils.getSqlSession();

     

        UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);

        UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);

     

        User user1 = mapper1.selectUserByPrimaryKey(2L);

        System.out.println(user1);

     

        User user2 = mapper2.selectUserByPrimaryKey(2L);

        System.out.println(user2);

     

        sqlSession1.close();

    }

    运行结果

    DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ?

    DEBUG [main] - ==> Parameters: 2(Long)

    TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time

    TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52

    DEBUG [main] - <==      Total: 1

    User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

    DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ?

    DEBUG [main] - ==> Parameters: 2(Long)

    TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time

    TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52

    DEBUG [main] - <==      Total: 1

    User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

     

    Process finished with exit code 0

    从上面的测试代码可以看出,两次查询的 mapper 是从两个不同的 SqlSession 获取的,而 MyBatis 的一级缓存是依赖 SqlSession 的生命周期的,所以两个不同的 SqlSession 的一级缓存是不同的。

    例2:SqlSession 相同,查询条件不同。

    测试代码

    /**

     * 一级缓存失效:SqlSession相同,查询条件不同。

     */

    @Test

    public void testCacheMissForTwo() {

        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

     

        User user1 = mapper.selectUserByPrimaryKey(2L);

        System.out.println(user1);

        User user2 = mapper.selectUserByPrimaryKey(3L);

        System.out.println(user2);

     

        sqlSession.close();

    }

    运行结果

    DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ?

    DEBUG [main] - ==> Parameters: 2(Long)

    TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time

    TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52

    DEBUG [main] - <==      Total: 1

    User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

    DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ?

    DEBUG [main] - ==> Parameters: 3(Long)

    TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time

    TRACE [main] - <==        Row: 3, batch1, a1, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52

    DEBUG [main] - <==      Total: 1

    User(id=3, name=batch1, password=a1, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

     

    Process finished with exit code 0

    从上面测试代码可以看出,二次查询的条件是不一样的,也就是说第二次查询的数据在一级缓存中是不存在的,所以两次查询操作都是从数据库获取数据。

    例3:SqlSession 相同,两次查询之间执行了增、删、改操作。

    测试代码

    /**

     * 一级缓存失效:SqlSession相同,两次查询之间进行了增、删、改操作。

     */

    @Test

    public void testCacheMissForThree() {

        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

     

        User user1 = mapper.selectUserByPrimaryKey(2L);

        System.out.println(user1);

     

        User user = new User();

        user.setName("hotchXX");

        user.setPassword("xxx");

        user.setPhone("xxx");

        user.setEmail("123@123.com");

        user.setInfo("xxx");

        user.setImg(new byte[]{'a','v','d'});

        user.setCreateTime(new Date());

        mapper.insertUser(user);

     

        User user2 = mapper.selectUserByPrimaryKey(2L);

        System.out.println(user2);

     

        sqlSession.commit();

        sqlSession.close();

    }

    运行结果

    DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ?

    DEBUG [main] - ==> Parameters: 2(Long)

    TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time

    TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52

    DEBUG [main] - <==      Total: 1

    User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

    DEBUG [main] - ==>  Preparing: insert into `db_user`(user_name, user_password, user_phone, user_email, user_info, user_img, create_time) values (?, ?, ?, ?, ?, ?, ?)

    DEBUG [main] - ==> Parameters: hotchzz(String), zzz(String), zzz(String), 123@123.com(String), zzz(String), [B@4facf68f(byte[]), 2018-08-09 15:25:35.739(Timestamp)

    DEBUG [main] - <==    Updates: 1

    DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ?

    DEBUG [main] - ==> Parameters: 2(Long)

    TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time

    TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52

    DEBUG [main] - <==      Total: 1

    User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

     

    Process finished with exit code 0

    从上面的测试代码可以看出在两次查询操作之间进行了增加操作,所以即使两次查询都是查询id为2的对象,但是还是进行了2此数据库查询。

    例4:SqlSession 相同,手动清除一级缓存。

    测试代码

    /**

     * 一级缓存失效:SqlSession相同,手动清除了一级缓存。

     */

    @Test

    public void testCacheMissForFour() {

        SqlSession sqlSession = MyBatisUtils.getSqlSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);

     

        User user1 = mapper.selectUserByPrimaryKey(2L);

        System.out.println(user1);

     

        sqlSession.clearCache();// 清除一级缓存

     

        User user2 = mapper.selectUserByPrimaryKey(2L);

        System.out.println(user2);

     

        sqlSession.close();

    }

    运行结果

    DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ?

    DEBUG [main] - ==> Parameters: 2(Long)

    TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time

    TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52

    DEBUG [main] - <==      Total: 1

    User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

    DEBUG [main] - ==>  Preparing: select id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time from `db_user` where id = ?

    DEBUG [main] - ==> Parameters: 2(Long)

    TRACE [main] - <==    Columns: id, user_name, user_password, user_phone, user_email, user_info, user_img, create_time

    TRACE [main] - <==        Row: 2, batch0, a0, 222, 123@123.com, <<BLOB>>, <<BLOB>>, 2018-08-03 11:17:52

    DEBUG [main] - <==      Total: 1

    User(id=2, name=batch0, password=a0, phone=222, email=123@123.com, info=222, img=[97, 118, 100], createTime=Fri Aug 03 11:17:52 CST 2018)

     

    Process finished with exit code 0

    从上面的测试代码可以看出,在两次查询操作之前执行了清除一级缓存操作sqlSession.clearCache();// 清除一级缓存 ,所以第二次查询还是从数据库获取数据。

    内容来自:https://www.jianshu.com/p/6d6b685ed4c3

  • 相关阅读:
    盘一盘 synchronized (二)—— 偏向锁批量重偏向与批量撤销
    盘一盘 synchronized (一)—— 从打印Java对象头说起
    盘一盘 Thread源码
    盘一盘 System.out.println()
    Java中的单例模式
    Spring AOP SpringBoot集成
    第16周作业
    第15周作业
    迟到的第14周作业
    第13周作业集
  • 原文地址:https://www.cnblogs.com/ylsx/p/13295778.html
Copyright © 2020-2023  润新知