android 混淆了2.1.0版之后升级的默认值迁移要求

hrirmatl  于 2022-12-16  发布在  Android
关注(0)|答案(2)|浏览(128)

在Room 2.1.0中,通常使用以下代码

版本2

@Entity(tableName = "password")
public class Password {
    @ColumnInfo(name = "dummy0")
    @NonNull
    public String dummy0;

}

public class Migration_1_2 extends Migration {
    public Migration_1_2() {
        super(1, 2);
    }

    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE password ADD COLUMN dummy0 TEXT NOT NULL DEFAULT ''");
    }
}

迁移指南来自

是相当混乱的。
注:如果您的数据库方案已有默认值(例如通过ALTER TABLE x ADD COLUMN y INTEGER NOTNULL DEFAULT z添加的默认值),并且您决定通过@ColumnInfo为相同的列定义默认值,则可能需要提供迁移来验证未计入的默认值。有关详情,请参阅:房间迁移
在升级到2.2.3之前,有两种可能性
1.如果迁移运行,则dummy0列具有默认值。
1.或者,如果这是新的DB,我们有一个没有默认值的dummy0列。
当我们从Room 2.1.0升级到Room 2.2.3时,这两种情况下一切都运行良好,无需为删除并重新创建表添加额外的迁移代码。
我们会做进一步测试。

版本3

@Entity(tableName = "password")
public class Password {
    @ColumnInfo(name = "dummy0")
    @NonNull
    public String dummy0;

    @ColumnInfo(name = "dummy1")
    @NonNull
    public String dummy1;
}

public class Migration_2_3 extends Migration {
    public Migration_2_3() {
        super(2, 3);
    }

    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE password ADD COLUMN dummy1 TEXT NOT NULL DEFAULT ''");
    }
}

仍然工作正常。

版本4

@Entity(tableName = "password")
public class Password {
    @ColumnInfo(name = "dummy0")
    @NonNull
    public String dummy0;

    @ColumnInfo(name = "dummy1")
    @NonNull
    public String dummy1;

    @ColumnInfo(name = "dummy2", defaultValue = "")
    @NonNull
    public String dummy2;
}

public class Migration_3_4 extends Migration {
    public Migration_3_4() {
        super(3, 4);
    }

    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE password ADD COLUMN dummy2 TEXT NOT NULL DEFAULT ''");
    }
}

仍然工作正常。
所以,我很困惑?在什么用例下,我们需要删除并重新创建表?

uqzxnwby

uqzxnwby1#

我认为问题不在于添加新列时,而在于对现有列应用/更改/删除默认值时。然后,您可能必须重新创建受影响的表。
例如,如果您更改了:-

@ColumnInfo(name = "dummy0")
@NonNull
public String dummy0;

添加默认值

@ColumnInfo(name = "dummy0", defaultValue = "")
@NonNull
public String dummy0;

那么就会出现模式不匹配,因为预期的模式将具有DEFAULT '',而找到的模式(原始数据库)没有默认编码。

  • 这将需要删除并重新创建表,因为您不能使用ALTER来更改列的属性。

如果在2.2.0之前,您有一个包含默认值的先前非空间生成的模式,并且实体没有相应地更改,那么您将得到冲突,因为预期的模式没有默认值,而找到的模式包含DEFAULT = ''

  • 这将要求各实体作出相应的改变。

示例

假设当前实体为:

@Entity(tableName = "password")
public class Password {
    @PrimaryKey
    public Long id;
    @ColumnInfo(name = "dummy0")
    @NonNull
    public String dummy0;

    @ColumnInfo(name = "dummy1")
    @NonNull
    public String dummy1;
}

那么生成的用于创建表的代码是:-

_db.execSQL("CREATE TABLE IF NOT EXISTS `password` (`id` INTEGER, `dummy0` TEXT NOT NULL, `dummy1` TEXT NOT NULL, PRIMARY KEY(`id`))");
  • 应用程序与上述程序一起运行,以创建数据库。

如果现在是版本2,则更改为:-

@Entity(tableName = "password")
public class Password {
    @PrimaryKey
    public Long id;
    @ColumnInfo(name = "dummy0", defaultValue = "" /*<<<<<<<<<< ADDED */)
    @NonNull
    public String dummy0;

    @ColumnInfo(name = "dummy1")
    @NonNull
    public String dummy1;
}

则生成的代码为:-

_db.execSQL("CREATE TABLE IF NOT EXISTS `password` (`id` INTEGER, `dummy0` TEXT NOT NULL DEFAULT '', `dummy1` TEXT NOT NULL, PRIMARY KEY(`id`))");

运行哑/空迁移(1-2),然后:-

  • 找到的模式(原始数据库)具有:-defaultValue='null'
  • 但预期的模式为:-defaultValue=''''

依据:-

2020-01-11 19:11:15.300 12539-12539/a.so59691979 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: a.so59691979, PID: 12539
    java.lang.RuntimeException: Unable to start activity ComponentInfo{a.so59691979/a.so59691979.MainActivity}: java.lang.IllegalStateException: Migration didn't properly handle: password(a.so59691979.Password).
     Expected:
    TableInfo{name='password', columns={dummy0=Column{name='dummy0', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue=''''}, dummy1=Column{name='dummy1', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
     Found:
    TableInfo{name='password', columns={dummy0=Column{name='dummy0', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, dummy1=Column{name='dummy1', type='TEXT', affinity='2', notNull=true, primaryKeyPosition=0, defaultValue='null'}, id=Column{name='id', type='INTEGER', affinity='3', notNull=false, primaryKeyPosition=1, defaultValue='null'}}, foreignKeys=[], indices=[]}
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3270)

示例修复

使用迁移:-

Migration M1_2 = new Migration(1,2) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {

        // CREATE SQL Copied from the generated Java PasswordDatabase_Impl (name changed)
        final String SQL_CREATE_NEW_PASSWORDTABLE =
                "CREATE TABLE IF NOT EXISTS `password_new` (`id` INTEGER, `dummy0` TEXT NOT NULL DEFAULT '', `dummy1` TEXT NOT NULL, PRIMARY KEY(`id`))";
        database.execSQL(SQL_CREATE_NEW_PASSWORDTABLE);
        database.execSQL("INSERT INTO `password_new` SELECT * FROM `password`");
        database.execSQL("ALTER TABLE `password` RENAME TO `password_old`");
        database.execSQL("ALTER TABLE `password_new` RENAME TO `password`");
        database.execSQL("DROP TABLE IF EXISTS `password_old`");
    }
}

修复了该问题。
编号
以下代码用于生成上述内容:

密码.java

/*
//Original
@Entity(tableName = "password")
public class Password {
    @PrimaryKey
    public Long id;
    @ColumnInfo(name = "dummy0")
    @NonNull
    public String dummy0;

    @ColumnInfo(name = "dummy1")
    @NonNull
    public String dummy1;
}
*/

// New
@Entity(tableName = "password")
public class Password {
    @PrimaryKey
    public Long id;
    @ColumnInfo(name = "dummy0", defaultValue = "" /*<<<<<<<<<< ADDED */)
    @NonNull
    public String dummy0;

    @ColumnInfo(name = "dummy1")
    @NonNull
    public String dummy1;
}
  • 最初使用原件
    密码数据库.java
@Database(version = 2, entities = {Password.class})
public abstract class PasswordDatabase extends RoomDatabase {
}
  • 初始版本为1
    主要活动.java
public class MainActivity extends AppCompatActivity {

    PasswordDatabase passwordDatabase;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        passwordDatabase = Room.databaseBuilder(
                this,
                PasswordDatabase.class,
                "passworddb"
        )
                .allowMainThreadQueries()
                .addMigrations(M1_2)
                .build();
        passwordDatabase.getOpenHelper().getWritableDatabase();
    }

    Migration M1_2 = new Migration(1,2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {

            // CREATE SQL Copied from the generated Java PasswordDatabase_Impl (name changed)
            final String SQL_CREATE_NEW_PASSWORDTABLE =
                    "CREATE TABLE IF NOT EXISTS `password_new` (`id` INTEGER, `dummy0` TEXT NOT NULL DEFAULT '', `dummy1` TEXT NOT NULL, PRIMARY KEY(`id`))";
            database.execSQL(SQL_CREATE_NEW_PASSWORDTABLE);
            database.execSQL("INSERT INTO `password_new` SELECT * FROM `password`");
            database.execSQL("ALTER TABLE `password` RENAME TO `password_old`");
            database.execSQL("ALTER TABLE `password_new` RENAME TO `password`");
            database.execSQL("DROP TABLE IF EXISTS `password_old`");
        }
    };
}
  • 最初,M1_2的主体为空(以便强制错误)
smdncfj3

smdncfj32#

初始版本数据库瓦尔为var saleType:字符串=“买入”
在下一个版本中,我将其更改为var saleType:字符串=“卖出”
那么如何编写迁移代码呢?

val migration1to2: Migration = object : Migration(1, 2) {
            override fun migrate(database: SupportSQLiteDatabase) {
                try {
....

相关问题