一般提到 MyBatis 缓存都是指二级缓存,一级缓存(本地缓存)默认会启用,并且不能控制,因此很少会提到。
新建测试类:
@Test
public void testL1Cache() {
SqlSession sqlSession = getSqlSession();
SysUser user1 = null;
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//查询id=1的用户
user1 = userMapper.selectById(1L);
//对当前获取的对象重新赋值
user1.setUserName("new name");
//再次查询id相同的用户
SysUser user2 = userMapper.selectById(1L);
//虽然没有更新数据库,但是这个用户名和user1重新赋值的名字相同
Assert.assertEquals("new name", user2.getUserName());
//user2和user1完全就是同一个实例
Assert.assertEquals(user1, user2);
} finally {
//关闭当前的sqlSession
sqlSession.close();
}
System.out.println("开启新的SqlSession");
//开始另外一个新的session
sqlSession = getSqlSession();
try {
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//查询id=1的用户
SysUser user2 = userMapper.selectById(1L);
//第二个session获取的用户名仍然是admin
Assert.assertNotEquals("new name", user2.getUserName());
//这里的user2和前一个session查询的结果是两个不同的实例
Assert.assertNotEquals(user1, user2);
//执行删除操作
userMapper.deleteUserById(1002L);
//获取user3
SysUser user3 = userMapper.selectById(1L);
//这里的user2和user3是两个不同的实例
Assert.assertNotEquals(user2, user3);
} finally {
//关闭sqlSession
sqlSession.close();
}
}
输出日志:
DEBUG [main] - ==> Preparing: select * from sys_user where id = ?
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <== Columns: id, user_name, user_password, user_info, create_time
TRACE [main] - <== Row: 1, admin, 12345, <<BLOB>>, 2019-01-06 10:35:24.0
DEBUG [main] - <== Total: 1
开启新的SqlSession
DEBUG [main] - ==> Preparing: select * from sys_user where id = ?
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <== Columns: id, user_name, user_password, user_info, create_time
TRACE [main] - <== Row: 1, admin, 12345, <<BLOB>>, 2019-01-06 10:35:24.0
DEBUG [main] - <== Total: 1
DEBUG [main] - ==> Preparing: {call delete_user_by_id(?)}
DEBUG [main] - ==> Parameters: 1002(Long)
DEBUG [main] - <== Updates: 1
DEBUG [main] - ==> Preparing: select * from sys_user where id = ?
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <== Columns: id, user_name, user_password, user_info, create_time
TRACE [main] - <== Row: 1, admin, 12345, <<BLOB>>, 2019-01-06 10:35:24.0
DEBUG [main] - <== Total: 1
在第一次执行 selectById 方法获取 SysUser 数据时,真正执行了数据库查询,得到了user1。第二次执行获取 user2 的时候,从日志可以看出,在”开启新的sqlSession“ 这行日志的上面,只有一次查询,也就是说第二次查询并没有执行数据库操作。从测试代码看,user1 重新设置了 userName 的值,之后没有进行数据库操作,后来获取的 user2 对象的 userName 值和重新设置后的值一样,user1 和 user2 是同一个对象。
MyBatis 的一级缓存存在于 SqlSession 的生命周期中,在同一个 sqlsession 中查询时,MyBtis 会把执行的方法和参数通过算法生成缓存的键值,将键值和查询结果存入一个 Map 中。如果同一个 SqlSession 中执行的方法和参数完全一致,那么会生成相同的键值,当 Map 缓存对象中已经存在该键值时,则返回缓存中的对象。
在第二个 SqlSession 中,又重新查询了 user2,user2 是一个新的实例,和user1没有关系,因为一级缓存是和 SqlSession 绑定的,只存在于 SqlSession 的生命周期中。
接下来执行了一个 deleteById 操作,然后使用相同的方法和参数获取了 user3 实例,从日志和结果上看,user3 和 user2 也是完全不同的两个对象。这是因为任何的 INSERT、UPDATE、DELETE 操作都会清空一级缓存。