Room数据库迁移实现过程

迁移概述

在Android应用中使用Room数据库时,当数据库结构需要变更(如新增字段、修改字段类型、删除表等)时,需要进行数据库迁移,以确保用户数据不会丢失。

迁移步骤

修改实体类:

在需要变更的实体类中添加、修改或删除字段。

例如,在User实体类中新增一个email字段:

@Entity(tableName = "users")

public class User {

@PrimaryKey

public int id;

public String name;

public int age;

public String email; // 新增字段

}

更新数据库版本号:

在RoomDatabase子类中,将version属性值增加1。

例如,将版本号从1升级到2:

@Database(entities = {User.class}, version = 2)

public abstract class AppDatabase extends RoomDatabase {

// ...

}

定义Migration对象:

创建一个Migration对象,指定起始版本和目标版本,并在migrate方法中编写SQL语句来更新数据库结构。

例如,从版本1迁移到版本2,新增email字段:

static final Migration MIGRATION_1_2 = new Migration(1, 2) {

@Override

public void migrate(@NonNull SupportSQLiteDatabase database) {

database.execSQL("ALTER TABLE users ADD COLUMN email TEXT");

}

};

在RoomDatabase子类中注册迁移:

在构建RoomDatabase实例时,通过addMigrations方法注册迁移对象。

例如:

Room.databaseBuilder(context, AppDatabase.class, "app_database")

.addMigrations(MIGRATION_1_2)

.build();

注意事项

备份数据:在进行数据库迁移前,务必备份用户数据,以防迁移过程中出现意外导致数据丢失。

测试迁移:在发布应用前,务必在测试环境中测试迁移过程,确保迁移能够成功执行且数据不会丢失。

处理复杂迁移:对于复杂的迁移(如修改字段类型、删除表等),可能需要创建临时表、使用事务等策略来确保数据的安全迁移。

1. 数据库迁移的实现步骤

(1) 定义 Migration 对象

创建一个 Migration 对象,指定 起始版本(startVersion) 和 目标版本(endVersion)。

在 migrate() 方法中编写 SQL 语句 来更新数据库结构。

(2) 在 RoomDatabase 子类中添加迁移

在构建 RoomDatabase 时,通过 addMigrations() 方法注册迁移逻辑。

2. 示例代码

(1) 定义 Migration

假设:

版本 1 的数据库只有 users 表(id, name, age)。

版本 2 需要新增 email 列。

java

// 定义迁移:从版本 1 升级到版本 2

static final Migration MIGRATION_1_2 = new Migration(1, 2) {

    @Override

    public void migrate(@NonNull SupportSQLiteDatabase database) {

        // 执行 SQL 语句,新增 email 列

        database.execSQL("ALTER TABLE users ADD COLUMN email TEXT");

    }

};

// 如果还有更复杂的迁移(如版本 2 → 3)

static final Migration MIGRATION_2_3 = new Migration(2, 3) {

    @Override

    public void migrate(@NonNull SupportSQLiteDatabase database) {

        // 例如:新增一个表

        database.execSQL("CREATE TABLE IF NOT EXISTS orders (" +

                "id INTEGER PRIMARY KEY AUTOINCREMENT, " +

                "user_id INTEGER, " +

                "product_name TEXT, " +

                "FOREIGN KEY(user_id) REFERENCES users(id))");

    }

};

(2) 在 RoomDatabase 中注册迁移

java

@Database(entities = {User.class}, version = 3) // 当前数据库版本

public abstract class AppDatabase extends RoomDatabase {

    public abstract UserDao userDao();

    private static volatile AppDatabase INSTANCE;

    public static AppDatabase getInstance(Context context) {

        if (INSTANCE == null) {

            synchronized (AppDatabase.class) {

                if (INSTANCE == null) {

                    INSTANCE = Room.databaseBuilder(

                            context.getApplicationContext(),

                            AppDatabase.class,

                            "app_database" // 数据库名称

                    )

                    .addMigrations(MIGRATION_1_2, MIGRATION_2_3) // 注册迁移

                    .build();

                }

            }

        }

        return INSTANCE;

    }

}

3. 关键点说明

(1) 迁移的触发条件

当 version 变化 时(如从 1 升级到 2),Room 会检查是否提供了对应的 Migration。

如果没有提供迁移,Room 会 默认删除整个数据库并重建(导致数据丢失!)。

(2) 多版本迁移

如果用户直接从 版本 1 升级到版本 3,Room 会 自动应用所有必要的迁移(1→2 和 2→3)。

但如果中间版本缺失(如 1→3 缺少 1→2),会抛出 IllegalStateException。

(3) 破坏性变更(Fallback)

如果迁移太复杂(如删除表、重构数据),可以 强制销毁旧数据库:

java

Room.databaseBuilder(context, AppDatabase.class, "app_database")

    .fallbackToDestructiveMigration() // 无法迁移时直接重建数据库(数据丢失!)

    .build();

或者指定允许的版本范围:

java

Room.databaseBuilder(context, AppDatabase.class, "app_database")

    .fallbackToDestructiveMigrationFrom(2) // 仅当从版本 2 升级时允许销毁重建

    .build();

4. 完整流程示例

(1) 初始数据库(版本 1)

java

@Entity(tableName = "users")

public class User {

    @PrimaryKey

    public int id;

    public String name;

    public int age;

}

@Database(entities = {User.class}, version = 1)

public abstract class AppDatabase extends RoomDatabase { ... }

(2) 升级到版本 2(新增 email 列)

java

// 1. 修改 User 实体类

@Entity(tableName = "users")

public class User {

    @PrimaryKey

    public int id;

    public String name;

    public int age;

    public String email; // 新增字段

}

// 2. 定义迁移

static final Migration MIGRATION_1_2 = new Migration(1, 2) {

    @Override

    public void migrate(SupportSQLiteDatabase database) {

        database.execSQL("ALTER TABLE users ADD COLUMN email TEXT");

    }

};

// 3. 更新数据库版本并注册迁移

@Database(entities = {User.class}, version = 2)

public abstract class AppDatabase extends RoomDatabase { ... }

(3) 升级到版本 3(新增 orders 表)

java

// 1. 定义 Order 实体类

@Entity(tableName = "orders", foreignKeys = @ForeignKey(entity = User.class,

        parentColumns = "id", childColumns = "user_id"))

public class Order {

    @PrimaryKey

    public int id;

    public int userId;

    public String productName;

}

// 2. 修改数据库类

@Database(entities = {User.class, Order.class}, version = 3)

public abstract class AppDatabase extends RoomDatabase {

    public abstract UserDao userDao();

    public abstract OrderDao orderDao(); // 新增 DAO

}

// 3. 定义迁移

static final Migration MIGRATION_2_3 = new Migration(2, 3) {

    @Override

    public void migrate(SupportSQLiteDatabase database) {

        database.execSQL("CREATE TABLE IF NOT EXISTS orders (" +

                "id INTEGER PRIMARY KEY AUTOINCREMENT, " +

                "user_id INTEGER, " +

                "product_name TEXT, " +

                "FOREIGN KEY(user_id) REFERENCES users(id))");

    }

};

// 4. 注册迁移

Room.databaseBuilder(context, AppDatabase.class, "app_database")

    .addMigrations(MIGRATION_1_2, MIGRATION_2_3)

    .build();

5. 测试迁移

在测试时,可以 手动降级数据库版本 来验证迁移逻辑:

java

// 删除旧数据库(模拟用户从旧版本升级)

context.deleteDatabase("app_database");

// 创建版本 1 的数据库

RoomDatabase db = Room.databaseBuilder(context, AppDatabase.class, "app_database")

    .createFromAsset("databases/version1.db") // 可选:从预置数据库文件初始化

    .build();

// 升级到版本 2(触发迁移)

db.close();

AppDatabase upgradedDb = Room.databaseBuilder(context, AppDatabase.class, "app_database")

    .addMigrations(MIGRATION_1_2)

    .build();

6. 总结

关键步骤 代码示例

定义 Migration new Migration(1, 2) { ... }

执行 SQL 修改表结构 database.execSQL("ALTER TABLE users ADD COLUMN email TEXT")

注册迁移 .addMigrations(MIGRATION_1_2)

处理破坏性变更 .fallbackToDestructiveMigration()

通过正确实现迁移,可以确保 用户数据在数据库升级时不会丢失,同时支持复杂的表结构变更。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值