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, [email protected], <>, <>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
user1:User(id=2, name=batch0, password=a0, phone=222, [email protected], 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 protected], 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, [email protected], <>, <>, 2018-08-03 11:17:52
DEBUG [main] - <==      Total: 1
User(id=2, name=batch0, password=a0, phone=222, [email protected], 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 protected], 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 方法使用一级缓存,可以做如下修改。


该修改在原来方法的