How to populate Android Room database table on first run?

AndroidAndroid RoomAndroid Room-Prepackageddatabase

Android Problem Overview


In SQLiteOpenHelper there is a onCreate(SQLiteDatabase ...) method which i used to populate database tables with some initial data.

> Is there a way to insert some data into Room database table on first > app run?

Android Solutions


Solution 1 - Android

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

Solution 2 - Android

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

Solution 3 - Android

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")
            )
        }
    }

}

Solution 4 - Android

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

Solution 5 - Android

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

Solution 6 - Android

There are 3 ways of prepopulating of db
The first 2 is coping from assets and file which is described here
The third way is programmatical after db creation

Room.databaseBuilder(context, Database::class.java, "app.db")
    // ...
    // 1
    .createFromAsset(...)
    // 2
    .createFromFile(...)
    // 3
    .addCallback(DatabaseCallback())
    .build()

Here is the manual filling

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

Solution 7 - Android

I was struggling with this topic too and this solution worked for me:

// 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"

In your App class:

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser2538289View Question on Stackoverflow
Solution 1 - AndroidArnav RaoView Answer on Stackoverflow
Solution 2 - AndroidN1234View Answer on Stackoverflow
Solution 3 - AndroidAbu NomanView Answer on Stackoverflow
Solution 4 - AndroidDarryl StaflundView Answer on Stackoverflow
Solution 5 - AndroidnAkhmedovView Answer on Stackoverflow
Solution 6 - AndroidVladView Answer on Stackoverflow
Solution 7 - AndroidDennis AllertView Answer on Stackoverflow