Room数据库迁移:POJO继承与多表映射问题求助
这问题我之前帮朋友解决过,核心原因是Room默认会把父类的所有字段都当成当前Entity表结构的一部分,但你的EventTable是和SmsTable独立关联的,不是继承表结构,所以才会出现“预期有sender、body、date但实际没有”的迁移错误。
接下来分步骤给你解决方案,亲测有效:
第一步:正确定义Sms实体
首先把你的Sms类标记为@Entity,对应SmsTable,字段要和数据库表完全匹配(建议把Date改成long存时间戳,Room处理起来更省心,避免额外写类型转换器):
@Entity(tableName = "SmsTable") public class Sms { @PrimaryKey(autoGenerate = true) public int _id; public String sender; public String body; public long date; // 用long存储Date的时间戳,替代原Date类型 }
第二步:独立定义Event实体(不要继承Sms)
绝对不能让Event继承Sms后直接标记@Entity!Room会把父类Sms的所有字段都塞进Event的表结构预期里,和你现有的EventTable冲突。正确的做法是让Event成为独立实体,通过sms_id和Sms表关联,还可以加上外键约束保证数据一致性:
@Entity( tableName = "EventTable", foreignKeys = @ForeignKey( entity = Sms.class, parentColumns = "_id", childColumns = "sms_id", onDelete = CASCADE // 可选:删除Sms时自动关联删除Event ) ) public class Event { @PrimaryKey(autoGenerate = true) public int _id; public int sms_id; // 关联Sms表的_id字段 public String eventName; public long eventDueDate; }
第三步:用POJO组合Event和关联的Sms数据
如果需要一次性获取Event及其对应的Sms内容,不用手动查两次数据库,写一个组合POJO,用Room的@Embedded和@Relation实现关联查询:
public class EventWithSms { @Embedded // 嵌入Event实体的所有字段 public Event event; @Relation( parentColumn = "sms_id", // Event表的关联字段 entityColumn = "_id" // Sms表的主键 ) public Sms sms; // 关联的Sms对象 }
然后在Dao里写带事务的查询方法,就能一次性拿到关联数据:
@Dao public interface EventDao { @Transaction // 关联查询必须添加事务注解 @Query("SELECT * FROM EventTable") List<EventWithSms> getAllEventsWithSms(); }
第四步:修复迁移逻辑
现在你的实体定义和实际表结构完全匹配了,接下来要写正确的Migration类,让Room识别现有表结构,避免迁移错误。假设你现在是从版本1升级到版本2:
static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override public void migrate(@NonNull SupportSQLiteDatabase database) { // 如果旧数据库已存在SmsTable和EventTable,这里不需要新建表 // 若之前未添加外键约束,可在这里补加: // database.execSQL("ALTER TABLE EventTable ADD CONSTRAINT fk_event_sms FOREIGN KEY(sms_id) REFERENCES SmsTable(_id) ON DELETE CASCADE"); } };
最后在RoomDatabase的注解里指定版本和迁移规则:
@Database(entities = {Sms.class, Event.class}, version = 2, exportSchema = true) public abstract class AppDatabase extends RoomDatabase { public abstract SmsDao smsDao(); public abstract EventDao eventDao(); private static AppDatabase INSTANCE; public static AppDatabase getInstance(Context context) { if (INSTANCE == null) { INSTANCE = Room.databaseBuilder(context.getApplicationContext(), AppDatabase.class, "your_database_name") .addMigrations(MIGRATION_1_2) .build(); } return INSTANCE; } }
为什么之前会出错?
再给你捋一遍:当你让Event extends Sms并标记@Entity时,Room会自动遍历父类Sms的所有字段,把它们加入Event实体的表结构定义中。所以它会检查EventTable是否包含父类的_id、sender、body、date这些字段,但你的实际EventTable只有自己的字段,自然就触发了迁移错误。
按照上面的步骤改完,不仅能解决迁移问题,还能正确实现两张表的关联查询,完全符合你“不能合并表、Sms需单独转为Entity”的要求。
内容的提问来源于stack exchange,提问作者Aalap

