Room Upsert成功更新却返回-1的问题排查求助
Room Upsert成功更新却返回-1的问题解析
问题描述
我之前了解到Room的Upsert操作出错时通常返回-1,但最近在项目中发现,即使更新操作成功执行(通过App Inspection确认数据库条目已被修改,比如性别从female改为male),Upsert仍然返回-1,导致错误提示被触发。当前项目使用Room版本为2.6.1,想知道哪里出现了认知偏差?
相关代码
Room配置
@Entity(tableName = "birth_defect_beneficiary_details") data class BirthDefectBabyDetails( @PrimaryKey(autoGenerate = true) val id: Int? = null, val dateOfBirth: Long, val sex: String, val motherName: String, val fatherName: String, val address: String, val contactNumber: String, val condition: String, val patientContacted: Boolean ) @Dao interface BDBabyDetailsDao { @Query("SELECT * FROM birth_defect_beneficiary_details ORDER BY dateOfBirth DESC") fun getDetailsOfAllBabies(): Flow<List<BirthDefectBabyDetails>> @Upsert suspend fun upsertEntry(entry: BirthDefectBabyDetails): Long @Delete suspend fun deleteEntry(entry: BirthDefectBabyDetails): Int @Query("DELETE FROM birth_defect_beneficiary_details") suspend fun deleteAllEntries() } @Database( entities = [BirthDefectBabyDetails::class], version = 1, exportSchema = false ) abstract class BDBabiesDatabase : RoomDatabase() { abstract val babyDao: BDBabyDetailsDao } interface BabyDetailsEntryRepository { fun getAllBabiesStream(): Flow<List<BirthDefectBabyDetails>> suspend fun upsertEntry(entry: BirthDefectBabyDetails): Long suspend fun deleteEntry(entry: BirthDefectBabyDetails): Int suspend fun deleteAllEntries() } class BDBabyDetailsLocalRepository @Inject constructor( private val babyDao: BDBabyDetailsDao ) : BabyDetailsEntryRepository { override fun getAllBabiesStream(): Flow<List<BirthDefectBabyDetails>> { return babyDao.getDetailsOfAllBabies() } override suspend fun upsertEntry(entry: BirthDefectBabyDetails): Long { return babyDao.upsertEntry(entry = entry) } override suspend fun deleteEntry(entry: BirthDefectBabyDetails): Int { return babyDao.deleteEntry(entry = entry) } override suspend fun deleteAllEntries() { babyDao.deleteAllEntries() } }
ViewModel中执行Upsert的代码
private fun upsertEntry() { // some codes... viewModelScope.launch(IO) { try { val result = babyRepository.upsertEntry(newEntry) Log.d("DPDC", "$result") if (result != -1L) { triggerEvent(VMEvents.EntryAdded("B/O ${newEntry.motherName}")) } else { triggerEvent(VMEvents.ShowError("Failed to add/update entry")) } } catch (e: Exception) { triggerEvent(VMEvents.ShowError("Error occurred: ${e.message}")) } } // some more codes... }
newEntry声明为val newEntry: BirthDefectBabyDetails
问题原因与解决方案
核心原因
你对Room @Upsert的返回值逻辑理解有误:
- 当执行插入操作时,Room返回新插入条目的自增主键ID
- 当执行更新操作时,如果实体使用
autoGenerate = true的自增主键,Room固定返回**-1**,这是正常设计,并非操作失败
Room的Upsert操作只有在真正发生异常(比如约束冲突、数据库连接错误)时才会抛出Exception,返回-1仅仅代表本次是更新动作,而非插入。
解决方案
修改ViewModel中的判断逻辑,去掉基于返回值的错误判断,只要没有抛出异常就判定操作成功:
private fun upsertEntry() { // some codes... viewModelScope.launch(IO) { try { babyRepository.upsertEntry(newEntry) triggerEvent(VMEvents.EntryAdded("B/O ${newEntry.motherName}")) } catch (e: Exception) { triggerEvent(VMEvents.ShowError("Error occurred: ${e.message}")) } } // some more codes... }
额外说明
如果需要区分插入和更新动作,可以通过实体的主键是否为null来判断:
- 若
newEntry.id == null,执行的是插入操作,返回新生成的ID - 若
newEntry.id != null,执行的是更新操作,返回-1
这是Room 2.5及以上版本针对自增主键的@Upsert注解的标准行为。
内容的提问来源于stack exchange,提问作者TheSelfTaughtNoob
相关产品推荐
相关产品推荐

