美文网首页android开发学习
Room数据库和Cache缓存联合使用出现的小问题

Room数据库和Cache缓存联合使用出现的小问题

作者: 奔跑的佩恩 | 来源:发表于2021-04-18 22:11 被阅读0次

    前言

    在上节我们介绍了Room数据库的使用,大家感兴趣的话,可以参考以下文章:
    kotlin版Room数据库 — 基本使用
    那么在数据库的使用过程中,我们经常想有一些加快效率的方法。二级缓存的概念便是这么来的: 先缓存中找数据,缓存中能找到的话就直接使用,缓存中找不到的话就取数据库中找。大致就是这么个流程。但是我们在用Room数据库时,是有结合自己封装的一个缓存帮助类Cache来使用的,然后在业务流程中出现了取值不正确的问题,下面就来分析下这个小问题吧。

    今天涉及到的内容有:

    1. 出现的问题
    2. 节点介绍
    3. 缓存原理介绍
    4. 排查问题
    5. 修正逻辑

    一. 出现的问题

    今天我在数据操作上大致是这么个流程:
    存数据 ---> 修改数据 ---> 取数据 ---> 其他业务逻辑
    问题就出在取数据环节,取的不是我存的数据,然后就导致我后面的其他业务逻辑无法展开。那么,这里肯定是存数据修改数据或者取数据的某个环节中出现了问题。下面就来具体看看这几个节点。

    二. 节点介绍

    2.1 存数据

    下面给出存数据环节的示例代码:

            var userDaoHelper: UserDaoHelper = UserDaoHelper()
    
            LogUtil.i("=======存入初始数据========")
            var user: User = User()
            user.name = "张三"
            user.age = 12
            user.sex = "男"
            AppDataBase.instance().userDao().insert(user)
    
            //遍历结果
            var list: MutableList<User> = userDaoHelper.getAll()
            list.forEach {
                LogUtil.i("=====遍历所有数据===name=${it.toString()}")
            }
    

    这里是使用Room数据库的insert方法进行存储的。以上代码写了数据存储和遍历查询,代码很简单,没什么问题,那么接着让我们去看下一个节点。

    2.2 修改数据

    修改数据的示例代码如下:

            LogUtil.i("=======查询叫张三的对象========")
            var tempUser:User?=getUserByName("张三")
            if(tempUser==null){
                LogUtil.i("========user=null")
            }else{
                LogUtil.i("========user=${user.toString()}")
            }
    
            LogUtil.i("=======更新操作========")
            //更新
            tempUser!!.hobby="大神"
            userDaoHelper.update(tempUser!!)
            LogUtil.i("========更新tempUser=${tempUser!!.toString()}")
    

    这里先是调用getUserByName("张三")获取存储的User对象,然后更新完tempUser后通过userDaoHelper.update(tempUser!!)方法存到Room数据中。这里,我们继续往下看,后面再细看getUserByName("张三")方法。

    2.3 查询环节

    最后一步是查询已存储的数据,示例代码如下:

            LogUtil.i("=======更新后查询结果========")
            //再取值
            var lastUser:User?=getUserByName("张三")
            if(lastUser==null){
                LogUtil.i("========user=null")
            }else{
                LogUtil.i("========user=${lastUser.toString()}")
            }
    
            LogUtil.i("=======更新后查数据库结果========")
            var listp: MutableList<User> = userDaoHelper.getAll()
            listp.forEach {
                if("张三".equals(it.name)){
                    LogUtil.i("========user=${it.toString()}")
                }
            }
    

    这个地方用了两种查询方式,一种是通过getUserByName("张三")方法查询出User,另一种是通过Room数据库相关方法封装出的方法userDaoHelper.getAll()来获取数据。
    执行完毕后,我们可以来看看log:

    =======存入初始数据========
    =====遍历所有数据===name=User(id=1, name=张三, age=12, hobby=null, sex=null)
    =======查询叫张三的对象========
    ========取数据库======
    ========user=User(id=0, name=张三, age=12, hobby=null, sex=男)
    =======更新操作========
    ========更新tempUser=User(id=1, name=张三, age=12, hobby=大神, sex=null)
    =======更新后查询结果========
    ========取cache======
    ========user=User(id=1, name=张三, age=12, hobby=null, sex=null)
    =======更新后查数据库结果========
    ========user=User(id=1, name=张三, age=12, hobby=大神, sex=null)
    

    以上log打印表示的执行逻辑大概是(主要看User对象中的hobby属性):
    存入user时,hobby=null
    然后通过getUserByName("张三")取出user时,hobby=null
    然后更新user时,hobby=大神
    最后getUserByName("张三")查询出的user中,hobby=null
    而数据库查询出user时, hobby=大神
    这里可以看出存更新后的user是成功的,因为数据库查出来userhobby=大神,那么问题就出在getUserByName("张三")方法上了。

    三. 缓存原理介绍

    3.1 getUserByName(name:String) 方法

    下面就来看看getUserByName("张三")方法示例代码:

        private fun getUserByName(name:String): User? {
            var user:User?=null
    
            var userDaoHelper: UserDaoHelper = UserDaoHelper()
            var obj: Any? = Cache.getInstance().getObject(name, User::class.java)
            if (obj != null) {
                LogUtil.i("========取cache======")
                user= obj as User
            }else{
                //遍历结果
                var list: MutableList<User> = userDaoHelper.getAll()
    
                var index:Int=0
                while(true){
                    var temp:User=list[index]
                    if(name.equals(temp.name)){
                        user=temp
                        LogUtil.i("========取数据库======")
                        //存储对象
                        Cache.getInstance().putObject(name, user)
                        break
                    }
                    index++
                }
            }
            return user
        }
    

    ok,可以看到取数据的逻辑是:先从缓存中取数据,若缓存中没有,则从数据库取,并在数据库查出数据的同时,将数据存到缓存中
    接着让我们来了解下Cache缓存逻辑。

    3.2 Cache原理

    Cache是先在内存中取数据,存储利用Lru算法,如果内存中没有,则在手机硬盘中获取。而手机硬盘采用的是文件方式存储,即硬存储。

    四.排查问题

    然后再看整个逻辑:
    先存数据 ---> 更新Room 数据库数据 ---> 缓存中找数据,没有则去Room数据库查,查到的同时将数据设置到缓存中,若缓存中有则直接返回数据。
    于是这里出现了一个问题:
    存时ok,通过缓存查是错误数据,但是通过数据库查却是正确数据。
    那么问题便出在缓存找数据上了。Cache的强悍存储之处在于,它最终采用的存储方式是文件存储来保持数据的持久性。那么如果上一次数据存储到内存之后,若之后进行了其他数据处理,却没去更新缓存,则缓存中取到的可能还是上一次的数据,而不是最新数据。

    五.修正逻辑

    经过以上分析,当我们在使用数据库时有结合缓存使用的话,我们在操作数据库的增,删,改时,记得要去更新缓存,然后在取值时要用以下逻辑

    1. 若缓存中有,直接返回
    2. 若缓存中无,去查数据库
    3. 若数据库中有,返回数据的同时,将数据存到缓存中,方便下次使用时取出
    4. 若数据库中无,则直接返回空
    

    ok, 今天关于数据库与缓存联合使用的知识就讲到这里了,谢谢大家。

    相关文章

      网友评论

        本文标题:Room数据库和Cache缓存联合使用出现的小问题

        本文链接:https://www.haomeiwen.com/subject/bojvlltx.html