【问题标题】:What format should the `.db` file take in a call to Room.createFromAsset("initialData.db")?`.db` 文件在调用 Room.createFromAsset("initialData.db") 时应该采用什么格式?
【发布时间】:2020-05-13 15:03:12
【问题描述】:

场景

我正在尝试使用一些条目预填充 Room 数据库。 api调用的文档很清楚(developer.android docs),我读过一篇很好的博客(Medium article),但我不熟悉实际预打包数据库文件的语法/格式(developmentData.db在下面的例子)。我在搜索过的任何地方都找不到任何文档。

Room
  .databaseBuilder(
      context.applicationContext, 
      MyDatabase::class.java, 
      "my_database")
  .fallbackToDestructiveMigration()
  .createFromAsset("database/developmentData.db")
  .build()

想法

我已按照documentation 导出架构。这是该过程的结果,以供参考。在这种情况下,我只是为了在这里提问而创建一个虚拟对象。

{
  "formatVersion": 1,
  "database": {
    "version": 1,
    "identityHash": "0ad43a6714902eedbb90c1f77ab1ffcb",
    "entities": [
      {
        "tableName": "library_table",
        "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`libraryId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `description` TEXT NOT NULL)",
        "fields": [
          {
            "fieldPath": "libraryId",
            "columnName": "libraryId",
            "affinity": "INTEGER",
            "notNull": true
          },
          {
            "fieldPath": "title",
            "columnName": "title",
            "affinity": "TEXT",
            "notNull": true
          },
          {
            "fieldPath": "description",
            "columnName": "description",
            "affinity": "TEXT",
            "notNull": true
          }
        ],
        "primaryKey": {
          "columnNames": [
            "libraryId"
          ],
          "autoGenerate": true
        },
        "indices": [],
        "foreignKeys": []
      }
    ],
    "setupQueries": [
      "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
      "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"0ad43a6714902eedbb90c1f77ab1ffcb\")"
    ]
  }
}

这是我的developmentData.db 的当前内容,但验证失败:

CREATE TABLE IF NOT EXISTS `library_table` (
`libraryId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
`title` TEXT NOT NULL,
`description` TEXT NOT NULL
);
INSERT INTO `library_table` (title, description) VALUES ("Library 1", "A whopping amazing library!");
INSERT INTO `library_table` (title, description) VALUES ("Library the Second", "A smashing library indeed!");
INSERT INTO `library_table` (title, description) VALUES ("Thou Third Library", "Quite possibly the most amazing library ever.");
INSERT INTO `library_table` (title, description) VALUES ("Ye Olde Fouthe Librarye", "So many profound thoughts in here.");

这是错误。它没有加密,所以这一定是语法问题。 E/SQLiteLog: (26) file is encrypted or is not a database

问题

在调用createFromAsset() 时引用的.db 文件应该采用什么格式?如果某处有现有文档,我会很高兴提供参考。

【问题讨论】:

  • 应该是SQLite数据库,我觉得可能需要Room自己准备的。 “但验证失败”——你能提供详细信息吗?
  • 请详细咨询,谢谢。刚刚将其添加到问题中。
  • 该错误表明 Room 试图打开的数据库文件本身已损坏。 “这是我的 developmentData.db 的当前内容”——你的意思是从字面上看,developmentData.db 是一个带有该文本的文本文件吗?如果是这样,请尝试使用 SQLite 数据库,而不是 SQL 脚本。例如,在您让 Room 创建一个数据库后,从设备上复制一个数据库(sans createFromAsset())。
  • 啊...我完全走错了路。我想到了 Spring 可以执行以预填充数据库的 data.sql 之类的东西。你是对的,这解决了问题。我将尝试在下面给出更完整的答案,但 @CommonsWare 应该得到它的功劳。

标签: android android-room


【解决方案1】:

这里的解决方案是创建一个实际的 SQLite 数据库二进制文件,而不是我上面的一系列 SQL 语句,并使用该文件。创建它的过程在文档中并没有立即显现出来,所以我会在这里解释它,以防其他人需要指出正确的方向。 @CommonsWare 应该因为这个答案而受到赞誉,因为它为我指出了正确的方向。

在应用中导出 Room 数据库的架构。

这一步有很好的文档here。简而言之,我需要将此注释添加到我的 RoomDatabase 类中。记下exportSchema = true 部分。这会导致 Gradle 构建将 .json 文件放到 app/schemas 目录中。

@Database(entities = [Library::class], version = 1, exportSchema = true)
abstract class MyDatabase : RoomDatabase() {

该 json 文件包含我在下一步中使用的 tableNamecreateSql 键:值对。它看起来像这样:

"tableName": "library_table",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`libraryId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `title` TEXT NOT NULL, `description` TEXT NOT NULL)",

创建 SQLite 二进制文件

然后我利用一个名为DB Browser for SQLite 的免费应用程序来实际创建我需要的.db 二进制文件,并参考上面提到的.json。一旦我向其中添加了几条记录并保存,数据就会毫无问题地预先填充。

将二进制文件放在正确的目录中

最后一点,我没有从文档中立即发现:对createFromAsset() 的调用默认位于/assets 目录中。此目录需要位于所需的源集内。因此,例如,我将 .db 二进制文件放在这里:
<project-root>/app/src/main/assets/initialData.db

【讨论】:

  • 出于好奇...您是在数据库中添加了room_master_table,还是只是在您自己的表中添加了?如果没有该表及其来自 JSON 中 setupQueries 的内容,我还没有尝试过这样做,但如果我们不必担心 createFromAsset() 路径的问题,它会更方便。
  • @humbleDev 是的,如果您提到了 json 文件,您应该在答案中包含另一个有用的部分“setupQueries”,其中包含用于创建的 room_master_table 代码
  • 我实际上并没有将 json 文件的那部分添加到我创建并传递给 createFromAsset() 调用的 SQLite 数据库中。不过,调用仍然成功,并按预期加载了数据。
  • @CommonsWare - 对这里的延迟评论表示歉意,但我尝试使用我设备中的数据库来创建一些初始数据,发现这样做时没有 room_master_table 存在。您是否在您从设备中提取的数据库中找到了一个?
  • @humbleDev:如果数据库是由 Room 创建的,是的。
【解决方案2】:

不太可能的服务器端数据库系统(MySQL、Oracle、SQL Server 等)在关闭时,SQLite 数据库是单个文件。 createFromAsset() 期待的是那种文件,而不是 SQL 脚本。

所以,我通常做的是:

  • 让应用使用 Room 的架构在设备上使用标准 Room 内容创建一个空数据库
  • 从设备上复制该数据库(例如,使用 Android Studio 中的设备文件资源管理器)
  • 使用 SQL Browser 或 sqlite3 或其他工具将数据添加到我的应用表中

这样,我可以保证数据库具有正确的结构和 Room 的元数据。

【讨论】:

  • 这个答案更简洁,从设备上复制数据库而不是检查 json 的想法比我在自己的答案中解释的要简单。我会建议其他人遵循这种模式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多