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

Room数据库重复条目问题:ID校验函数失效求助

问题描述

我编写了一个函数用于检查指定ID是否已存在于数据库中,但该函数未按预期工作,仍会插入重复条目。相关代码如下:

检查重复ID的函数

private fun CheckDuplicateId(id:String,donorDao:DonorDao):Boolean{
        var result=true
        lifecycleScope.launch {
            donorDao.fetchAllDonor().collect(){
                var Donorlist=ArrayList(it)
                for(item in Donorlist){
                    if (item.id==id){
                        result=false
                    }
                }
            }
        }
        return result
    }

插入条目的函数

private fun addRecord(donorDao:DonorDao){
        val Id:String=binding?.etDonorId?.text.toString()
        val bloodGr=binding?.etDonorBloodgroup?.text.toString()
        if(Id.isNotEmpty() && bloodGr.isNotEmpty() && CheckDuplicateId(Id,donorDao)){
            lifecycleScope.launch {
                donorDao.insert(DonorEntity(id = binding?.etDonorId?.text.toString(), bloodGroup = bloodGr))
                Toast.makeText(applicationContext, "Record saved", Toast.LENGTH_SHORT).show()
            }
        }else{
            Toast.makeText(this,"duplicate value",Toast.LENGTH_SHORT).show()
        }
    }

DonorEntity 定义

@Entity(tableName = "donor-table")
data class DonorEntity(
    @PrimaryKey(autoGenerate = true)
    var sr_no:Int=0,
    var id:String="",
    var bloodGroup:String="")

DonorDao 定义

@Dao
interface DonorDao {
    @Insert
    suspend fun insert(donorEntity:DonorEntity)
    @Update
    suspend fun update(donorEntity:DonorEntity)
    @Delete
    suspend fun delete(donorEntity:DonorEntity)
    @Query("SELECT*FROM `donor-table`")
    fun fetchAllDonor(): Flow<List<DonorEntity>>
    @Query("SELECT*FROM `donor-table` Where id=:id")
    fun fetchDonorById(id:Int): Flow<DonorEntity>
}

数据库定义

@Database(entities = [DonorEntity::class], version = 2)
abstract class DonorDatabase:RoomDatabase() {
    abstract fun donorDao():DonorDao

    companion object{
        @Volatile
        private var INSTANCE:DonorDatabase?=null
        fun getInstance(context: Context):DonorDatabase{
            synchronized(this){
                var instance=INSTANCE
                if (instance==null){
                    instance=Room.databaseBuilder(context.applicationContext,
                        DonorDatabase::class.java,"donor_database")
                        .fallbackToDestructiveMigration().build()
                }
                INSTANCE=instance
                return instance
            }
        }
    }

}

问题原因及修复方案

核心问题

CheckDuplicateId 函数中使用 lifecycleScope.launch 启动协程属于异步操作,函数会直接返回初始值 true,等协程内的数据库查询完成时,返回值早已确定,根本没起到检查重复的作用。此外还有两个次要问题:

  • fetchDonorById 的参数类型是 Int,但实体类中 id 字段是 String,类型不匹配导致该查询无法正常使用;
  • 遍历全表检查重复效率极低,应该直接用查询语句定位指定ID。

修复步骤

1. 修改 DonorDao,添加ID存在性查询

新增专门检查ID是否存在的挂起函数,同时修正原有查询的参数类型:

@Dao
interface DonorDao {
    // 保留原有方法
    @Insert
    suspend fun insert(donorEntity:DonorEntity)
    @Update
    suspend fun update(donorEntity:DonorEntity)
    @Delete
    suspend fun delete(donorEntity:DonorEntity)
    @Query("SELECT*FROM `donor-table`")
    fun fetchAllDonor(): Flow<List<DonorEntity>>
    
    // 修正参数类型为String
    @Query("SELECT*FROM `donor-table` Where id=:id")
    fun fetchDonorById(id: String): Flow<DonorEntity?>
    
    // 新增检查ID是否存在的查询
    @Query("SELECT EXISTS(SELECT * FROM `donor-table` WHERE id = :id)")
    suspend fun isIdExists(id: String): Boolean
}

2. 重写重复检查函数

将检查函数改为挂起函数,直接调用DAO的查询方法,确保同步获取结果:

private suspend fun isIdUnique(id: String, donorDao: DonorDao): Boolean {
    return !donorDao.isIdExists(id)
}

3. 修改插入逻辑

在协程内完成检查后再执行插入,确保逻辑顺序正确:

private fun addRecord(donorDao: DonorDao) {
    val id = binding?.etDonorId?.text.toString().trim()
    val bloodGr = binding?.etDonorBloodgroup?.text.toString().trim()
    
    if (id.isEmpty() || bloodGr.isEmpty()) {
        Toast.makeText(this, "请填写完整信息", Toast.LENGTH_SHORT).show()
        return
    }

    lifecycleScope.launch {
        if (isIdUnique(id, donorDao)) {
            donorDao.insert(DonorEntity(id = id, bloodGroup = bloodGr))
            Toast.makeText(applicationContext, "记录已保存", Toast.LENGTH_SHORT).show()
        } else {
            Toast.makeText(this@YourActivity, "ID重复", Toast.LENGTH_SHORT).show()
        }
    }
}

4. 可选:数据库层面添加唯一约束(更可靠)

如果要从根源杜绝重复ID,可以给实体类的id字段添加唯一索引,即使代码层面有疏漏,数据库也会阻止重复插入:

@Entity(
    tableName = "donor-table",
    indices = [Index(value = ["id"], unique = true)]
)
data class DonorEntity(
    @PrimaryKey(autoGenerate = true)
    var sr_no: Int = 0,
    var id: String = "",
    var bloodGroup: String = ""
)

修改后记得更新数据库版本(比如从2改为3)。


内容的提问来源于stack exchange,提问作者Dhruv pratap

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:35:20