我现在在我的应用程序中使用DBflow,我想通过插入一个名为categoryId
的新列来修改表。所以我搜索了一下,我发现我必须编写一个迁移脚本来处理我的表“任务”中的这个修改。问题是我不知道出了什么问题,但我得到一个错误,说没有这样的专栏。
我卸载了应用程序并重新安装它,它运行得很好。问题是我有一些其他用户得到了同样的错误,我不知道是什么导致它。
/**
* This is generated code. Please do not modify */
public final class Task_Table extends ModelAdapter<Task> {
/**
* Primary Key */
public static final Property<String> id = new Property<String>(Task.class, "id");
public static final Property<String> project_id = new Property<String>(Task.class, "project_id");
public static final Property<String> title = new Property<String>(Task.class, "title");
public static final Property<String> description = new Property<String>(Task.class, "description");
public static final Property<String> collection_id = new Property<String>(Task.class, "collection_id");
public static final Property<Integer> priority = new Property<Integer>(Task.class, "priority");
public static final TypeConvertedProperty<Long, Date> created_at = new TypeConvertedProperty<Long, Date>(Task.class, "created_at", true,
new TypeConvertedProperty.TypeConverterGetter() {
@Override
public TypeConverter getTypeConverter(Class<?> modelClass) {
Task_Table adapter = (Task_Table) FlowManager.getInstanceAdapter(modelClass);
return adapter.global_typeConverterDateConverter;
}
});
public static final Property<String> created_by = new Property<String>(Task.class, "created_by");
public static final TypeConvertedProperty<Long, Date> updated_at = new TypeConvertedProperty<Long, Date>(Task.class, "updated_at", true,
new TypeConvertedProperty.TypeConverterGetter() {
@Override
public TypeConverter getTypeConverter(Class<?> modelClass) {
Task_Table adapter = (Task_Table) FlowManager.getInstanceAdapter(modelClass);
return adapter.global_typeConverterDateConverter;
}
});
public static final TypeConvertedProperty<Long, Date> due_date = new TypeConvertedProperty<Long, Date>(Task.class, "due_date", true,
new TypeConvertedProperty.TypeConverterGetter() {
@Override
public TypeConverter getTypeConverter(Class<?> modelClass) {
Task_Table adapter = (Task_Table) FlowManager.getInstanceAdapter(modelClass);
return adapter.global_typeConverterDateConverter;
}
});
public static final TypeConvertedProperty<Long, Date> completed_at = new TypeConvertedProperty<Long, Date>(Task.class, "completed_at", true,
new TypeConvertedProperty.TypeConverterGetter() {
@Override
public TypeConverter getTypeConverter(Class<?> modelClass) {
Task_Table adapter = (Task_Table) FlowManager.getInstanceAdapter(modelClass);
return adapter.global_typeConverterDateConverter;
}
});
public static final Property<String> completed_by = new Property<String>(Task.class, "completed_by");
public static final Property<String> categoryId = new Property<String>(Task.class, "categoryId");
public static final Property<Integer> feed_count = new Property<Integer>(Task.class, "feed_count");
public static final Property<Integer> read_count = new Property<Integer>(Task.class, "read_count");
public static final IProperty[] ALL_COLUMN_PROPERTIES = new IProperty[]{id,project_id,title,description,collection_id,priority,created_at,created_by,updated_at,due_date,completed_at,completed_by,categoryId,feed_count,read_count};
private final DateConverter global_typeConverterDateConverter;
public Task_Table(DatabaseHolder holder, DatabaseDefinition databaseDefinition) {
super(databaseDefinition);
global_typeConverterDateConverter = (DateConverter) holder.getTypeConverterForClass(Date.class);
}
@Override
public final Class<Task> getModelClass() {
return Task.class;
}
@Override
public final String getTableName() {
return "`Task`";
}
@Override
public final Task newInstance() {
return new Task();
}
@Override
public final Property getProperty(String columnName) {
columnName = QueryBuilder.quoteIfNeeded(columnName);
switch ((columnName)) {
case "`id`": {
return id;
}
case "`project_id`": {
return project_id;
}
case "`title`": {
return title;
}
case "`description`": {
return description;
}
case "`collection_id`": {
return collection_id;
}
case "`priority`": {
return priority;
}
case "`created_at`": {
return created_at;
}
case "`created_by`": {
return created_by;
}
case "`updated_at`": {
return updated_at;
}
case "`due_date`": {
return due_date;
}
case "`completed_at`": {
return completed_at;
}
case "`completed_by`": {
return completed_by;
}
case "`categoryId`": {
return categoryId;
}
case "`feed_count`": {
return feed_count;
}
case "`read_count`": {
return read_count;
}
default: {
throw new IllegalArgumentException("Invalid column name passed. Ensure you are calling the correct table's column");
}
}
}
@Override
public final IProperty[] getAllColumnProperties() {
return ALL_COLUMN_PROPERTIES;
}
@Override
public final void bindToInsertValues(ContentValues values, Task model) {
values.put("`id`", model.getId() != null ? model.getId() : null);
values.put("`project_id`", model.getProjectId() != null ? model.getProjectId() : null);
values.put("`title`", model.getTitle() != null ? model.getTitle() : null);
values.put("`description`", model.getDescription() != null ? model.getDescription() : null);
values.put("`collection_id`", model.getCollectionId() != null ? model.getCollectionId() : null);
values.put("`priority`", model.getPriority());
Long refmCreatedAt = model.getCreatedAt() != null ? global_typeConverterDateConverter.getDBValue(model.getCreatedAt()) : null;
values.put("`created_at`", refmCreatedAt != null ? refmCreatedAt : null);
values.put("`created_by`", model.getCreatedById() != null ? model.getCreatedById() : null);
Long refmUpdatedAt = model.getUpdatedAt() != null ? global_typeConverterDateConverter.getDBValue(model.getUpdatedAt()) : null;
values.put("`updated_at`", refmUpdatedAt != null ? refmUpdatedAt : null);
Long refmDueDate = model.getDueDate() != null ? global_typeConverterDateConverter.getDBValue(model.getDueDate()) : null;
values.put("`due_date`", refmDueDate != null ? refmDueDate : null);
Long refmCompletedAt = model.getCompletedAt() != null ? global_typeConverterDateConverter.getDBValue(model.getCompletedAt()) : null;
values.put("`completed_at`", refmCompletedAt != null ? refmCompletedAt : null);
values.put("`completed_by`", model.getCompletedById() != null ? model.getCompletedById() : null);
values.put("`categoryId`", model.getCategoryId() != null ? model.getCategoryId() : null);
values.put("`feed_count`", model.getFeedCount());
values.put("`read_count`", model.getReadCount());
}
@Override
public final void bindToInsertStatement(DatabaseStatement statement, Task model, int start) {
statement.bindStringOrNull(1 + start, model.getId());
statement.bindStringOrNull(2 + start, model.getProjectId());
statement.bindStringOrNull(3 + start, model.getTitle());
statement.bindStringOrNull(4 + start, model.getDescription());
statement.bindStringOrNull(5 + start, model.getCollectionId());
statement.bindLong(6 + start, model.getPriority());
Long refmCreatedAt = model.getCreatedAt() != null ? global_typeConverterDateConverter.getDBValue(model.getCreatedAt()) : null;
statement.bindNumberOrNull(7 + start, refmCreatedAt);
statement.bindStringOrNull(8 + start, model.getCreatedById());
Long refmUpdatedAt = model.getUpdatedAt() != null ? global_typeConverterDateConverter.getDBValue(model.getUpdatedAt()) : null;
statement.bindNumberOrNull(9 + start, refmUpdatedAt);
Long refmDueDate = model.getDueDate() != null ? global_typeConverterDateConverter.getDBValue(model.getDueDate()) : null;
statement.bindNumberOrNull(10 + start, refmDueDate);
Long refmCompletedAt = model.getCompletedAt() != null ? global_typeConverterDateConverter.getDBValue(model.getCompletedAt()) : null;
statement.bindNumberOrNull(11 + start, refmCompletedAt);
statement.bindStringOrNull(12 + start, model.getCompletedById());
statement.bindStringOrNull(13 + start, model.getCategoryId());
statement.bindLong(14 + start, model.getFeedCount());
statement.bindLong(15 + start, model.getReadCount());
}
@Override
public final void bindToUpdateStatement(DatabaseStatement statement, Task model) {
statement.bindStringOrNull(1, model.getId());
statement.bindStringOrNull(2, model.getProjectId());
statement.bindStringOrNull(3, model.getTitle());
statement.bindStringOrNull(4, model.getDescription());
statement.bindStringOrNull(5, model.getCollectionId());
statement.bindLong(6, model.getPriority());
Long refmCreatedAt = model.getCreatedAt() != null ? global_typeConverterDateConverter.getDBValue(model.getCreatedAt()) : null;
statement.bindNumberOrNull(7, refmCreatedAt);
statement.bindStringOrNull(8, model.getCreatedById());
Long refmUpdatedAt = model.getUpdatedAt() != null ? global_typeConverterDateConverter.getDBValue(model.getUpdatedAt()) : null;
statement.bindNumberOrNull(9, refmUpdatedAt);
Long refmDueDate = model.getDueDate() != null ? global_typeConverterDateConverter.getDBValue(model.getDueDate()) : null;
statement.bindNumberOrNull(10, refmDueDate);
Long refmCompletedAt = model.getCompletedAt() != null ? global_typeConverterDateConverter.getDBValue(model.getCompletedAt()) : null;
statement.bindNumberOrNull(11, refmCompletedAt);
statement.bindStringOrNull(12, model.getCompletedById());
statement.bindStringOrNull(13, model.getCategoryId());
statement.bindLong(14, model.getFeedCount());
statement.bindLong(15, model.getReadCount());
statement.bindStringOrNull(16, model.getId());
}
@Override
public final void bindToDeleteStatement(DatabaseStatement statement, Task model) {
statement.bindStringOrNull(1, model.getId());
}
@Override
public final String getCompiledStatementQuery() {
return "INSERT INTO `Task`(`id`,`project_id`,`title`,`description`,`collection_id`,`priority`,`created_at`,`created_by`,`updated_at`,`due_date`,`completed_at`,`completed_by`,`categoryId`,`feed_count`,`read_count`) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
}
@Override
public final String getUpdateStatementQuery() {
return "UPDATE `Task` SET `id`=?,`project_id`=?,`title`=?,`description`=?,`collection_id`=?,`priority`=?,`created_at`=?,`created_by`=?,`updated_at`=?,`due_date`=?,`completed_at`=?,`completed_by`=?,`categoryId`=?,`feed_count`=?,`read_count`=? WHERE `id`=?";
}
@Override
public final String getDeleteStatementQuery() {
return "DELETE FROM `Task` WHERE `id`=?";
}
@Override
public final String getCreationQuery() {
return "CREATE TABLE IF NOT EXISTS `Task`(`id` TEXT, `project_id` TEXT, `title` TEXT, `description` TEXT, `collection_id` TEXT, `priority` INTEGER, `created_at` TEXT, `created_by` TEXT, `updated_at` TEXT, `due_date` TEXT, `completed_at` TEXT, `completed_by` TEXT, `categoryId` TEXT, `feed_count` INTEGER, `read_count` INTEGER, PRIMARY KEY(`id`))";
}
@Override
public final void loadFromCursor(FlowCursor cursor, Task model) {
model.setId(cursor.getStringOrDefault("id"));
model.setProjectId(cursor.getStringOrDefault("project_id"));
model.setTitle(cursor.getStringOrDefault("title"));
model.setDescription(cursor.getStringOrDefault("description"));
model.setCollectionId(cursor.getStringOrDefault("collection_id"));
model.setPriority(cursor.getIntOrDefault("priority"));
int index_created_at = cursor.getColumnIndex("created_at");
if (index_created_at != -1 && !cursor.isNull(index_created_at)) {
model.setCreatedAt(global_typeConverterDateConverter.getModelValue(cursor.getLong(index_created_at)));
} else {
model.setCreatedAt(global_typeConverterDateConverter.getModelValue(null));
}
model.setCreatedById(cursor.getStringOrDefault("created_by"));
int index_updated_at = cursor.getColumnIndex("updated_at");
if (index_updated_at != -1 && !cursor.isNull(index_updated_at)) {
model.setUpdatedAt(global_typeConverterDateConverter.getModelValue(cursor.getLong(index_updated_at)));
} else {
model.setUpdatedAt(global_typeConverterDateConverter.getModelValue(null));
}
int index_due_date = cursor.getColumnIndex("due_date");
if (index_due_date != -1 && !cursor.isNull(index_due_date)) {
model.setDueDate(global_typeConverterDateConverter.getModelValue(cursor.getLong(index_due_date)));
} else {
model.setDueDate(global_typeConverterDateConverter.getModelValue(null));
}
int index_completed_at = cursor.getColumnIndex("completed_at");
if (index_completed_at != -1 && !cursor.isNull(index_completed_at)) {
model.setCompletedAt(global_typeConverterDateConverter.getModelValue(cursor.getLong(index_completed_at)));
} else {
model.setCompletedAt(global_typeConverterDateConverter.getModelValue(null));
}
model.setCompletedById(cursor.getStringOrDefault("completed_by"));
model.setCategoryId(cursor.getStringOrDefault("categoryId"));
model.setFeedCount(cursor.getIntOrDefault("feed_count"));
model.setReadCount(cursor.getIntOrDefault("read_count"));
model.loadPermissions();
model.loadChecklists();
model.loadMarkers();
model.loadAttachments();
}
@Override
public final boolean exists(Task model, DatabaseWrapper wrapper) {
return SQLite.selectCountOf()
.from(Task.class)
.where(getPrimaryConditionClause(model))
.hasData(wrapper);
}
@Override
public final OperatorGroup getPrimaryConditionClause(Task model) {
OperatorGroup clause = OperatorGroup.clause();
clause.and(id.eq(model.getId()));
return clause;
}
@Override
public final boolean save(Task model) {
boolean successful = super.save(model);
if (model.savePermissions() != null) {
ModelAdapter<Permission> adapter = FlowManager.getModelAdapter(Permission.class);
adapter.saveAll(model.savePermissions());
}
if (model.saveChecklists() != null) {
ModelAdapter<Checklist> adapter = FlowManager.getModelAdapter(Checklist.class);
adapter.saveAll(model.saveChecklists());
}
if (model.saveMarkers() != null) {
ModelAdapter<Marker> adapter = FlowManager.getModelAdapter(Marker.class);
adapter.saveAll(model.saveMarkers());
}
if (model.saveAttachments() != null) {
ModelAdapter<Attachment> adapter = FlowManager.getModelAdapter(Attachment.class);
adapter.saveAll(model.saveAttachments());
}
return successful;
}
@Override
public final long insert(Task model) {
long rowId = super.insert(model);
if (model.savePermissions() != null) {
ModelAdapter<Permission> adapter = FlowManager.getModelAdapter(Permission.class);
adapter.insertAll(model.savePermissions());
}
if (model.saveChecklists() != null) {
ModelAdapter<Checklist> adapter = FlowManager.getModelAdapter(Checklist.class);
adapter.insertAll(model.saveChecklists());
}
if (model.saveMarkers() != null) {
ModelAdapter<Marker> adapter = FlowManager.getModelAdapter(Marker.class);
adapter.insertAll(model.saveMarkers());
}
if (model.saveAttachments() != null) {
ModelAdapter<Attachment> adapter = FlowManager.getModelAdapter(Attachment.class);
adapter.insertAll(model.saveAttachments());
}
return rowId;
}
@Override
public final boolean update(Task model) {
boolean successful = super.update(model);
if (model.savePermissions() != null) {
ModelAdapter<Permission> adapter = FlowManager.getModelAdapter(Permission.class);
adapter.updateAll(model.savePermissions());
}
if (model.saveChecklists() != null) {
ModelAdapter<Checklist> adapter = FlowManager.getModelAdapter(Checklist.class);
adapter.updateAll(model.saveChecklists());
}
if (model.saveMarkers() != null) {
ModelAdapter<Marker> adapter = FlowManager.getModelAdapter(Marker.class);
adapter.updateAll(model.saveMarkers());
}
if (model.saveAttachments() != null) {
ModelAdapter<Attachment> adapter = FlowManager.getModelAdapter(Attachment.class);
adapter.updateAll(model.saveAttachments());
}
return successful;
}
@Override
public final boolean save(Task model, DatabaseWrapper wrapper) {
boolean successful = super.save(model, wrapper);
if (model.savePermissions() != null) {
ModelAdapter<Permission> adapter = FlowManager.getModelAdapter(Permission.class);
adapter.saveAll(model.savePermissions(), wrapper);
}
if (model.saveChecklists() != null) {
ModelAdapter<Checklist> adapter = FlowManager.getModelAdapter(Checklist.class);
adapter.saveAll(model.saveChecklists(), wrapper);
}
if (model.saveMarkers() != null) {
ModelAdapter<Marker> adapter = FlowManager.getModelAdapter(Marker.class);
adapter.saveAll(model.saveMarkers(), wrapper);
}
if (model.saveAttachments() != null) {
ModelAdapter<Attachment> adapter = FlowManager.getModelAdapter(Attachment.class);
adapter.saveAll(model.saveAttachments(), wrapper);
}
return successful;
}
@Override
public final long insert(Task model, DatabaseWrapper wrapper) {
long rowId = super.insert(model, wrapper);
if (model.savePermissions() != null) {
ModelAdapter<Permission> adapter = FlowManager.getModelAdapter(Permission.class);
adapter.insertAll(model.savePermissions(), wrapper);
}
if (model.saveChecklists() != null) {
ModelAdapter<Checklist> adapter = FlowManager.getModelAdapter(Checklist.class);
adapter.insertAll(model.saveChecklists(), wrapper);
}
if (model.saveMarkers() != null) {
ModelAdapter<Marker> adapter = FlowManager.getModelAdapter(Marker.class);
adapter.insertAll(model.saveMarkers(), wrapper);
}
if (model.saveAttachments() != null) {
ModelAdapter<Attachment> adapter = FlowManager.getModelAdapter(Attachment.class);
adapter.insertAll(model.saveAttachments(), wrapper);
}
return rowId;
}
@Override
public final boolean update(Task model, DatabaseWrapper wrapper) {
boolean successful = super.update(model, wrapper);
if (model.savePermissions() != null) {
ModelAdapter<Permission> adapter = FlowManager.getModelAdapter(Permission.class);
adapter.updateAll(model.savePermissions(), wrapper);
}
if (model.saveChecklists() != null) {
ModelAdapter<Checklist> adapter = FlowManager.getModelAdapter(Checklist.class);
adapter.updateAll(model.saveChecklists(), wrapper);
}
if (model.saveMarkers() != null) {
ModelAdapter<Marker> adapter = FlowManager.getModelAdapter(Marker.class);
adapter.updateAll(model.saveMarkers(), wrapper);
}
if (model.saveAttachments() != null) {
ModelAdapter<Attachment> adapter = FlowManager.getModelAdapter(Attachment.class);
adapter.updateAll(model.saveAttachments(), wrapper);
}
return successful;
}
}
我设法在其他表中插入其他列,但我没有得到相同的错误。例如,我在DataBase中运行以插入“categoryId”列的迁移脚本与此类似:
@Database(name = ConstructDB.NAME, version = ConstructDB.VERSION)
public class ConstructDB {
public static final String NAME = "construct_v3";
public static final int VERSION = 22;
@Migration(version = 21, database = ConstructDB.class)
public static class Migration21 extends AlterTableMigration<UserProject> {
public Migration21(Class<UserProject> table) {
super(table);
}
@Override
public void onPreMigrate() {
super.onPreMigrate();
addColumn(SQLiteType.TEXT, "load");
}
}
}
这是我通过CrashLytics收到的LogCat:
Fatal Exception: android.database.sqlite.SQLiteException: no such column: categoryId (code 1): , while compiling: SELECT `id`,`project_id`,`title`,`description`,`collection_id`,`priority`,`created_at`,`created_by`,`updated_at`,`due_date`,`completed_at`,`completed_by`,`categoryId`,`feed_count`,`R`.`read_count` FROM `Task` AS `T` LEFT OUTER JOIN `TaskRead` AS `R` ON `id`=`task_id` WHERE (`project_id`='5716f387d07c5a887cf97c4b' AND `completed_at` IS NULL ) ORDER BY `due_date` ASC, `priority` DESC, `created_at` ASC LIMIT 2147483647
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(SQLiteConnection.java)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:898)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:509)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:44)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1346)
at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1285)
at com.raizlabs.android.dbflow.structure.database.AndroidDatabase.rawQuery(AndroidDatabase.java:62)
at com.raizlabs.android.dbflow.sql.queriable.ModelLoader.load(ModelLoader.java:59)
at com.raizlabs.android.dbflow.sql.queriable.ListModelLoader.load(ListModelLoader.java:44)
at com.raizlabs.android.dbflow.sql.queriable.ListModelLoader.load(ListModelLoader.java:15)
at com.raizlabs.android.dbflow.sql.queriable.ModelLoader.load(ModelLoader.java:53)
at com.raizlabs.android.dbflow.sql.queriable.ListModelLoader.load(ListModelLoader.java:37)
at com.raizlabs.android.dbflow.sql.queriable.ListModelLoader.load(ListModelLoader.java:15)
at com.raizlabs.android.dbflow.sql.queriable.ModelLoader.load(ModelLoader.java:36)
at com.raizlabs.android.dbflow.sql.queriable.ListModelLoader.load(ListModelLoader.java:25)
at com.raizlabs.android.dbflow.sql.language.BaseModelQueriable.queryList(BaseModelQueriable.java:61)
at com.raizlabs.android.dbflow.sql.language.Where.queryList(Where.java:236)
at com.construct.v2.db.dao.TaskDao.read(TaskDao.java:39)
at com.construct.v2.providers.TaskProvider.readCached(TaskProvider.java:62)
at com.construct.v2.providers.TaskProvider.read(TaskProvider.java:54)
at com.construct.v2.viewmodel.entities.tasks.TasksViewModel.load(TasksViewModel.java:37)
at com.construct.v2.viewmodel.entities.AbstractEntitiesViewModel.subscribe(AbstractEntitiesViewModel.java:76)
at com.construct.v2.fragments.entities.TasksFragment.onViewCreated(TasksFragment.java:82)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1314)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1528)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1595)
at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:758)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2363)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2149)
at android.support.v4.app.FragmentManagerImpl.optimizeAndExecuteOps(FragmentManager.java:2103)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2013)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:710)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5624)
at java.lang.reflect.Method.invoke(Method.java)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:959)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:754)
答案 0 :(得分:0)
首次在您的设备上安装应用程序后,将创建数据库,如果您更改数据库(更改列,添加列或添加表...),并且您有以前版本的应用程序您的设备,安装新版本后数据库不会更改。因此,如果您想更改数据库,则需要卸载以前版本的应用程序。如果您已将应用程序发布给用户,并且您希望用户不会丢失以前的版本数据,则必须迁移数据库。