- 数据库版本升级准备
- 定义新数据库版本号:在Android应用的
SQLiteOpenHelper
子类中,通过DB_VERSION
常量定义新的数据库版本号,例如:
private static final int DB_VERSION = 2;
- 备份原数据库:在应用开始升级操作前,可通过
SQLiteDatabase
的backupHelper
方法备份原数据库文件。例如:
SQLiteDatabase db = getWritableDatabase();
File backupDir = new File(getFilesDir().getParentFile(), "backup");
if (!backupDir.exists()) {
backupDir.mkdirs();
}
File backup = new File(backupDir, "backup.db");
SQLiteDatabase backupDb = SQLiteDatabase.openOrCreateDatabase(backup, null);
db.backupHelper(backupDb, "main", null, null);
backupDb.close();
db.close();
- 数据迁移方案
- 创建新表结构:在
SQLiteOpenHelper
的onUpgrade
方法中,创建新的表结构。例如:
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 2) {
db.execSQL("CREATE TABLE new_table (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)");
}
}
- 数据迁移:从旧表读取数据并插入新表。可使用
Cursor
读取旧表数据,然后通过ContentValues
插入新表。例如:
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 2) {
Cursor cursor = db.query("old_table", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
ContentValues values = new ContentValues();
values.put("name", cursor.getString(cursor.getColumnIndex("old_name_column")));
db.insert("new_table", null, values);
} while (cursor.moveToNext());
}
cursor.close();
db.execSQL("DROP TABLE old_table");
}
}
- 保证数据迁移准确性
- 使用事务:将数据迁移操作放在事务中,确保要么全部成功,要么全部失败。例如:
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 2) {
db.beginTransaction();
try {
Cursor cursor = db.query("old_table", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
ContentValues values = new ContentValues();
values.put("name", cursor.getString(cursor.getColumnIndex("old_name_column")));
db.insert("new_table", null, values);
} while (cursor.moveToNext());
}
cursor.close();
db.execSQL("DROP TABLE old_table");
db.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction();
}
}
}
- 数据校验:在迁移完成后,对比旧表和新表的数据记录数是否一致。例如:
int oldCount = 0;
Cursor oldCursor = db.query("old_table", null, null, null, null, null, null);
if (oldCursor != null) {
oldCount = oldCursor.getCount();
oldCursor.close();
}
int newCount = 0;
Cursor newCursor = db.query("new_table", null, null, null, null, null, null);
if (newCursor != null) {
newCount = newCursor.getCount();
newCursor.close();
}
if (oldCount != newCount) {
// 处理数据不一致情况
}
- 保证数据迁移高效性
ArrayList<ContentValues> valuesList = new ArrayList<>();
Cursor cursor = db.query("old_table", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
ContentValues values = new ContentValues();
values.put("name", cursor.getString(cursor.getColumnIndex("old_name_column")));
valuesList.add(values);
} while (cursor.moveToNext());
}
cursor.close();
db.beginTransaction();
try {
for (ContentValues values : valuesList) {
db.insert("new_table", null, values);
}
db.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction();
}
- 优化查询:在读取旧表数据时,尽量只选择需要的列,避免全表扫描。例如:
Cursor cursor = db.query("old_table", new String[]{"old_name_column"}, null, null, null, null, null);
- 可能遇到的问题及解决办法
- 数据类型不匹配:在迁移过程中,如果旧表和新表的数据类型不一致,可能导致数据丢失或错误。解决办法是在迁移前进行数据类型转换。例如,如果旧表的
id
是TEXT
类型,新表是INTEGER
类型,可在插入新表前将TEXT
转换为INTEGER
:
String oldId = cursor.getString(cursor.getColumnIndex("old_id_column"));
try {
long newId = Long.parseLong(oldId);
values.put("id", newId);
} catch (NumberFormatException e) {
// 处理转换失败情况
}
- 约束冲突:新表可能有不同的约束(如唯一性约束),在插入数据时可能导致冲突。解决办法是在插入前先检查是否会发生冲突,或者在插入时捕获异常并处理。例如:
try {
db.insert("new_table", null, values);
} catch (SQLiteConstraintException e) {
// 处理约束冲突,如更新数据而不是插入
}
- 数据库锁定:在数据迁移过程中,可能会出现数据库锁定的情况,导致应用无响应。解决办法是尽量缩短事务时间,避免长时间占用数据库连接。同时,可以在迁移操作中设置合理的超时机制。例如,在获取数据库连接时设置超时时间:
SQLiteDatabase db = SQLiteDatabase.openDatabase(databasePath, null, SQLiteDatabase.OPEN_READWRITE, null, 5000);
这里的5000
表示5秒的超时时间。