Android Room数据库:添加NOT NULL列报错及列类型修改迁移方法
问题分析与解决方案
异常原因
SQLite 不允许直接给已有数据的表添加 NOT NULL 约束且未指定默认值的列——因为现有行的新列会被自动赋值为 NULL,这直接违反了 NOT NULL 约束,从而触发 SQLiteException。
解决方案一:添加带默认值的非空列
如果业务需要这两列必须为非空,修改迁移代码时给新列指定默认值(比如空字符串),确保现有行的新列值符合约束要求:
static final Migration MIGRATION_2_3 = new Migration(2, 3) { @Override public void migrate(SupportSQLiteDatabase database) { // 添加带默认空字符串的非空列 database.execSQL("ALTER TABLE LearningMaterialChildDetailEntity ADD COLUMN classes TEXT NOT NULL DEFAULT ''"); database.execSQL("ALTER TABLE LearningMaterialChildDetailEntity ADD COLUMN school TEXT NOT NULL DEFAULT ''"); } };
同时同步修改对应的 Entity 实体类,确保字段定义与数据库约束一致:
@Entity(tableName = "LearningMaterialChildDetailEntity") public class LearningMaterialChildDetailEntity { // 其他已有字段... @ColumnInfo(name = "classes", nullable = false) private String classes; @ColumnInfo(name = "school", nullable = false) private String school; // Getter、Setter 方法... }
解决方案二:添加允许为空的 TEXT 列
如果业务允许这两列的值为空,直接去掉 NOT NULL 约束即可:
static final Migration MIGRATION_2_3 = new Migration(2, 3) { @Override public void migrate(SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE LearningMaterialChildDetailEntity ADD COLUMN classes TEXT"); database.execSQL("ALTER TABLE LearningMaterialChildDetailEntity ADD COLUMN school TEXT"); } };
实体类中对应字段可省略 nullable = false(默认即为允许为空):
@Entity(tableName = "LearningMaterialChildDetailEntity") public class LearningMaterialChildDetailEntity { // 其他已有字段... @ColumnInfo(name = "classes") private String classes; @ColumnInfo(name = "school") private String school; // Getter、Setter 方法... }
如何将已有的 TEXT NOT NULL 列改为 TEXT(允许为空)
SQLite 不支持直接通过 ALTER TABLE 修改列的约束,需通过临时表中转实现:
static final Migration MIGRATION_3_4 = new Migration(3, 4) { // 版本号对应实际升级版本 @Override public void migrate(SupportSQLiteDatabase database) { // 1. 创建临时表,复制原表结构但去掉目标列的 NOT NULL 约束 database.execSQL("CREATE TABLE temp_LearningMaterialChildDetailEntity (" + "id INTEGER PRIMARY KEY AUTOINCREMENT," + // 替换为原表实际主键 "other_column1 TEXT," + // 替换为原表其他所有字段 "classes TEXT," + // 去掉 NOT NULL 约束 "school TEXT" + // 去掉 NOT NULL 约束 ")"); // 2. 将原表数据导入临时表 database.execSQL("INSERT INTO temp_LearningMaterialChildDetailEntity SELECT * FROM LearningMaterialChildDetailEntity"); // 3. 删除原表 database.execSQL("DROP TABLE LearningMaterialChildDetailEntity"); // 4. 将临时表重命名为原表名 database.execSQL("ALTER TABLE temp_LearningMaterialChildDetailEntity RENAME TO LearningMaterialChildDetailEntity"); } };
注意:需严格复制原表的所有字段、主键、索引等结构(仅修改目标列的约束),避免数据丢失或结构异常;同时同步修改实体类中对应字段的 nullable 属性为 true。
内容的提问来源于stack exchange,提问作者userVani
相关产品推荐
相关产品推荐

