Solution 1 :
There are basically two ways to import data into a Room database upon creation. The first uses an ugly workaround but then allows you to use Room Entities and all. The second is to work directly with the SQLiteDatabase instance provided.
1. Insert a guard and work with Room abstractions
final boolean[] doImport = { false };
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "my-db")
.addCallback(new RoomDatabase.Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
doImport[0] = true;
}
})
.build();
db.userDao().get(17);
if (doImport[0]) {
UserEntity user = new UserEntity();
user.name = "John Doe";
db.userDao().insert(user);
}
The doImport
boolean serves as a guard to protocol, whether the onCreate
callback has been called. It needs to be an array though, because a new value couldn’t be assigned to a simple boolean from within onCreate
.
Notice also the seemingly senseless line db.userDao().get(17);
. It is necessary to access the database in order for the onCreate
callback to be called. Otherwise doImport
would remain false
at this point, regardless of whether or not the database was newly created.
Finally in the last if
block the database may be accessed with all the nice abstractions Room provides.
2. Work with the SQLiteDatabase directly
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "my-db")
.addCallback(new RoomDatabase.Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
db.beginTransaction();
ContentValues values = new ContentValues();
values.put("name", "John Doe");
db.insert("UserEntity", SQLiteDatabase.CONFLICT_ABORT, values);
db.setTransactionSuccessful();
db.endTransaction();
}
})
.build();
A lot less painful then I suspected. You need to work with strings though to identify database tables and fields, which is error prone!
Problem :
I’ve upgraded my app to feature a Room database. When the user upgrades to my new app version, I need to import data into the Room Database that was stored in other data structures. At first glance it looks like Room already supports this scenario:
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "my-db")
.addCallback(new RoomDatabase.Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
// IMPORT DATA
}
})
.build();
However, when trying to actually import data, things get more complicated. When I try to import data using Entities, Daos and the AppDatabase, I run into an Exception:
java.lang.IllegalStateException: getDatabase called recursively
It doesn’t seem to be possible to import data into the database using all the nice Room Entities, Daos and so forth. The onCreate
method does however provide access to the underlying SQLite database. Maybe I’m meant to import data there?
The documentation on using the SQLite database directly is rather thin. And it starts with a big red warning not to access the SQLite database directly but to use the Room abstractions instead!
How should I proceed? How is this usually done?
Do the tables I’ve defined with Entities already exist at this point? Or do I need to create the tables with SQL statements before I can start to import my data?
Comments
Comment posted by prepopulate your Room DB
Have you considered using your existing DB to
Comment posted by user1785730
There is no existing database in my app.
Comment posted by Bob Snyder
What are the “other data structures” that contain the existing data?
Comment posted by stackoverflow.com/q/50775527/4815718
See the answers to this question:
Comment posted by developer.android.com/reference/android/arch/persistence/db/…
As you have a