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


69

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

现在,将其放入,src/main/assets/databases并在创建Room数据库的实例时以这种方式创建它:

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

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

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


6
我还没有看到一个好的解决方案。我对此提出了功能要求
CommonsWare

@CommonsWare,所以它要花一段时间才能实现,我想..您知道在那之前可以使用任何解决方法吗?(非常感谢您举报!)
Alberto Giunta

2
好吧,您可以假设getDatabasePath()您选择的数据库文件名存储在数据库中。然后,安排在创建之前将资产复制到该路径RoomDatabase,如果该文件尚不存在。这是我在该问题中建议的第一个选项,理想情况下,我们可以保证“getDatabasePath()对于您选择的数据库文件名”是正确的答案。
CommonsWare


1
CommonsWare在这里有一个很好的解决方案:github.com/commonsguy/cw-androidarch/tree/v0.6/General/...
活的爱情

Answers:


40

这就是我解决问题的方法,以及如何使用预先填充的数据库(最高到Room v。alpha5)运送应用程序

  • 将您的SQLite数据库database_name.db放入assets/databases文件夹

  • 从此仓库中获取文件,并将其放入名为ie的程序包中sqlAsset

  • 在您的AppDatabase课程中,相应地修改Room的DB创建代码:

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

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


嗨,阿尔贝托,您会推荐什么程序来生成.db文件(我的意思是数据库编辑器)?我需要一个生成与Room模式兼容的数据库的编辑器。
Pedro Guerra

7
嘿@PedroGuerra!我使用了一个非常简单的程序,名为“ SQLite的数据库浏览器”。我认为这是跨平台的,因此应该可以很好地满足您的需求!
Alberto Giunta

1
也可以与alpha9一起使用(仅需要从AutoClosable需要的地方实现方法),但不能与beta1一起使用(SupportSQLiteOpenHelper.Configuration缺少一些东西,因此AssetSQLiteOpenHelper无法实例化)
makvasic

@AlbertoGiunta嗨,阿尔贝托,对不起,但您能否在github上创建一个新标签,以便我可以使用jitpack将其导入到我的项目中?
Pedro Guerra

12
感谢@Alberto Giunta节省了我的时间!我在这里使用您的代码并构建到依赖关系,以便于使用! github.com/daolq3012/AssetSQLiteOpenHelper 任何人都可以通过简单的方式使用它。
DaoLQ '17

28

更新(2019年11月7日)

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

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

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

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

因此,最简单的解决方案是mContext.getDatabasePath("my-database.sqlite")在使用Room创建数据库之前,只需将数据库文件从资产目录复制到。

就您而言,代码看起来像这样-

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
}

该链接具有复制数据库所需的代码-带有代码的链接


4
非常感谢。这应该是公认的答案。我花了一整天试图解决这个问题。我知道我们不需要另一个库,并且可以将数据库文件复制到数据库位置。
错误发生

不幸的是,这个答案仅排在第三位,可能被很多人错过了。
Big_Chair

您将如何为此编写测试?
user1927033 '19

1
您如何告诉Room使用新复制的数据库?我可以通过文件浏览器确认数据库已正确传输,但Room.databasebuilder仍会生成一个空数据库。
DinosauRuss

16

我遇到了同样的问题,所以我创建了一个库来做到这一点。可接受的答案,但是我认为使用库会更容易。

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

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

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

添加依赖项

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

您可以在这里找到该库:https : //github.com/humazed/RoomAsset


3
这是有风险的。迁移可能不会成功。
Sourav Bagchi

您能详细说明什么意味着冒险!有什么需要关注的。
令人震惊

Context.deleteDatabase()当应用程序升级时,我个人在应用程序中使用。
shkschneider

它不起作用:未将架构导出目录提供给注释处理器,因此我们无法导出架构。您可以提供room.schemaLocation注释处理器参数,也可以将exportSchema设置为false。
Sandeep Yohans

13

没有黑客或依赖关系的2019年工作解决方案(Kotlin)

  1. 将您的.db文件放置在assets/databases(或实际上在其中的任何文件夹,只要在下面assets)。

  2. 使用Room 2.2的现有createFromAsset()功能,将其传递到数据库的路径。例如,如果您的数据库文件已命名my_data.db并且位于文件夹的databases目录下assets,则可以这样做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文件的架构完全匹配。例如,如果未NOT NULL.db文件中明确标记列,则表示该列中可以包含空值。在Kotlin中,您必须将其与val colName: dataType? = null您的数据类相匹配。如果您只是这样做val colName: dataType,Kotlin会将其编译为一NOT NULL列,并且在您尝试运行您的应用程序时会抛出异常。

注意:如果您想从下载到Android设备本身的数据库文件创建Room数据库,则可以选择使用此createFromFile()功能。查看有关如何执行此操作的官方文档


6

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()

有关此功能的更多描述或数据库迁移,请查看文档培训


1
完美的作品。非常感谢@Xenolion您节省了我很多时间。
GreenROBO '02

1
我只是注意到,我的数据库总是被其中的一个内部资产覆盖(每次我启动该应用程序时)。其他人没有这个问题吗?使用会议室2.2.5
边缘

我知道,请单击此处让我看看是否可以为您提供帮助,在此处给我发短信并删除您的评论,然后删除我的wa.me/255684521543
Xenolion

1

具有空间的类似解决方案,无需使用外部库:1.将数据库复制到资产文件夹中2.从资产文件夹复制数据库

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();
    }
}

}


1

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

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

0

你只要复制assets/databasesapp/databases
比加addMigrations()databaseBuilder
它会让你的数据

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.