如何将 Room Persistence Library 与预填充的数据库一起使用?

Posted

技术标签:

【中文标题】如何将 Room Persistence Library 与预填充的数据库一起使用?【英文标题】:How to use Room Persistence Library with pre-populated database? 【发布时间】:2017-10-31 00:37:32 【问题描述】:

我想将 Room 与预先填充的数据库一起使用,但我不明白如何告诉 Room 在哪里可以找到我的数据库。

我现在把它放在src/main/assets/databases 中,当我为 Room 数据库创建实例时,我是这样创建的:

Room.databaseBuilder(
    getApplicationContext(),
    AppDatabase.class,
    "justintrain.db"
)
.allowMainThreadQueries()
.build();

这样,我认为它每次都在创建一个新数据库,或者无论如何,它没有使用预先填充的数据库。

我怎样才能找到我的数据库?

【问题讨论】:

我还没有看到一个好的解决方案。我为此提交了a feature request。 @CommonsWare 所以我猜它需要一段时间才能实施。你知道在那之前可以使用的任何解决方法吗? (非常感谢您的报告!) 好吧,您可以假设数据库存储在getDatabasePath() 中,用于您选择的数据库文件名。然后,如果该文件尚不存在,请在创建 RoomDatabase 之前安排将资产复制到该路径中。这是我在该问题中提出的第一个选项,理想情况下,我们可以更加保证“getDatabasePath() for your selected database filename”是正确答案。 @AlbertoGiunta 为你工作medium.com/google-developers/… ? CommonsWare 在这里有一个很好的解决方案:github.com/commonsguy/cw-androidarch/tree/v0.6/General/… 【参考方案1】:

Room 现在支持预填充数据库。只需使用SQLite Browser 之类的程序或您选择的任何其他程序来准备您的数据库。然后把它放在Assets Folder 可能在一个名为database 的子文件夹中然后调用:

Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromAsset("database/myapp.db")
.build()

如果您没有将数据库作为资产提供但您下载了它或者它在文件系统中,那么方法是:

Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromFile(File("mypath"))
.build()

有关此功能的更多描述或数据库迁移,您可以查看Documentation Training。

【讨论】:

完美运行。非常感谢 @Xenolion 你救了我很多天。 我刚刚注意到,我的数据库总是被内部资产覆盖(每次启动应用程序时)。其他人没有这个问题吗?使用 Room 2.2.5 我知道了,点这里看看能不能帮到你,发短信给我,删除你的评论,我会删除我的wa.me/255684521543【参考方案2】:

从 Room 2.2 开始,您可以使用以下命令预填充您的数据库:

Room.databaseBuilder(appContext, TestDatabase.class, “Sample.db”)
    .createFromAsset(“database/myapp.db”)
    .build()

【讨论】:

【参考方案3】:

更新(2019 年 11 月 7 日)

Room 现在支持使用开箱即用的预打包数据库,从 2.2.0 版开始

https://developer.android.com/jetpack/androidx/releases/room#2.2.0

2.2.0 版之前的解决方案:没有任何其他外部库的简单解决方案。

Room 依赖于现有的 Android 框架代码来创建或打开数据库。如果您查看FrameworkSQLiteOpenHelper(Room 版本的SQLiteOpenHelper)的源代码,它会在需要时在内部调用SQLiteOpenHelper.getReadableDatabase() 和其他方法。

因此,最简单的解决方案是在使用 Room 创建 DB 之前,将 DB 文件从 assets 目录复制到 mContext.getDatabasePath("my-database.sqlite")

在你的情况下,代码看起来像这样 -

private final String DB_NAME = "my-database.sqlite";

private MyDatabase buildDatabase(Context context) 
    final File dbFile = context.getDatabasePath(DB_NAME);

    if(!dbFile.exists()) 
        copyDatabaseFile(dbFile.getAbsolutePath());
    

    return Room.databaseBuilder(context.getApplicationContext(),
        MyDatabase.class, DB_NAME)
        .build();


private void copyDatabaseFile(String destinationPath) 
    // code to copy the file from assets/database directory to destinationPath

此链接包含复制数据库所需的代码 - link with code

【讨论】:

非常感谢。这应该是公认的答案。我花了一整天的时间试图弄清楚这一点。我知道我们不需要另一个库,将数据库文件复制到数据库位置应该可以工作。 很遗憾,这个答案只是第三名,可能很多人都错过了。 你将如何为此编写测试? 如何告诉 Room 使用新复制的数据库?我可以通过文件浏览器确认数据库已正确传输,但 Room.databasebuilder 仍会生成一个空数据库。【参考方案4】:

2019 年有效的解决方案,没有 hack 或依赖项 (Kotlin)

    将您的.db 文件放入assets/databases(或其中的任何文件夹,只要它位于assets 下)。

    使用 Room 2.2 现有的 createFromAsset() 函数,传入数据库的路径。例如,如果您的数据库文件名为my_data.db,并且位于assets 文件夹的databases 目录下,那么您将使用createFromAsset("databases/my_data.db")

假设您的数据库名称(例如,my_data)存储在一个名为 DATABASE_NAME 的常量变量中,您可以使用以下示例代码:

Room.databaseBuilder(
                    context.applicationContext,
                    MyDatabase::class.java,
                    DATABASE_NAME
                )
                    .createFromAsset("databases/$DATABASE_NAME.db")
                    .build()

重要提示:确保您的数据类/实体的架构与您的 .db 文件的架构精确匹配。例如,如果一个列在.db 文件中没有显式标记为NOT NULL,那么这意味着该列可以有 null 其中的价值观。在 Kotlin 中,您必须将其与数据类中的 val colName: dataType? = null 匹配。如果您只是执行val colName: dataType,Kotlin 会将其编译为 NOT NULL 列,当您尝试运行您的应用程序时会抛出异常。

注意:如果您想从下载到 Android 设备本身的数据库文件创建 Room 数据库,您也可以使用 createFromFile() 函数。查看official documentation 了解如何执行此操作。

【讨论】:

【参考方案5】:

不使用外部库的带房间的类似解决方案: 1.将您的数据库复制到资产文件夹中 2. 从 assets 文件夹中复制你的数据库

public class MainActivity extends AppCompatActivity 

public static AppDatabase db;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    copyDatabase(getApplicationContext(), "yourdatabase.db");

    db = Room.databaseBuilder(getApplicationContext(), .class, "yourdatabase.db").allowMainThreadQueries().build();


private void copyDatabase(Context context, String databaseName) 
    final File dbPath = context.getDatabasePath(databaseName);

    // If the database already exists, return
    if (dbPath.exists()) 
        Log.d("Activity", "db Path Exists");
        return;
    

    // Make sure we have a path to the file
    dbPath.getParentFile().mkdirs();

    // Try to copy database file
    try 
        final InputStream inputStream = context.getAssets().open(databaseName);
        final OutputStream output = new FileOutputStream(dbPath);

        byte[] buffer = new byte[8192];
        int length;

        while ((length = inputStream.read(buffer, 0, 8192)) > 0) 
            output.write(buffer, 0, length);
        

        output.flush();
        output.close();
        inputStream.close();
    
    catch (IOException e) 
        Log.d("Activity", "Failed to open file", e);
        e.printStackTrace();
    

【讨论】:

【参考方案6】:

我也遇到了同样的问题,所以我创建了一个库来解决这个问题。 接受的答案有效,但我认为使用库更容易。

AppDatabase db = RoomAsset
    .databaseBuilder(context.getApplicationContext(), AppDatabase.class, "database_name.db")
    .build(); 

将其添加到存储库末尾的根 build.gradle:

allprojects 
    repositories 
        ...
        maven  url "https://jitpack.io" 
    

添加依赖

dependencies 
    // ... other dependencies
    implementation 'com.github.humazed:RoomAsset:v1.0'

你可以在这里找到图书馆:https://github.com/humazed/RoomAsset

【讨论】:

这是有风险的。迁移可能不会成功。 你能详细说明什么是风险!。有什么问题吗? 我个人在应用升级时在Application中使用Context.deleteDatabase() 它不起作用:模式导出目录未提供给注释处理器,因此我们无法导出模式。您可以提供 room.schemaLocation 注释处理器参数或将 exportSchema 设置为 false。【参考方案7】:

您只需将assets/databases 复制到app/databases 而不是在databaseBuilder中添加addMigrations() 它会保留您的数据

【讨论】:

【参考方案8】:

这就是我解决它的方法,以及如何使用预先填充的数据库(最高 Room v. alpha5)来发布您的应用程序

将您的 SQLite DB database_name.db 放入 assets/databases 文件夹

将文件from this repo 放入一个名为sqlAsset的包中

在您的 AppDatabase 类中,相应地修改您 Room 的数据库创建代码:

Room.databaseBuilder(context.getApplicationContext(), 
                     AppDatabase.class, 
                     "database_name.db")
.openHelperFactory(new AssetSQLiteOpenHelperFactory())
.allowMainThreadQueries()
.build();

请注意,您必须使用"database_name.db" 而不是getDatabasePath() 或其他方法:它只需要文件名。

【讨论】:

嗨 Alberto,你会推荐什么程序来生成 .db 文件(我的意思是数据库编辑器)?我需要一个生成与 Room 模式兼容的数据库的编辑器。 嘿@PedroGuerra!我使用了一个非常简单的程序,名为“DB Browser for SQLite”。我认为它是跨平台的,因此应该可以很好地满足您的需求! 也适用于 alpha9(只需要在需要的地方实现来自 AutoClosable 的方法)但无法使用 beta1(SupportSQLiteOpenHelper.Configuration 缺少一些东西,因此无法实例化 AssetSQLiteOpenHelper @AlbertoGiunta 你好 Alberto,很抱歉这个请求,但是请你在 github 上创建一个新标签,以便我可以使用 jitpack 将它导入我的项目中吗? 感谢@Alberto Giunta 它节省了我的时间!我使用您的代码并建立依赖关系,以便在这里使用! github.com/daolq3012/AssetSQLiteOpenHelper任何人都可以轻松使用它。

以上是关于如何将 Room Persistence Library 与预填充的数据库一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

Android Persistence room:“无法弄清楚如何从光标读取此字段”

Android Room Persistence Library - 如何查找 ID 列表中包含 ID 的实体?

无法导入 android.arch.persistence.room.testing.MigrationTestHelper

Android Room Persistence 库和 Kotlin

调用 Rooms inMemoryBuilder 方法时,Room Persistence Library 运行时异常

Room Persistence Library:迁移过程中的奇怪错误