MyBatis的一, 二级缓存

默认MyBatis开启了一级缓存, 在同一个sqlSession中, 如果命中同一个查询, MyBatis是不会真的查询的, 而只是拿结果对象糊弄你一下, 甚至如果这个对象被改了, 它也不管:

    @Test
    public void testCache() {
        SqlSession sqlSession = getSqlSession();
        SysUser user1 = null;
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            user1 = userMapper.selectById(1L);
            user1.setUserName("New Name");
            SysUser resultUser = userMapper.selectById(1L);
            System.out.println(user1);
            System.out.println(resultUser);
            Assert.assertEquals("New Name", resultUser.getUserName());
            Assert.assertEquals(user1, resultUser);
            // 这里的输出很惊人啊, user1改过名字, 理论上resultUser的userName不能跟user1一样啊.
            // 说明mybatis并未去真的查询, 而直接把结果拿来用了, 甚至对象都是直接拿来用了.可怕.
            // 这就是缓存的力量?

        } finally {
            sqlSession.close();
        }

        System.out.println("开启新的session");

        sqlSession = getSqlSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            user1 = userMapper.selectById(1L);
            System.out.println("user1: " + user1);
            user1.setUserName("New Name");
            SysUser resultUser2 = userMapper.selectById(1L);
            System.out.println(user1);
            System.out.println(resultUser2);
            Assert.assertEquals("New Name", resultUser2.getUserName());
            Assert.assertEquals(user1, resultUser2);
            // 这里经过重新开启一个session之后, 又重新搜索了.
        } finally {
            sqlSession.close();
        }
    }

这里对象名很乱, 不好意思.

我们发现, 在session没关闭的情况下, 哪怕你把对象用setUserName方法改了, 再查询的时候, 它仍返回一个原来的结果对象, 变成了你查询的结果成了脏数据, 这是MyBatis的一级缓存的威力.

OK, 开启二级缓存.

首先拿SysRole对象做测试, 修改SysRole:

public class SysRole implements Serializable {

    private static final long serialVersionUID = 3423434546568423L;

... ...

SysRoleMapper的xml文件增加cache, 二级缓存只能针对namespace有效, 故这个namespace下的所有select都进入了缓存范围

<mapper namespace="marc.mybatis.lesson1.mapper.SysRoleMapper">
    <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="false" />
... ...

Mapper接口加个注解:

@CacheNamespaceRef(SysRoleMapper.class)
public interface SysRoleMapper {
... ...

测试类如下, 注意, 这个测试类是拿SysRole而不是SysUser.

    @Test
    public void testLevel2Cache() {
        SqlSession sqlSession = getSqlSession();
        SysRole role1 = null;
        try {
            SysRoleMapper roleMapper = sqlSession.getMapper(SysRoleMapper.class);
            role1 = roleMapper.selectById(1L);
            role1.setRoleName("New Name");
            SysRole roleResult = roleMapper.selectById(1L);
            System.out.println(role1);
            System.out.println(roleResult);
            Assert.assertEquals("New Name", roleResult.getRoleName());
            Assert.assertEquals(role1, roleResult);
        } finally {
            sqlSession.close();
        }

        System.out.println("开启新的session");

        sqlSession = getSqlSession();
        try {
            SysRoleMapper roleMapper  = sqlSession.getMapper(SysRoleMapper.class);
            role1 = roleMapper.selectById(1L);
            System.out.println("user1: " + role1);
            //user1.setUserName("New Name");
            SysRole resultRole2 = roleMapper.selectById(1L);
            System.out.println(role1);
            System.out.println(resultRole2);
            Assert.assertEquals("New Name", resultRole2.getRoleName());
            Assert.assertNotEquals(role1, resultRole2);
        } finally {
            sqlSession.close();
        }
    }

输出如下:

DEBUG [main] - Cache Hit Ratio [marc.mybatis.lesson1.mapper.SysRoleMapper]: 0.0
DEBUG [main] - ==>  Preparing: select id,role_name roleName,enabled,create_by createBy,create_time createTime from sys_role where id=? 
DEBUG [main] - ==> Parameters: 1(Long)
TRACE [main] - <==    Columns: id, roleName, enabled, createBy, createTime
TRACE [main] - <==        Row: 1, 管理员, 1, 1, 2017-12-09 12:22:12.0
DEBUG [main] - <==      Total: 1
DEBUG [main] - Cache Hit Ratio [marc.mybatis.lesson1.mapper.SysRoleMapper]: 0.0
SysRole [id=1, roleName=New Name, enabled=disabled, createBy=1, createTime=Sat Dec 09 12:22:12 CST 2017, user=null, privilegeList=null]
SysRole [id=1, roleName=New Name, enabled=disabled, createBy=1, createTime=Sat Dec 09 12:22:12 CST 2017, user=null, privilegeList=null]
开启新的session
DEBUG [main] - Cache Hit Ratio [marc.mybatis.lesson1.mapper.SysRoleMapper]: 0.3333333333333333
user1: SysRole [id=1, roleName=New Name, enabled=disabled, createBy=1, createTime=Sat Dec 09 12:22:12 CST 2017, user=null, privilegeList=null]
DEBUG [main] - Cache Hit Ratio [marc.mybatis.lesson1.mapper.SysRoleMapper]: 0.5
SysRole [id=1, roleName=New Name, enabled=disabled, createBy=1, createTime=Sat Dec 09 12:22:12 CST 2017, user=null, privilegeList=null]
SysRole [id=1, roleName=New Name, enabled=disabled, createBy=1, createTime=Sat Dec 09 12:22:12 CST 2017, user=null, privilegeList=null]

注意, 开启新的session之后, 仍然没有去真的查询, 而是击中了cache, 所以, 命中率一开始1/3, 后来变成了1/2, 而且注意看roleName, 全都被修改过. so, 注意使用缓存时产生的脏数据, 处理好这一点, 会让你的数据库更有效率, 数据吞吐量更大.

想了想, 缓存的好处多半是用于处理搜索把, 比如你十月末在淘宝搜索羽绒服, 如果没有缓存, 数据库会疯掉......

原文地址:https://www.cnblogs.com/Montauk/p/9797382.html