This is the 27th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021

preface

In the previous article, Room was wrapped with ViewModel+LiveData. In this article, we will explain Room upgrades and pre-populations. Just get started!

1. Prepopulate the database

As is shown in

When you first open your App, use createFromAsset() and createFrimFile() to create Room database.

As is shown in

Since we want to bring some data, so we prepare some data in advance into the own database.

How do you use it?

@Database(entities = [Student::class], version =1, exportSchema = false)
abstract class MyDatabase : RoomDatabase() {
    companion object {
        private const val DATABASE_NAME = "my_db.db"
        private var mInstance: MyDatabase? = null

        @Synchronized
        @JvmStatic
        open fun getInstance(context: Context): MyDatabase? {
            if (mInstance == null) {
                mInstance = Room.databaseBuilder(
                    context.applicationContext,
                    MyDatabase::class.java,
                    DATABASE_NAME
                ) //.allowMainThreadQueries()
                    .createFromAsset("prestudent.db")
                    .build()
            }
            return mInstance
        }


    abstract fun getStudentDao(a): StudentDao?
}
Copy the code

CreateFromAsset (“prestudent.db”), “FromAsset”, “Asset”, “Asset”, “Asset”, “Asset”, “Asset”

Let’s see how it works:

It can be seen that when the software is successfully installed, the corresponding data is reserved in advance!

All right, next!

2. Upgrade the database using Migration

2.1 Additional names only

Using Migration has been mentioned here, so how about that?

@Database(entities = [Student::class], version =1, exportSchema = false)
abstract class MyDatabase : RoomDatabase() {

    companion object {
        private const val DATABASE_NAME = "my_db.db"
        private var mInstance: MyDatabase? = null

        @Synchronized
        @JvmStatic
        open fun getInstance(context: Context): MyDatabase? {
            if (mInstance == null) {
                mInstance = Room.databaseBuilder(
                    context.applicationContext,
                    MyDatabase::class.java,
                    DATABASE_NAME
                ) //.allowMainThreadQueries()
                    .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
                    .createFromAsset("prestudent.db")
                    .build()
            }
            return mInstance
        }

        @JvmStatic
        val MIGRATION_1_2: Migration = object : Migration(1.2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("ALTER TABLE student ADD COLUMN sex INTEGER NOT NULL DEFAULT 1")}}@JvmStatic
        val MIGRATION_2_3: Migration = object : Migration(2.3) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("ALTER TABLE student ADD COLUMN bar_data INTEGER NOT NULL DEFAULT 1")}}}abstract fun getStudentDao(a): StudentDao?
}
Copy the code

Code parsing

  • MIGRATION_1_2withMIGRATION_2_3Respectively indicate the upgrade policy of the corresponding database
  • Version 1-2 has been addedCOLUMN sex INTEGERThe column name
  • Version 2-3 has been addedCOLUMN bar_data INTEGERThe column name
  • @Database(xxx, version =1, xxxz)Here belowversionRepresents the current version. If you want to upgrade 2, change the corresponding attribute of the latest App to 2
  • Notice, corresponding toStudentEntity classes also need to be modified according to the corresponding SQL! For example, version 1-2 has been addedsex So does the entity class

Entity class will not paste ha, according to the SQL to do the corresponding adjustment!

Let’s take a look at upgrade 1-2 in action

Database information of version 1 before the upgrade:

Database information of version 2 after the upgrade :(version=2, the entity class of the corresponding data table is adjusted)

Database information of version 3 after upgrade :(version=3, corresponding entity class of data table is modified)

OK, all the tests are fine! We’re all at one liter, two liters, three. So what if we keep 1 liter 2,2 liter 3, and we don’t have 1 liter 3, the user version is 1, and our database is already upgraded to 3?

We will change the version to 1, and the corresponding data table entity class will also revert to 1 state. Uninstall the original App and try again!

After re-running, the current App database version is 1.

SQL > alter table entity class (version=3);

We found that when Room encounters cross-version upgrades:

  • Room will first determine whether there is an upgrade plan from 1 to 3,
  • If yes, perform the upgrade plan from 1 to 3 directly.
  • If not, Room executes in sequenceMigration(1, 2),Migration(2, 3)To complete the upgrade!

At this point, we see that the update database only sees the addition of column names! So what do I do if I need the variable type for the column name?

For example, if the original sex is INTEGER, then change the sex to TEXT.

2.2 Destruction and reconstruction strategy

It can be roughly divided into the following steps:

  • Create a temporary table temp_student that meets the table structure requirements
  • Copy data from old table STUDENT to temporary table temp_STUDENT
  • Drop old table student
  • Rename temporary table temp_student to student

After looking at the steps, the idea is clear, to achieve a look!

@Database(entities = [Student::class], version =3, exportSchema = false)
abstract class MyDatabase : RoomDatabase() {

    companion object {
        private const val DATABASE_NAME = "my_db.db"
        private var mInstance: MyDatabase? = null

        @Synchronized
        @JvmStatic
        open fun getInstance(context: Context): MyDatabase? {
            if (mInstance == null) {
                mInstance = Room.databaseBuilder(
                    context.applicationContext,
                    MyDatabase::class.java,
                    DATABASE_NAME
                ) //.allowMainThreadQueries()
// .fallbackToDestructiveMigration()
                    .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4)
                    .createFromAsset("prestudent.db")
                    .build()
            }
            return mInstance
        }

        @JvmStatic
        val MIGRATION_1_2: Migration = object : Migration(1.2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("ALTER TABLE student ADD COLUMN sex INTEGER NOT NULL DEFAULT 1")}}@JvmStatic
        val MIGRATION_2_3: Migration = object : Migration(2.3) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL("ALTER TABLE student ADD COLUMN bar_data INTEGER NOT NULL DEFAULT 1")}}@JvmStatic
        val MIGRATION_3_4: Migration = object : Migration(3.4) {
            override fun migrate(database: SupportSQLiteDatabase) {
                database.execSQL(
                    "CREATE TABLE temp_student (" +
                            "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL," +
                            "name TEXT," +
                            "age INTEGER NOT NULL," +
                            "sex TEXT DEFAULT 'M'," +
                            "bar_data INTEGER NOT NULL DEFAULT 1)"
                )
                database.execSQL(
                    "INSERT INTO temp_student (name,age,sex,bar_data)" +
                            "SELECT name,age,sex,bar_data FROM student"
                )
                database.execSQL("DROP TABLE student")
                database.execSQL("ALTER TABLE temp_student RENAME TO student")}}}abstract fun getStudentDao(a): StudentDao?
}
Copy the code

At this point, we see that MIGRATION_3_4 is created, and the logic inside is the steps we have clarified above. So?

Change the version to 4 (version=4, corresponding entity class needs to be adjusted accordingly)

We see that the corresponding column name type has been modified successfully!

So when we upgrade, should we keep the upgrade process? If you can keep it, it will be convenient for you to troubleshoot problems in the future!

3. Schema file

Room in each database upgrade process, will export a Schema file, which is a JSON format file, which contains the basic information of the database, with this file, developers can clearly know the data can be severe change process, greatly convenient for developers to troubleshoot problems!

So how do you use it?

android {
    compileSdkVersion 30defaultConfig { ... slightly// javaCompileOptions {
// annotationProcessorOptions {
// Arguments = [" room.schemalocation ": "$projectDir/schemas".tostring ()]// Specify where database schema is exported
/ /}
/ /}

        kapt {
            arguments {
                arg("room.schemaLocation"."$projectDir/schemas".toString())
            }
        }
    }
}
Copy the code

The comment section is where the configuration is exported when the business logic uses Java! The next is Kotlin’s configuration export location!

There is also the need to transform

@Database(entities = [Student::class], version =1, exportSchema = false){}Copy the code

Change the corresponding exportSchema to true (the default is true) and uninstall to run again from version 1!

After running successfully, change the version to 2 (version=2, corresponding entity class adjustment) after running again:

OK, we see a new file on the left! Open to see, is the corresponding upgrade policy!

conclusion

Well, that’s the end of this article, I believe you have a comprehensive understanding of Room! In the next post, we’ll cover Jetpack’s Navigation component!