Fork me on GitHub

mybatis之脏数据产生与避免

一、脏数据产生

  二级缓存虽然能提高应用效率,减轻数据库服务器的压力 ,但是如果使用不当,很容易产脏数据,这些脏数据会在不知不觉中影响业务逻辑,影响应用的实效,所以我们 需要了解在MyBat 缓存中脏数据是如何产生的,也要掌握避免脏数据的技巧。MyBatis二级缓存是和命名空间绑定的 ,所以通常情况下每 Mapper 映射文件都拥有自己的二级缓存,不同 Mapper 二级缓存互不影 。在常见的数据库操作中, 多表联合查询非常常见,由于关系型数据库的设计 使得很多时候需要关联多个表才能获得想要的数据。在关联多表查询时肯定会将该查询放到某个命名空间下的映射文件中,这样一个多表的查询就会缓存在该命名空间的二级缓存中。涉及这些表的增、删、改操作通常不在 个映射文件中,它们的命名空间不同,当有数据变 时,多表查询的缓存未必会被清空,这种情况下就会产生脏数据。
  现在我们先来模拟一下脏数据是如何产生的。
1. 准备工作:
UserMapper.xml和RoleMapper.xml中分别设置二级缓存:
<mapper namespace="com.example.simple.mapper.UserMapper">
    <cache
            eviction="FIFO"
            flushInterval="6000"
            size="512"
            readOnly="false"/>
</mapper>
<mapper namespace="com.example.simple.mapper.RoleMapper">
    <cache
            eviction="FIFO"
            flushInterval="6000"
            size="512"
            readOnly="false"/>
</mapper>

SysRole.java和SysUser.java序列化实现:

public class SysUser implements Serializable {

    private static final long serialVersionUID = 6320941908222932112L ;
}
public class SysRole implements Serializable {

    private static final long serialVersionUID = 6320941908222932112L ;
}

2. 准备代码测试:

 

@Test
        public void testDirtyDate(){
            SqlSession sqlSession = getSqlSession();
            try {
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                SysUser user = userMapper.selectUserAndRoleById(1001L);
                Assert.assertEquals("普通用户",user.getRole().getRoleName());
                System.out.println("角色名:" + user.getRole().getRoleName());
            }finally {
                sqlSession.close();

            }

            System.out.println("开启一个新的session");
            sqlSession = getSqlSession();
            try{
                RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
                SysRole role = roleMapper.selectById(2L);
                role.setRoleName("脏数据");
                //提交修改
                sqlSession.commit();
            }finally {
                sqlSession.close();
            }

            System.out.println("开启第二个新的session");
            sqlSession = getSqlSession();
            try{
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                RoleMapper roleMapper = sqlSession.getMapper(RoleMapper.class);
                SysUser user = userMapper.selectUserAndRoleById(1001L);
                SysRole role = roleMapper.selectById(2l);
                Assert.assertEquals("普通用户" , user.getRole().getRoleName());
                Assert.assertEquals("脏数据" , role.getRoleName());;
                System.out.println("角色名:" + user.getRole().getRoleName());
                //还原数据
                role.setRoleName("普通用户");
                roleMapper.updateById(role);
                sqlSession.commit();
            }finally {
                sqlSession.close();
            }


        }

测试后结果:

DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.UserMapper]: 0.0
DEBUG [main] - ==> Preparing: select u.id, u.user_name userName, u.user_password userPassword, u.user_email userEmail , u.user_info userInfo , u.head_img headImg, u.create_time createTime , r.id "role.id", r.role_name "role.roleName" , r.enabled "role.enabled" , r.create_by "role.createBy", r.create_time "role.createTime" from sys_user u inner join sys_user_role ur on u.id = ur.user_id inner join sys_role r on ur.role_id = r.id where u.id = ?
DEBUG [main] - ==> Parameters: 1001(Long)
TRACE [main] - <== Columns: id, userName, userPassword, userEmail, userInfo, headImg, createTime, role.id, role.roleName, role.enabled, role.createBy, role.createTime
TRACE [main] - <== Row: 1001, test, 123456, test@mybatis.com, <<BLOB>>, <<BLOB>>, 2020-12-01 20:05:01, 2, 普通用户, 1, 1, 2020-12-01 20:05:04
DEBUG [main] - <== Total: 1
角色名:普通用户
开启一个新的session
DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.RoleMapper]: 0.0
DEBUG [main] - ==> Preparing: select id, role_name,enabled,create_by,create_time from sys_role where id = ?
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <== Columns: id, role_name, enabled, create_by, create_time
TRACE [main] - <== Row: 2, 普通用户, 1, 1, 2020-12-01 20:05:04
DEBUG [main] - <== Total: 1
开启第二个新的session
DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.UserMapper]: 0.5
DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.RoleMapper]: 0.5
角色名:普通用户
DEBUG [main] - ==> Preparing: update sys_role set enabled = ? where id = ?
DEBUG [main] - ==> Parameters: 1(Integer), 2(Long)
DEBUG [main] - <== Updates: 1

测试的思路和脏数据产生分析是:

第一个sqlSession负责先关联用户表和角色表将user_id = 1001L的用户信息和角色信息查询出来;

第二个sqlSession负责将上一个sqlSession中查询到的角色信息对应的role_id进行再查询,并对role_name进行赋值,使得role_name被赋予新值;

第三个sqlSession分别通过SysUser类和SysRole类获取角色name,发现两者取出来的值并不一样,这时就出现了脏数据,这时因为UserMapper.xml和RoleMapper.xml中分别设置的二级缓存互不影响,这时就出现了同一角色对应两个name。

二、脏数据避免

 脏数据产生原因是二级缓存在不同的mapper.xml中,因此出现了缓存内容不一样,内容显示不一致的原因,这时解决方案如下:

<mapper namespace="com.example.simple.mapper.UserMapper">
   <cache-ref namespace="com.example.simple.mapper.RoleMapper"/>
    <!-- <cache
            eviction="FIFO"
            flushInterval="6000"
            size="512"
            readOnly="false"/>-->
</mapper>

这时测试结果是:

DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.RoleMapper]: 0.0
DEBUG [main] - ==> Preparing: select u.id, u.user_name userName, u.user_password userPassword, u.user_email userEmail , u.user_info userInfo , u.head_img headImg, u.create_time createTime , r.id "role.id", r.role_name "role.roleName" , r.enabled "role.enabled" , r.create_by "role.createBy", r.create_time "role.createTime" from sys_user u inner join sys_user_role ur on u.id = ur.user_id inner join sys_role r on ur.role_id = r.id where u.id = ?
DEBUG [main] - ==> Parameters: 1001(Long)
TRACE [main] - <== Columns: id, userName, userPassword, userEmail, userInfo, headImg, createTime, role.id, role.roleName, role.enabled, role.createBy, role.createTime
TRACE [main] - <== Row: 1001, test, 123456, test@mybatis.com, <<BLOB>>, <<BLOB>>, 2020-12-01 20:05:01, 2, 普通用户, 1, 1, 2020-12-01 20:05:04
DEBUG [main] - <== Total: 1
角色名:普通用户
开启一个新的session
DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.RoleMapper]: 0.0
DEBUG [main] - ==> Preparing: select id, role_name,enabled,create_by,create_time from sys_role where id = ?
DEBUG [main] - ==> Parameters: 2(Long)
TRACE [main] - <== Columns: id, role_name, enabled, create_by, create_time
TRACE [main] - <== Row: 2, 普通用户, 1, 1, 2020-12-01 20:05:04
DEBUG [main] - <== Total: 1
开启第二个新的session
DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.RoleMapper]: 0.3333333333333333
DEBUG [main] - Cache Hit Ratio [com.example.simple.mapper.RoleMapper]: 0.5
角色名:普通用户
DEBUG [main] - ==> Preparing: update sys_role set enabled = ? where id = ?
DEBUG [main] - ==> Parameters: 1(Integer), 2(Long)
DEBUG [main] - <== Updates: 1

这样一来,二级缓存就存在于两个关联表中,因此就实现了脏数据的避免。

posted @ 2020-12-30 11:10  叶语婷  阅读(817)  评论(0编辑  收藏  举报