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
相关产品推荐
相关产品推荐

