You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从Room数据库获取MAX值时触发NullPointerException的解决方法

问题:从Room数据库获取指定列MAX值并返回Long类型时,添加第一个条目触发NullPointerException如何修复?

异常信息

java.lang.NullPointerException: Attempt to invoke virtual method 'long java.lang.Number.longValue()' on a null object reference

相关代码

ShoppingListEntity

@Entity(tableName = "shopping_lists")
data class ShoppingListEntity(
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0L,
    @ColumnInfo(name = "name")
    val name: String,
    @ColumnInfo(name = "number_of_items")
    val numberOfItems: Long,
    @ColumnInfo(name = "list_total")
    val listTotal: String,
    @ColumnInfo(name = "sort_order")
    val sortOrder: String
)

ShoppingListItemEntity

@Entity(
    tableName = "shopping_list_items",
    foreignKeys = [ForeignKey(
        entity = ShoppingListEntity::class,
        parentColumns = arrayOf("id"),
        childColumns = arrayOf("shopping_list_id"),
        onUpdate = ForeignKey.CASCADE,
        onDelete = ForeignKey.CASCADE
    )]
)
data class ShoppingListItemEntity(
    @PrimaryKey(autoGenerate = true)
    var id: Long = 0L,
    @ColumnInfo(name = "shopping_list_id")
    var shoppingListId: Long = 0L,
    
    ...

    @ColumnInfo(name = "custom_sort_index")
    val customSortIndex: Long
)

Dao

@Query("SELECT MAX(custom_sort_index) FROM shopping_list_items WHERE shopping_list_id = :listId")
suspend fun getMaxCustomSortIndex(listId: Long): Long

Repository

override suspend fun getMaxCustomSortIndex(listId: Long): Long {
    return withContext(Dispatchers.IO) {
        shoppingListItemDao.getMaxCustomSortIndex(listId)
    }
}

Use Case

class GetMaxCustomSortIndexInItemsTableUseCase@Inject constructor(
    private val shoppingListItemsRepository: ShoppingListItemsRepository
) {
    suspend operator fun invoke(listId: Long): Long {
        return shoppingListItemsRepository.getMaxCustomSortIndex(listId)
    }
}

ViewModel

suspend fun addShoppingListItemToDb() {
    if (!_shoppingListScreenState.value.isNameError && !_shoppingListScreenState.value.isCategoryError) {
        viewModelScope.launch {
            val listId = _shoppingListState.value!!.id

            // 此处抛出NullPointerException
            val maxCustomSortIndex = getMaxCustomSortIndexInItemsTableUseCase(listId)

            val item = _shoppingListItemState.value
            addShoppingListItemUseCase(item.copy(customSortIndex = maxCustomSortIndex + 1))
            _listItemsLoadingState.value = ListItemsState.Success(_shoppingListItemsState.value)

            setStateValue(IS_SCROLL_TO_LAST_ITEM_IN_LAZYCOLUMN_STR, true)
        }
    }
}
解决方案

问题核心是:当指定listId下无任何条目时,MAX(custom_sort_index)返回NULL,但Room尝试将NULL转换为非空Long类型时,会触发自动拆箱操作,从而抛出空指针异常。

提供两种修复方案:

方案一:SQL层直接处理空值

使用COALESCE函数,当MAX结果为NULL时返回默认值(比如0),这样Room可以直接将结果转为非空Long:

@Query("SELECT COALESCE(MAX(custom_sort_index), 0) FROM shopping_list_items WHERE shopping_list_id = :listId")
suspend fun getMaxCustomSortIndex(listId: Long): Long

此方案无需修改其他层代码,逻辑简洁高效。

方案二:Dao层返回可空类型,Repository处理空值

  1. 修改Dao返回类型为Long?,允许返回空值:
@Query("SELECT MAX(custom_sort_index) FROM shopping_list_items WHERE shopping_list_id = :listId")
suspend fun getMaxCustomSortIndex(listId: Long): Long?
  1. 在Repository中对空值做兜底处理,返回默认值:
override suspend fun getMaxCustomSortIndex(listId: Long): Long {
    return withContext(Dispatchers.IO) {
        shoppingListItemDao.getMaxCustomSortIndex(listId) ?: 0L
    }
}

此方案将空值处理逻辑放在业务层,适合需要灵活调整默认值的场景。

内容的提问来源于stack exchange,提问作者Raj Narayanan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 05:30:50