Room查询返回空结果(含Maybe<List>场景)致应用崩溃求助
嘿,我来帮你搞定这个崩溃问题!先看你给出的崩溃日志,核心问题其实很明确:android.arch.persistence.room.EmptyResultSetException: Query returned empty result set: SELECT * FROM classes WHERE _id IN() ORDER BY date DESC——这里的IN()括号里完全是空的,没有任何ID值,Room执行这种非法SQL的时候就会抛出异常,而且这个异常是在你后退导航时触发的第二个查询里爆发的,虽然你觉得第二个查询本身没问题,但问题出在你传给它的参数上。
问题根源
当你给Room的IN查询传入一个空的ID列表时,Room不会自动处理这种情况,直接把IN()拼到SQL语句里,这本身就是不符合SQL语法的,Room就会抛出EmptyResultSetException(不同版本可能表现略有不同,但核心都是空IN子句的问题)。而你后退时触发的这个查询,刚好传入了空的ID集合,所以就崩溃了。
具体解决方案
我给你几个实用的解决思路,你可以根据自己的业务需求选:
提前拦截空参数,避免执行无效查询
先找到调用这个查询的代码,在执行查询前先判断ID列表是否为空,如果是空的直接返回空列表就行,不用走Room查询。比如你的调用逻辑可以改成这样:// 假设这是你的Repository层代码 suspend fun getTargetClasses(classIds: List<Long>): List<ClassEntity> { return if (classIds.isEmpty()) { emptyList() // 直接返回空,不执行查询 } else { classDao.getClassesByIds(classIds) // 参数有效时才查询 } }对应的DAO方法可以保持原来的样子:
@Query("SELECT * FROM classes WHERE _id IN(:classIds) ORDER BY date DESC") suspend fun getClassesByIds(classIds: List<Long>): List<ClassEntity>修改DAO查询语句,兼容空IN的情况
如果你不想在调用层做判断,也可以直接修改DAO的查询语句,用SQL逻辑处理空参数的场景。比如根据你的业务需求,空ID列表时要么返回所有数据,要么返回空:- 空列表时返回空结果:
当@Query("SELECT * FROM classes WHERE 1=0 OR _id IN(:classIds) ORDER BY date DESC") suspend fun getClassesByIds(classIds: List<Long>): List<ClassEntity>classIds为空时,1=0永远为假,所以返回空列表,不会触发异常。 - 空列表时返回所有数据:
调用的时候传入@Query("SELECT * FROM classes WHERE (:isListEmpty = 1 OR _id IN(:classIds)) ORDER BY date DESC") suspend fun getClassesByIds(classIds: List<Long>, isListEmpty: Boolean): List<ClassEntity>isListEmpty = classIds.isEmpty(),这样空列表时就会返回所有classes数据。
- 空列表时返回空结果:
检查后退导航时的参数传递逻辑
崩溃发生在后退的时候,说明后退时触发这个查询的ID列表是空的。你得排查下前一个页面查询返回空结果后,是怎么把参数传递给这个查询的——是不是前一个页面没数据,导致传递的ID集合为空?要确保在后退触发查询时,要么传递有效的ID列表,要么已经提前处理了空列表的情况。
小提醒
Room对空集合作为IN参数的处理比较严格,不会像有些ORM框架那样自动转换成WHERE 1=0,所以一定要手动处理空参数的场景,避免这种莫名其妙的崩溃。
内容的提问来源于stack exchange,提问作者Tuesday Four AM

