向现有房间数据库添加一列,同时使用房间版本 2.4.0-alpha01 中可用的当前自动迁移功能

Add a column to existing room database whilst using the current auto-migration feature available in room version 2.4.0-alpha01

我有一个包含数据的房间数据库,现在我想向其中添加另一列。

Room 版本 2.4.0-alpha01 及更高版本应该使自动迁移更容易,所以我这样使用它:

...
version = 2,
autoMigrations = {
        @AutoMigration(from = 1, to = 2)
        }, 
exportSchema = true

然后在我的模型 class 中,我添加了新的列名称并生成了它的 setter 和 getter,就像其他的一样。

room 文档说,如果 room 由于复杂的架构更改而无法执行迁移,则会抛出编译时错误。但是,就我而言,我收到有关预期模式和新模式(我添加了一列的模式)之间差异的运行时错误。

错误如下:

java.lang.RuntimeException: Exception while computing database live data.
        at androidx.room.RoomTrackingLiveData.run(RoomTrackingLiveData.java:92)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)
     Caused by: java.lang.IllegalStateException: Migration didn't properly handle: accounts(com.bisform.susu.models.Account).
     Expected:
    TableInfo{name='accounts', columns={accountSavings=Column{name='accountSavings', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountName=Column{name='accountName', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountCreateTime=Column{name='accountCreateTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, numberOfDeposits=Column{name='numberOfDeposits', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, accountNumber=Column{name='accountNumber', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, numberOfWithdrawals=Column{name='numberOfWithdrawals', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, accountPayoutDate=Column{name='accountPayoutDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, phoneOfNextOfKin=Column{name='phoneOfNextOfKin', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountImageURI=Column{name='accountImageURI', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountID=Column{name='accountID', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, lastUpdatedDate=Column{name='lastUpdatedDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, phoneNumber=Column{name='phoneNumber', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountPayoutTime=Column{name='accountPayoutTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, lastUpdatedTime=Column{name='lastUpdatedTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, nameOfNextOfKin=Column{name='nameOfNextOfKin', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountCreateDate=Column{name='accountCreateDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, isPaidOut=Column{name='isPaidOut', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
     Found:
E/AndroidRuntime: TableInfo{name='accounts', columns={accountSavings=Column{name='accountSavings', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountName=Column{name='accountName', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountCreateTime=Column{name='accountCreateTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, numberOfDeposits=Column{name='numberOfDeposits', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, accountNumber=Column{name='accountNumber', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, numberOfWithdrawals=Column{name='numberOfWithdrawals', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, accountPayoutDate=Column{name='accountPayoutDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountImageURI=Column{name='accountImageURI', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountID=Column{name='accountID', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, lastUpdatedDate=Column{name='lastUpdatedDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, phoneNumber=Column{name='phoneNumber', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountPayoutTime=Column{name='accountPayoutTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, lastUpdatedTime=Column{name='lastUpdatedTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountCreateDate=Column{name='accountCreateDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, isPaidOut=Column{name='isPaidOut', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
        at androidx.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:103)
...

请问我应该如何处理这个错误?如何使用当前存在的自动迁移功能获得正确迁移的空间?

Please what should I do to handle this error? How do I get room to migrate properly using the auto-migrate feature currently present?

我相信如果您以某种方式省略了自动迁移,您只会得到这个。我相信,使用您展示的代码,唯一的方法是包含手动迁移,因为这将覆盖自动迁移。

测试如下所示。

测试

此测试显示了从 V1 到 V2 覆盖 AutoMigration 的效果,以及未覆盖时迁移的工作方式。

测试基于 Accounts class 根据您预期的 (v2) 和找到的 (v1) 消息构建。

所以账户 class 是 :-

@Entity(tableName = "accounts")
class Accounts {

    //String phoneOfNextOfKin; //<<<<< For  V2 else commented out
    //String nameOfNextOfKin; //<<<<< for V2 else commented out

    String accountSavings;
    String accountName;
    String accountCreateTime;
    Integer numberOfDeposits;
    String accountNumber;
    int numberOfWithdrawals;
    String accountPayoutDate;
    String accountImageURI;
    @PrimaryKey
    Long accountId;
    String lastUpdatedDate;
    String phoneNumber;
    String accountPayoutTime;
    String lastUpdatedTime;
    String accountCreateDate;
    boolean isPaidOut;
}
  • 请注意带有注释的行//<<<<< ....,是已更改的行(针对 V1 注释掉并包含在 V2 中)
  • 即为 V2
  • 添加了 2 列 phoneOfNextOfKin 和 nameOfNextOfKin

@Dao class 在不同版本之间没有变化:-

@Dao
abstract class AccountsDao {
    @Insert
    abstract long insert(Accounts accounts);
    @Query("SELECT * FROM accounts")
    abstract List<Accounts> getAllFromAccounts();
}

一个@Database class :-

@Database(
        entities = {Accounts.class},
        version = TheDatabase.DBVERSION
        /* following line, if no schema saved, needs to be commented out */
        , autoMigrations = {@AutoMigration(from = 1, to = 2)} 
)
abstract class TheDatabase extends RoomDatabase {
    abstract AccountsDao getAccountsDao();

    public static final int DBVERSION = 1; //<<<<< change accordingly

    private static volatile TheDatabase instance = null;

    static TheDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(
                    context,
                    TheDatabase.class,
                    "accounts.db"
            )
                    .allowMainThreadQueries() //run on main thread for brevity and convenience
                    .addMigrations(MIGRATION_1_2) //<<<<< if included then Migration didn't properly handle:
                    .build();
        }
        return instance;
    }

    /* Only needed for creating error shown in question */
    /* doesn't hurt if no addMigrations or when creating V1 */
    static final Migration MIGRATION_1_2 = new Migration(1,2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {

        }
    };
}

最后是下面的 activity :-

public class MainActivity extends AppCompatActivity {

    TheDatabase db;
    AccountsDao dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        db = TheDatabase.getInstance(this);
        dao = db.getAccountsDao();

        Accounts a = new Accounts();
        a.accountCreateDate = "2021-01-01";
        a.accountCreateTime = "08:00:00";
        a.accountImageURI = "imageURI";
        a.accountName = "MyAccount";
        a.accountNumber = "0000000000";
        a.accountPayoutDate = "2021-05-05";
        a.accountPayoutTime = "10:00:00";
        a.accountSavings = "savings";
        a.lastUpdatedDate = "2021-05-03";
        a.lastUpdatedTime = "09:15:00";
        a.numberOfDeposits = 10;
        a.numberOfWithdrawals = 5;
        a.isPaidOut = false;

        //a.nameOfNextOfKin = "Mr Next of Kin"; //<<<<< For V2
        //a.phoneOfNextOfKin = "1111111111"; //<<<<< For V2

        dao.insert(a);
        for(Accounts account: dao.getAllFromAccounts()) {
            String msg = "Account is " + account.accountName + " ID is " + account.accountId;
            if (TheDatabase.DBVERSION > 1) {
                //msg = msg + " next of kin is " + account.nameOfNextOfKin; //<<<<< For V2
            }
            Log.d("ACCOUNTINFO", msg);
        }
    }
}

运行s 和结果:-

  1. 使用上述代码(不包含 V2 代码)且未安装应用程序。即创建 V1 数据库:-
  • App运行成功,Database Inspector显示:-

  • 架构和单行一样符合预期。

  1. 运行 没有任何变化
  • App运行成功,Database Inspector显示:-

3。对 V2 所做的更改特别是:-

  • 在帐户名称和 phone 中包括近亲。

  • 在 TheDatabase DBVERSION 中从 1 更改为 2 并且 .addMigrations 包含在内

  • 在调用 activity 时,姓名和近亲值被分配值而不是被注释掉。连接 msg 变量以包括近亲。

  • 应用程序崩溃

:-

Caused by: java.lang.IllegalStateException: Migration didn't properly handle: accounts(a.a.so69442030javaroomautomigrationsaddcolumns.Accounts).
     Expected:
2021-10-05 14:31:50.669 30268-30268/a.a.so69442030javaroomautomigrationsaddcolumns E/AndroidRuntime: TableInfo{name='accounts', columns={accountSavings=Column{name='accountSavings', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountName=Column{name='accountName', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountCreateTime=Column{name='accountCreateTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, numberOfDeposits=Column{name='numberOfDeposits', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountNumber=Column{name='accountNumber', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, numberOfWithdrawals=Column{name='numberOfWithdrawals', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, phoneOfNextOfKin=Column{name='phoneOfNextOfKin', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountPayoutDate=Column{name='accountPayoutDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountImageURI=Column{name='accountImageURI', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountId=Column{name='accountId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, lastUpdatedDate=Column{name='lastUpdatedDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, phoneNumber=Column{name='phoneNumber', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountPayoutTime=Column{name='accountPayoutTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, nameOfNextOfKin=Column{name='nameOfNextOfKin', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, lastUpdatedTime=Column{name='lastUpdatedTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountCreateDate=Column{name='accountCreateDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, isPaidOut=Column{name='isPaidOut', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
     Found:
2021-10-05 14:31:50.670 30268-30268/a.a.so69442030javaroomautomigrationsaddcolumns E/AndroidRuntime: TableInfo{name='accounts', columns={accountSavings=Column{name='accountSavings', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountName=Column{name='accountName', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountCreateTime=Column{name='accountCreateTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, numberOfDeposits=Column{name='numberOfDeposits', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountNumber=Column{name='accountNumber', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, numberOfWithdrawals=Column{name='numberOfWithdrawals', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}, accountPayoutDate=Column{name='accountPayoutDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountImageURI=Column{name='accountImageURI', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountId=Column{name='accountId', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}, lastUpdatedDate=Column{name='lastUpdatedDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, phoneNumber=Column{name='phoneNumber', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountPayoutTime=Column{name='accountPayoutTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, lastUpdatedTime=Column{name='lastUpdatedTime', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, accountCreateDate=Column{name='accountCreateDate', type='TEXT', affinity='2', notNull=false, primaryKeyPosition=0, defaultValue='null'}, isPaidOut=Column{name='isPaidOut', type='INTEGER', affinity='3', notNull=true, primaryKeyPosition=0, defaultValue='null'}}, foreignKeys=[], indices=[]}
        at androidx.room.RoomOpenHelper.onUpgrade(RoomOpenHelper.java:103)
  • 即复制遇到的失败
  1. 卸载应用程序并使用 V1 中的代码(从头开始演示整个过程)后重新运行步骤 1-2(重新运行 .addMigrations 注释掉或删除会解决这个问题)

  2. 已应用第 3 步的更改但另外数据库构建中的 .addMigrations 行已被注释掉。

  • 应用程序没有崩溃。数据库检查器在新插入的行中显示修改后的模式和适当的数据(另外 2 个对于两个添加的列有空值):-

解决上述问题所需要做的就是清理项目并重新构建。我想旧模式不知何故仍在内存中,因此崩溃。