How to populate Android Room database table on first run?
Updated
You can do this in 3 ways: important check this for migration details
1- Populate your database from exported asset schema
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromAsset("database/myapp.db")
.build();
2- Populate your database from file
Room.databaseBuilder(appContext, AppDatabase.class, "Sample.db")
.createFromFile(new File("mypath"))
.build();
3- You can run scripts after database is created or run every time database is opened using RoomDatabase.Callback
, this class is available in the latest version of the Room library.
You need to implement onCreate
and onOpen
method of RoomDatabase.Callback
and add it to RoomDatabase.Builder
as shown below.
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
You can use Room DAO itself in the RoomDatabase.Callback methods to fill the database. For complete examples see 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);
}
});
}
};
I tried to use the RoomDatabase.Callback as suggested by Arnav Rao, but to use a callback you cannot use the DAO as the callback is created before the database has been built. You could use db.insert and content values, but I didn't think that would have been correct. So after looking into it a bit more - took me ages lol - but I actually found the answer when going through the samples provided by Google.
https://github.com/googlesamples/android-architecture-components/blob/master/PersistenceContentProviderSample/app/src/main/java/com/example/android/contentprovidersample/data/SampleDatabase.java
See line 52 and the method on line 71 - In there you can see after the build of the database instance, the next line calls a method which checks if there are any records in the database (using the DAO) and then if it’s empty it inserts the initial data (again using the DAO).
Hope this helps anyone else who was stuck :)
You can populate tables after creating the database, make sure the operation is running on a separate thread. You can follow the classes bellow to pre-populate tables on the first time.
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")
)
}
}
}
I tried a number of ways to do this, each to no available.
First, I tried adding a Migration implementation to Room using the 'addMigrations' method, but found that it only runs during a database upgrade, but not on creation.
Then, I tried passing a SQLiteOpenHelper implementation to Room using the 'openHelperFactory' method. But after creating a bunch of classes in order to get around Room's package-level access modifiers, I abandoned the effort. I also tried subclassing Room's FrameworkSQLiteOpenHelperFactory but, again, the package-level access modifier of its constructor didn't support this.
Finally, I created a IntentService to populate the data and invoked it from the onCreate method of my Application subclass. The approach works but a better solution should be the upcoming fix to the tracker issue mentioned by Sinigami elsewhere on this page.
Darryl
[Added July 19, 2017]
The issue looks as though it's resolved in Room 1.0.0. Alpha 5. This release added a callback to RoomDatabase that lets you execute code when the database is first created. Take a look at:
https://developer.android.com/reference/android/arch/persistence/room/RoomDatabase.Callback.html
@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();
}