preface

Recently, WHEN I was using Room, I mistakenly upgraded the database due to great carelessness. Many crashes occurred during gray scale. This article is to commemorate their own “young ignorance”.

This article is translated from the official Google blog (self-provided science online), I stepped on the pit after the search, posted to hope you avoid falling into the pit ~

The body of the

Removed some of the “nonsense” from the original text and went straight to the code.

If you are interested in more details, you can read the original text.

Preconditions: Our current version of the app already has a database like this:

@Database(entities = {User.class}, version = 1)
public abstract class UsersDatabase extends RoomDatabase
Copy the code

If we change the table structure of the database in the next version of app, different problems will occur in different scenarios:

Scenario 1: Vesion does not change – crash

Now run the app, crash welcome you:

java.lang.IllegalStateException: Room cannot verify the data integrity. Looks like you've changed schema but FORGOT to update the version number. You can  simply fixthis by increasing the version number.
Copy the code

Suppose we now increment the version number:

@Database(entities = {User.class}, version = 2)
public abstract class UsersDatabase extends RoomDatabase
Copy the code

We have another problem:

Scenario 2: Add vesion, but do not provide migration-crash

When we change version from 1 to 2. After the jubilant upgrade from the old version… Welcome to Crash:

java.lang.IllegalStateException: A migration from 1 to 2 is necessary. Please provide a Migration in the builder or call fallbackToDestructiveMigration in the builder in which case Room will re-create all of the tables.
Copy the code

Scenario 3: Adding vesion, using Fallback Migration – Data is cleared

Do not provide custom Migration, but do not want to crash, you can try this:

database = Room.databaseBuilder(context.getApplicationContext(),
                UsersDatabase.class."Sample.db")
        .fallbackToDestructiveMigration()
        .build();
Copy the code

However, understand what it means before you use it:

When Room starts, it detects if version has increased, and if so, Migration is found to perform specific operations. If there is no because fallbackToDestructiveMigration (). The database will be deleted and rebuilt…

It does not crash, but all data is lost.

Scenario 4: VesION is added and Migration is provided. – Data is normal

What if version changes and Migration is still required even if the table structure does not change?

@Database(entities = {User.class}, version = 2)
public abstract class UsersDatabase extends RoomDatabase {
/ /...
static final Migration MIGRATION_1_2 = new Migration(1.2) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        // An empty implementation because there is no change}};/ /...
database =  Room.databaseBuilder(context.getApplicationContext(),
        UsersDatabase.class."Sample.db")
        .addMigrations(MIGRATION_1_2)
        .build();
Copy the code

Scenario 5: Change the table structure

If we want to update the data again, this time we need to change the table structure: add a last_UPDATE field to the user table. So what do we do now?

1. Add version first

@Database(entities = {User.class}, version = 3)
public abstract class UsersDatabase extends RoomDatabase
Copy the code

2. Provide Migration

static final Migration MIGRATION_2_3 = new Migration(2.3) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE users "
                + " ADD COLUMN last_update INTEGER"); }};Copy the code

PS: many old iron, see this may feel headache. I have to write so much SQL. There is a trick: when adding fields to user.class, compile and search for 3.json globally. You will find that the SQL statement has already been generated.

3, Modify Room. DatabaseBuilder

database = Room.databaseBuilder(context.getApplicationContext(),
        UsersDatabase.class."Sample.db")
        .addMigrations(MIGRATION_1_2, MIGRATION_2_3)
        .build();
Copy the code

That’s it. We’ve covered the basic litre usage. But this is just the basic usage, I believe that the pursuit of friends, may need to do something more “exciting” ~

6. Complex Migration

Select last_update (String) from last_update (Int) where last_update (String);

I guess experienced drivers already know what needs to be done:

  • Create a new temporary table,
  • Copy data from the Users table to the temporary table,
  • Delete the users table
  • Rename the temporary table to Users

So for this update, we need to provide this Migration:

static final Migration MIGRATION_3_4 = new Migration(3.4) {
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        // Create temporary tables
        database.execSQL(
                "CREATE TABLE users_new (userid TEXT, username TEXT, last_update INTEGER, PRIMARY KEY(userid))");
        // Copy data
        database.execSQL(
                "INSERT INTO users_new (userid, username, last_update) SELECT userid, username, last_update FROM users");
        // Delete the old table
        database.execSQL("DROP TABLE users");
        / / the name
        database.execSQL("ALTER TABLE users_new RENAME TO users"); }};Copy the code

At this time, we used the version3 app to upgrade to the version4 app without any problems, which was very cool. But let’s ask one more question: what if users upgrade from version1 directly to version4?

** actually won’t have a problem! **Room always performs 1-2 Migration, 2-3 Migration, and 3-4 Migration for this user. This is handy, but if you’re looking for extreme performance and experience, you can provide a 1-4 Migration, like this:

static final Migration MIGRATION_1_4 = new Migration(1.4) { // This line is the same as above
    @Override
    public void migrate(SupportSQLiteDatabase database) {
        // Create temporary tables
        database.execSQL(
                "CREATE TABLE users_new (userid TEXT, username TEXT, last_update INTEGER, PRIMARY KEY(userid))");
        // Copy data
        database.execSQL(
                "INSERT INTO users_new (userid, username, last_update) SELECT userid, username, last_update FROM users");
        // Delete the old table
        database.execSQL("DROP TABLE users");
        / / the name
        database.execSQL("ALTER TABLE users_new RENAME TO users"); }};Copy the code

And then I’m going to add Migration.

database = Room.databaseBuilder(context.getApplicationContext(),
        UsersDatabase.class."Sample.db")
        .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_1_4)
        .build();
Copy the code

The end of the chat

First, the basic usage of Room is not that different from the mainstream ORM library. In terms of API experience, I feel different, but in terms of upgrading Room, I think it is quite convenient.

Let’s talk about the official “touting” : the encapsulation of RxJava and LiveData. How can I say… The initial personal experience is great, but once the table structure becomes complex, it is time to query multiple tables. You will find your LiveData callback will explode… So I have some reservations about this feature, some good places and some awkward times…

My own feeling about Room is… Ok… Ha ha ~

I am a fresh graduate, recently and friends maintain a public account, the content is that we in the transition from fresh graduate to the development of this way stepped on the pit, as well as our step by step learning records, if interested in friends can pay attention to it, together with fuel ~