You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Room数据库执行迁移并将主键改为id+doorway_id复合主键?

Room复合主键设置与迁移指南

没问题,Room完全支持复合主键的配置,下面我会一步步教你实现这个需求,包括实体类修改和数据库迁移的具体步骤。

一、修改实体类实现复合主键

要把id和doorway_id设为复合主键,你需要调整@Entity注解的配置,同时确保doorway_id字段存在于你的实体类中。具体操作如下:

  1. 移除原_id字段上的@PrimaryKey注解
  2. 在@Entity注解中通过primaryKeys参数指定复合主键的字段名数组
  3. 添加doorway_id字段(如果你的实体类里还没有的话)

修改后的实体类代码示例:

@Entity(tableName = "hours", primaryKeys = {COLUMN_ID, COLUMN_DOORWAY_ID})
public class Hour extends EntityModel implements Parcelable {
    // 定义常量,方便维护
    public static final String COLUMN_ID = "id";
    public static final String COLUMN_DOORWAY_ID = "doorway_id";
    public static final String COLUMN_SERVER_ID = "server_id";
    public static final String COLUMN_ENTRIES = "entries";
    public static final String COLUMN_ALERTS = "alerts";
    public static final String COLUMN_MAG_LEVEL = "mag_level";

    @SerializedName("id")
    @ColumnInfo(name = COLUMN_ID)
    @Expose()
    private int _id;

    // 新增doorway_id字段
    @ColumnInfo(name = COLUMN_DOORWAY_ID)
    private int doorwayId;

    @SerializedName("server_id")
    @ColumnInfo(name = COLUMN_SERVER_ID)
    private int serverId;

    @SerializedName("entries")
    @ColumnInfo(name = COLUMN_ENTRIES)
    @Expose()
    private float entries;

    @SerializedName("alerts")
    @ColumnInfo(name = COLUMN_ALERTS)
    @Expose()
    private float alerts;

    @SerializedName("mag_level")
    @ColumnInfo(name = COLUMN_MAG_LEVEL)
    private float magLevel;

    // 这里省略Parcelable实现和其他构造、getter/setter方法
}

二、数据库迁移实现

因为修改主键属于破坏性结构变更,Room的自动迁移(AutoMigration)无法处理这类操作,所以必须编写手动迁移逻辑。具体步骤如下:

1. 升级数据库版本号

找到你的RoomDatabase子类,把版本号从原来的数值加1,比如原来的version = 1改成version = 2:

@Database(entities = {Hour.class}, version = 2)
public abstract class AppDatabase extends RoomDatabase {
    // 你的DAO定义等内容
}

2. 编写手动迁移类

创建一个Migration实例,在migrate方法中执行SQL语句完成数据迁移。这里需要注意:由于主键变更,我们需要创建临时表来过渡数据,步骤是:

  • 创建和新表结构一致的临时表
  • 将原表的数据迁移到临时表(注意处理新增的doorway_id字段,这里假设你要给它设置默认值0,你可以根据实际需求调整)
  • 删除原表
  • 将临时表重命名为原表名

示例代码:

static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        // 1. 创建临时表,结构和新的hours表一致
        database.execSQL("CREATE TABLE temp_hours (" +
                "id INTEGER NOT NULL, " +
                "doorway_id INTEGER NOT NULL, " +
                "server_id INTEGER, " +
                "entries REAL, " +
                "alerts REAL, " +
                "mag_level REAL, " +
                "PRIMARY KEY(id, doorway_id))");

        // 2. 将原表数据迁移到临时表,这里给doorway_id设默认值0
        database.execSQL("INSERT INTO temp_hours (id, doorway_id, server_id, entries, alerts, mag_level) " +
                "SELECT id, 0 AS doorway_id, server_id, entries, alerts, mag_level FROM hours");

        // 3. 删除原表
        database.execSQL("DROP TABLE hours");

        // 4. 重命名临时表为原表名
        database.execSQL("ALTER TABLE temp_hours RENAME TO hours");
    }
};

3. 将迁移类添加到数据库构建器中

在创建RoomDatabase实例时,添加这个迁移类:

AppDatabase db = Room.databaseBuilder(context.getApplicationContext(),
                AppDatabase.class, "your-db-name")
        .addMigrations(MIGRATION_1_2)
        .build();

三、注意事项

  • 数据填充:迁移时的doorway_id默认值要根据你的业务逻辑调整,如果有其他来源可以获取这个值,要替换掉示例中的0
  • 测试迁移:一定要在测试环境充分验证迁移逻辑,避免数据丢失
  • 开发阶段快捷方式:如果是开发初期,数据不重要,可以使用fallbackToDestructiveMigration(),它会直接删除旧表重建,但生产环境绝对不能用
  • 字段类型一致性:确保临时表的字段类型和新表完全一致,避免迁移时出现类型不兼容问题

内容的提问来源于stack exchange,提问作者asim mehmood

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:15:27