如何在首次运行时填充 Android Room 数据库表?
Posted
技术标签:
【中文标题】如何在首次运行时填充 Android Room 数据库表?【英文标题】:How to populate Android Room database table on first run? 【发布时间】:2017-11-25 14:47:20 【问题描述】:在SQLiteOpenHelper
中有一个onCreate(SQLiteDatabase ...)
方法,我用来用一些初始数据填充数据库表。
有没有办法先在 Room 数据库表中插入一些数据 应用运行?
【问题讨论】:
您可以提供一个预填充的数据库。然后继续努力。 这里有一篇好文章:medium.com/google-developers/7-pro-tips-for-room-fbadea4bfbd1 【参考方案1】:更新
您可以通过 3 种方式做到这一点:important check this for migration details
1- 从导出的资产架构填充您的数据库
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromAsset("database/myapp.db")
.build();
2- 从文件中填充数据库
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromFile(new File("mypath"))
.build();
3- 您可以在创建数据库后运行脚本,也可以在每次使用RoomDatabase.Callback
打开数据库时运行脚本,该类在最新版本的 Room 库中可用。
你需要实现RoomDatabase.Callback
的onCreate
和onOpen
方法,并添加到RoomDatabase.Builder
,如下所示。
yourDatabase = Room.databaseBuilder(context, YourDatabase.class, "your db")
.addCallback(rdc)
.build();
RoomDatabase.Callback rdc = new RoomDatabase.Callback()
public void onCreate (SupportSQLiteDatabase db)
// do something after database has been created
public void onOpen (SupportSQLiteDatabase db)
// do something every time database is open
;
Reference
您可以在 RoomDatabase.Callback 方法中使用 Room DAO 本身来填充数据库。完整示例见Pagination and Room example
RoomDatabase.Callback dbCallback = new RoomDatabase.Callback()
public void onCreate(SupportSQLiteDatabase db)
Executors.newSingleThreadScheduledExecutor().execute(new Runnable()
@Override
public void run()
getYourDB(ctx).yourDAO().insertData(yourDataList);
);
;
【讨论】:
这个应该被标记为正确答案。谢谢! 我遇到了在.build()
上未触发回调的问题。仅在第一次真正的读/写操作时as described here
我和@Maxim 有同样的问题,onCreate 只在第一次读/写操作被调用时触发,而不是在 build() 调用之后触发。到目前为止唯一的解决方案是Maxim在链接上提出的
我正在尝试这种方法,但我得到一个:java.lang.IllegalStateException: getDatabase called recursive
@EduardoCorona 您可能已将getYourDb
部分添加到onOpen()
方法中。自然,这将导致无休止的递归调用。【参考方案2】:
我尝试按照 Arnav Rao 的建议使用 RoomDatabase.Callback,但是要使用回调,您不能使用 DAO,因为回调是在构建数据库之前创建的。您可以使用 db.insert 和 content 值,但我认为这不正确。因此,在仔细研究之后——花了我很多时间哈哈——但实际上我在浏览谷歌提供的样本时找到了答案。
https://github.com/googlesamples/android-architecture-components/blob/master/PersistenceContentProviderSample/app/src/main/java/com/example/android/contentprovidersample/data/SampleDatabase.java
参见第 52 行和第 71 行的方法 - 在那里您可以看到在构建数据库实例之后,下一行调用一个方法,该方法检查数据库中是否有任何记录(使用 DAO),然后如果它是空的,它插入初始数据(再次使用 DAO)。
希望这可以帮助其他陷入困境的人:)
【讨论】:
非常感谢您的分享。这看起来是正确的方法。【参考方案3】:您可以在创建数据库后填充表,确保操作在单独的线程上运行。您可以按照以下课程第一次预填充表格。
AppDatabase.kt
@Database(entities = [User::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase()
abstract fun userDao(): UserDao
companion object
// For Singleton instantiation
@Volatile private var instance: AppDatabase? = null
fun getInstance(context: Context): AppDatabase
return instance ?: synchronized(this)
instance ?: buildDatabase(context).also instance = it
private fun buildDatabase(context: Context): AppDatabase
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
.addCallback(object : RoomDatabase.Callback()
override fun onCreate(db: SupportSQLiteDatabase)
super.onCreate(db)
//pre-populate data
Executors.newSingleThreadExecutor().execute
instance?.let
it.userDao().insertUsers(DataGenerator.getUsers())
)
.build()
DataGenerator.kt
class DataGenerator
companion object
fun getUsers(): List<User>
return listOf(
User(1, "Noman"),
User(2, "Aayan"),
User(3, "Tariqul")
)
【讨论】:
【参考方案4】:我尝试了多种方法来做到这一点,但每种方法都不可用。
首先,我尝试使用“addMigrations”方法将迁移实现添加到 Room,但发现它仅在数据库升级期间运行,而不是在创建时运行。
然后,我尝试使用“openHelperFactory”方法将 SQLiteOpenHelper 实现传递给 Room。但是在创建了一堆类以绕过 Room 的包级访问修饰符之后,我放弃了努力。我也尝试继承 Room 的 FrameworkSQLiteOpenHelperFactory,但同样,它的构造函数的包级访问修饰符不支持这一点。
最后,我创建了一个 IntentService 来填充数据并从我的 Application 子类的 onCreate 方法调用它。该方法有效,但更好的解决方案应该是即将修复本页其他地方 Sinigami 提到的跟踪器问题。
达里尔
[2017 年 7 月 19 日添加]
问题似乎已在 Room 1.0.0 中解决。 Alpha 5。此版本向 RoomDatabase 添加了一个回调,允许您在首次创建数据库时执行代码。看看:
https://developer.android.com/reference/android/arch/persistence/room/RoomDatabase.Callback.html
【讨论】:
【参考方案5】:@Provides
@Singleton
LocalDatabase provideLocalDatabase(@DatabaseInfo String dbName, Context context)
return Room.databaseBuilder(context, LocalDatabase.class, dbName)
.addCallback(new RoomDatabase.Callback()
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db)
super.onCreate(db);
db.execSQL("INSERT INTO id_generator VALUES(1, 1, 1);");
)
// .addMigrations(LocalDatabase.MIGRATION_1_2)
.build();
【讨论】:
【参考方案6】:有 3 种方式预填充 db 前 2 个是处理资产和文件,描述为 here 第三种方式是创建db后编程
Room.databaseBuilder(context, Database::class.java, "app.db")
// ...
// 1
.createFromAsset(...)
// 2
.createFromFile(...)
// 3
.addCallback(DatabaseCallback())
.build()
这里是手动填充
class DatabaseCallback : RoomDatabase.Callback()
override fun onCreate(db: SupportSQLiteDatabase) = db.run
// Notice non-ui thread is here
beginTransaction()
try
execSQL(...)
insert(...)
update(...)
delete(...)
setTransactionSuccessful()
finally
endTransaction()
【讨论】:
【参考方案7】:我也在这个话题上苦苦挣扎,这个解决方案对我有用:
// build.gradle
def room_version = "2.2.5"
// Room
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
在您的 App 类中:
// virtually create the db
val db = Room.databaseBuilder(
appContext, AppDatabase::class.java,
Res.getString(R.string.dbname)
).createFromAsset(Res.getString(R.string.source_db_name)).build()
// first call to db really creates the db on filesystem
db.query("SELECT * FROM " + Room.MASTER_TABLE_NAME, null)
【讨论】:
以上是关于如何在首次运行时填充 Android Room 数据库表?的主要内容,如果未能解决你的问题,请参考以下文章
android 应用程序在首次尝试在发布模式下使用 Facebook sdk 共享数据时崩溃
Swift Scene Delegate 在首次启动时不运行代码