Android Room删除操作失效:Dao测试中删除后数据未减少求助
基于Room的Android项目删除数据测试失败问题排查
在基于Room的Android项目中,测试Dao对象时,插入和查询数据的测试均正常,但删除数据的测试始终失效。调用delete和deleteById方法后,通过getAll查询到的数据数量与删除前一致。
实体类代码
@TypeConverters(TagConverters::class) @Entity( tableName = "tag", indices = [ // Enforce tag_name to be unique Index(value = ["tag_name"], unique = true) ] ) data class TagEntity( @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "tag_id") val id: Long = 0, @ColumnInfo(name = "tag_name") val name: TagName, val icon: String? = null, val color: String? = null )
Dao代码
@Dao interface TagDao : BaseDao<TagEntity> { @Query("SELECT * FROM tag WHERE tag_id = :id") suspend fun getById(id: Long): TagEntity @Query("SELECT * FROM tag WHERE tag_name = :name") suspend fun getByName(name: String): TagEntity @Query("SELECT * FROM tag") suspend fun getAll(): List<TagEntity> /** Some stuff that I have tested @Delete override suspend fun delete(vararg theme: ThemeEntity) @Query("DELETE FROM theme WHERE theme_id LIKE :id") */ @Query("DELETE FROM tag WHERE tag_name = :id") suspend fun deleteById(id: Long) }
BaseDao代码
@Dao interface BaseDao<T> { @Insert suspend fun insert(obj: T): Long @Insert suspend fun insert(vararg obj: T) @Upsert suspend fun upsert(vararg obj: T) @Delete suspend fun delete(obj: T) }
测试代码
class TagDaoTest { private lateinit var database: AppDatabase private lateinit var tagDao: TagDao @Before fun createDb() { val context = ApplicationProvider.getApplicationContext<Context>() database = Room.inMemoryDatabaseBuilder( context, AppDatabase::class.java ).build() tagDao = database.tagDao() } @Test fun tagDao_delete_a_tag() = runTest { val tag0 = newTag(name = TagName("HISTORY")) val tag1 = newTag(name = TagName("Science")) val tag2 = newTag(name = TagName("Nature")) val tag3 = newTag(name = TagName("Sport")) val tag4 = newTag(name = TagName("culture")) tagDao.insert(tag0) tagDao.insert(tag1) tagDao.insert(tag2) tagDao.insert(tag3) tagDao.insert(tag4) val retrievedTags = tagDao.getAll() Assert.assertEquals(5, retrievedTags.size) tagDao.delete(tag1) tagDao.deleteById(tag2.id) val retrievedTags2 = tagDao.getAll() Assert.assertEquals(3, retrievedTags2.size) } } private fun newTag( name: TagName, date: LocalDate = LocalDate(year= 1979, monthNumber= 11, dayOfMonth = 25) ) = TagEntity( name = name, icon = null, color = null)
报错信息
java.lang.AssertionError: expected:<3> but was:<5> at org.junit.Assert.failNotEquals(Assert.java:835) at eu.aklal.app.core.database.TagDaoTest$tagDao_delete_a_tag$1.invokeSuspend(TagDaoTest.kt:93) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at eu.aklal.app.core.database.TagDaoTest.tagDao_delete_a_tag(TagDaoTest.kt:73)
问题原因及解决方案
1. deleteById方法SQL语句错误
当前deleteById的SQL是DELETE FROM tag WHERE tag_name = :id,但参数id是Long类型的主键值,而tag_name对应数据库中是TagName转换后的字符串,两者完全不匹配,导致这条删除语句找不到任何数据。
修正方法:
把SQL语句改为根据主键tag_id匹配:
@Query("DELETE FROM tag WHERE tag_id = :id") suspend fun deleteById(id: Long)
2. 调用@Delete方法时实体主键不匹配
你调用tagDao.delete(tag1)时,传入的tag1是插入前创建的对象,它的id还是初始值0。但插入Room后,数据库会自动生成自增的主键id,这个值不会自动同步回原对象。Room的@Delete注解是根据实体的主键值来匹配数据库中的数据的,所以用id为0的对象去删除,根本找不到对应的数据。
修正方法(二选一):
方式一:插入后获取主键并更新实体
利用insert方法返回的主键值,更新原对象的id,或者直接查询数据库中的实体再删除:// 插入时获取返回的主键id val tag1Id = tagDao.insert(tag1) // 查询数据库中的真实实体 val tag1FromDb = tagDao.getById(tag1Id) // 用真实实体执行删除 tagDao.delete(tag1FromDb)方式二:统一用deleteById方法删除
既然已经有了deleteById方法,插入后直接用返回的id调用该方法即可:val tag1Id = tagDao.insert(tag1) tagDao.deleteById(tag1Id)
内容的提问来源于stack exchange,提问作者eqtèöck
相关产品推荐
相关产品推荐

