【问题标题】:Room Dao Returns Insert IDs but Data is Missing from DatabaseRoom Dao 返回插入 ID,但数据库中缺少数据
【发布时间】:2020-05-15 15:55:10
【问题描述】:

使用 Dao 在我的房间数据库中保存对象列表时

@Insert()
fun saveCharmRankMaterialCosts(materialCosts: List<CharmRankCraftingCost>) : List<Long>

这在我的存储库类中用于保存 API 调用的结果:

val charmRankCosts = CharmRankCraftingCost.fromJsonCraftingCost(
    charmRankId.toInt(),
    jsonCharmRank.crafting
)

// save crafting/upgrade costs for the rank
val results = charmDao.saveCharmRankMaterialCosts(charmRankCosts)
Log.d("CharmRepository", "Saved charm material costs: ${results.toString()}");
assert(!results.contains(-1))

运行此代码时,将返回插入 ID,并且永远不会触发断言(即没有插入失败)。

但是,当我检查设备上的数据库时,大部分应该插入的 ID 都从表中丢失了。我对这里发生的事情感到非常困惑。我已经调试了这个问题很多小时,但未能成功。我有什么明显的遗漏吗?

【问题讨论】:

  • 1.你确定你检查了确切的数据库吗?有时会在处理设备文件资源管理器、复制数据库文件等过程中出现混乱。 2. 也许您的代码中还有其他地方可以更改数据库(在另一个线程中),删除插入的行?
  • @sergiytikhonov 我很确定我正在查看正确的数据库,但我会仔细检查。我也可以尝试在 android studio 4.1 中使用数据库检查器
  • 至于删除,我会在保存新项目之前清除表格,所以在保存内容时可能会调用删除函数?
  • 如果你之前做了清除(在一个线程或一个协程中)它不应该影响。顺便说一句,您的代码不清楚您使用什么来从主线程切换数据库操作。
  • 代码在我的存储库中,用于视图模型,在我的视图中可以从 IO 调度程序上的协程访问。

标签: android kotlin android-sqlite android-room


【解决方案1】:

这个问题似乎与外键约束有关。我有一个带有多个相关数据对象的CharmRank 数据类。见下文:

/**
 * Copyright Paul, 2020
 * Part of the MHW Database project.
 *
 * Licensed under the MIT License
 */
@Entity(tableName = "charm_ranks")
data class CharmRank(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "charm_rank_id")
    var id: Int = 0,

    @ColumnInfo(name = "charm_id")
    var charmId : Int,

    @ColumnInfo(name = "charm_rank_level")
    var level: Int = 0, // 3

    @ColumnInfo(name = "charm_rank_rarity")
    var rarity: Int = 0, // 6

    @ColumnInfo(name = "charm_rank_name")
    var name: String = "",

    @ColumnInfo(name = "craftable")
    var craftable: Boolean
)

每个魅力等级都有相关的技能和物品来制作所述等级。这些对象只是关系对象,因为它们持有CharmRank 的ID 和SkillRank 在技能对象的情况下,或CharmRank 的ID 和Item 对象的ID。

data class CharmRankSkill(

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "charm_rank_skill_id")
    var id: Int,

    var charmRankId : Int,

    var skillRankId: Int
)
data class CharmRankCraftingCost(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "charm_rank_crafting_cost_id")
    var id: Int,

    @ColumnInfo(name = "charm_rank_id")
    var charmRankId: Int,

    @ColumnInfo(name = "charm_rank_crafting_cost_item_quantity")
    val quantity: Int,

    val itemId: Int
)

最初在CharmRankCraftingCost 中,我对Item 对象和CharmRank 对象有一个外键约束。下面是Item对象的外键约束:

ForeignKey(
  entity = Item::class,
  parentColumns = ["item_id"],
  childColumns = ["itemId"],
  onDelete = ForeignKey.CASCADE
)

Item 数据对象具有远程数据源提供的 ID,因此当我将项目插入到其各自的表中时,冲突解决设置为 Replace。在为CharmRanks 将关系项保存到数据库的过程中,我还必须在保存CharmRankCraftingCosts 之前保存Item 对象。似乎发生的事情是,当插入 Item 对象时,有时项目会被替换,这会触发外键的级联动作,导致我刚刚为 CharmRank 保存的 CharmRankCraftingCosts 项目由于级联效应而被删除。

删除Item 表上的外键约束解决了我的问题。

【讨论】:

  • 谢谢保罗。您的解决方案对我有所帮助,尽管在我的情况下,外键引用的父表有一个 @Insert 和导致问题的 REPLACE 冲突策略。
【解决方案2】:

正如我从 cmets 中了解到的,您在插入之前进行了删除。问题是插入在删除之前完成,因为您在单独的线程中执行它们。您需要在一笔交易中同时完成这两项工作。在带有@Transaction 注解的DAO 类中创建一个方法(确保你的dao 是一个抽象类,这样你就可以实现这个方法的主体):

@Dao
public abstract class YourDao{

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    public abstract List<Long> insertData(List<Data> list);

    @Query("DELETE FROM your_table")
    public abstract void deleteData();

    @Transaction
    public void insertAndDeleteInTransaction(List<Data> list) {
        // Anything inside this method runs in a single transaction.
        deleteData();
        insertData(list);
    }
}

阅读 this 了解 Kotlin 版本的代码。

【讨论】:

  • 我试过了,但这并没有解决问题。即使在使我的保存功能成为非暂停功能之后,行为也是完全相同的
  • 请分享您的 DAO 和存储库。我们需要看看你从数据库中删除和插入的方式。
  • 我发现了这个问题,它与我删除和插入的方式无关。这实际上是由于外键级联效应。
  • 我仍然会奖励你的答案,因为你努力帮助我。然而,我的答案将是被接受的。谢谢
猜你喜欢
  • 1970-01-01
  • 2018-12-14
  • 1970-01-01
  • 1970-01-01
  • 2019-05-01
  • 1970-01-01
  • 2023-01-21
  • 2020-03-30
  • 2018-02-20
相关资源
最近更新 更多