天天看點

【資料庫】sqlite版本更新、降級

參考:https://www.jianshu.com/p/65923fa3e3dc

1 正常全部使用流程

1.1 定義全局變量

public static SQLiteHelper dbHelper;
public static String folder = "android.xxx.xxx"; // 資料庫儲存位址
public static String file = "database.db"; // 資料庫名稱
public static int DB_VERSION = 1; // 資料庫的版本号
public static SQLiteDatabase db; // 資料庫
           

1.2 初始化資料庫(在MainActivity中或者Services中)

這樣得到的資料庫儲存在公共檔案夾,即使解除安裝APP,資料庫也不會丢失。

static void init_db(Context context) {
    String pathname = GetDir.getDir(Constant.folder);
    File file = new File(pathname, Constant.file);
    try {
        if(!file.exists()){
            file.createNewFile();
            Uri uri = Uri.fromFile(file);
            Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri);
            context.sendBroadcast(intent);
        }
    } catch (IOException e) {
        Log.v("main", "failed1");
        e.printStackTrace();
    }
    try {
        Constant.dbHelper = new SQLiteHelper(context, pathname+"/"+Constant.file, null, Constant.DB_VERSION);
        Constant.db = Constant.dbHelper.getWritableDatabase();    // 調用SQLiteHelper.OnCreate()
        Log.v("main", "new db");
    } catch (IllegalArgumentException e) {
        Log.v("main", "failed2");
        e.printStackTrace();
        Constant.dbHelper.onUpgrade(Constant.db, Constant.DB_VERSION - 1, Constant.DB_VERSION);
    }
}
           

其中

GetDir.getDir

如下:

public class GetDir {
    public static String getDir(String pathname) {
        String sdcardPath = Environment.getExternalStorageDirectory().toString();
        File dir = new File(sdcardPath + File.separator + pathname /*+ File.separator + "Files"*/);
        if (dir.exists()) {
            return dir.toString();
        } else {
            dir.mkdirs();
            return dir.toString();
        }
    }
}
           

1.3 在SQLiteHelper中定義資料庫初始化的步驟,資料庫更新和降級的操作

public class SQLiteHelper extends SQLiteOpenHelper {
	static  final String TAG = "SQLiteHelper";

	public SQLiteHelper(Context context, String name, CursorFactory factory, int version) {
		super(context, name, factory, version);
	}
	
	/**
	 * 建立新表
	 */
	@Override
	public void onCreate(SQLiteDatabase db) {
		Log.v(TAG,"onCreate");
		db.execSQL("CREATE TABLE IF NOT EXISTS mytable (name varchar(15), age varchar(5))");
	}

	@Override
	public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		Log.v(TAG,"onDowngrade");
		if(oldVersion == 3 && newVersion == 2){ // 從3降到2
			Log.i(TAG, "onDowngrade: 從3降到2");
			db.execSQL("create table tmp_mytable as select name, age from mytable"); // 根據舊表建立新表,相當于删除score列
			db.execSQL("drop table mytable");
			db.execSQL("alter table tmp_mytable rename to mytable");
		}
	}

	/**
	 * 當檢測與前一次建立的資料庫版本不一樣時,先删除表再建立新表
	 */
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		Log.v(TAG,"onUpgrade");
		switch (newVersion){
			case 2: { // 給表添加score字段,并賦初值0
				Log.i(TAG, "onUpgrade: newVersion = 2");

				// 方式1
				db.execSQL("alter table mytable add score varchar(5)");
				ContentValues values = new ContentValues();
				values.put("score", "0");
				db.update("mytable ", values, "name!=?", new String[]{""});


				// 方式2
				// 先建立符合要求的臨時表			
				db.execSQL("CREATE TABLE IF NOT EXISTS mytable (name varchar(15), age varchar(5), score varchar(5))");
				
				// 将資料從舊表複制到臨時表
				ContentValues values = new ContentValues();
				values.put("score", "0");
				db.execSQL("INSERT INTO tmp_mytable  (name, age) SELECT name, age from mytable");
				db.update("tmp_mytable", values, "name!=?", new String[]{""}); // 隻要name不為空,都修改score為0

				// 删除舊表
				db.execSQL("DROP TABLE IF EXISTS mytable");

				// 将臨時表重命名為舊表名
				db.execSQL("ALTER TABLE tmp_mytable RENAME TO mytable");
				break;
			}

			case 3: { // 删掉兩個表中的region字段
				Log.i(TAG, "onUpgrade: newVersion = 3");
				db.execSQL("create table tmp_mytable as select name, age from mytable"); //  若寫 where 1 = 2 則隻會複制表結構,不複制内容
				db.execSQL("drop table mytable");
				db.execSQL("alter table tmp_mytable rename to mytable");
				break;
			}
			
			default:
				break;
		}
	}
}
           

2. sqlite版本更新

2.1 方法1 硬更新 不推薦

首先是一種硬更新的方法,删除所有的所有舊表,然後重新建立資料庫中的所有表。這種方法不夠優雅,删除資料存在無法恢複的風險,不推薦這種方式。

@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// 硬更新
		db.execSQL("DROP TABLE IF EXISTS numbers");
		db.execSQL("DROP TABLE IF EXISTS recordInfo");
		onCreate(db);
           

2.2 推薦方法2種如下

/**
	 * 當檢測與前一次建立的資料庫版本不一樣時,先删除表再建立新表
	 */
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		Log.v(TAG,"onUpgrade");
		switch (newVersion){
			case 2: { // 給表添加score字段,并賦初值0
				Log.i(TAG, "onUpgrade: newVersion = 2");

				// 方式1
				// 先檢查是否已經有score字段,如果沒有,才給它更新
				ContentValues values = new ContentValues();
				values.put("score", "0");
				if(!checkColumnExist(db, "mytable", "score")){
					db.execSQL("alter table mytable add score varchar(5)");
					db.update("mytable", values, "name!=?", new String[]{""});
				}


				// 方式2
				// 先建立符合要求的臨時表			
				db.execSQL("CREATE TABLE IF NOT EXISTS mytable (name varchar(15), age varchar(5), score varchar(5))");
				
				// 将資料從舊表複制到臨時表
				ContentValues values = new ContentValues();
				values.put("score", "0");
				db.execSQL("INSERT INTO tmp_mytable  (name, age) SELECT name, age from mytable");
				db.update("tmp_mytable", values, "name!=?", new String[]{""}); // 隻要name不為空,都修改score為0

				// 删除舊表
				db.execSQL("DROP TABLE IF EXISTS mytable");

				// 将臨時表重命名為舊表名
				db.execSQL("ALTER TABLE tmp_mytable RENAME TO mytable");
				break;
			}

			case 3: { // 删掉兩個表中的region字段
				Log.i(TAG, "onUpgrade: newVersion = 3");
				db.execSQL("create table tmp_mytable as select name, age from mytable"); //  若寫 where 1 = 2 則隻會複制表結構,不複制内容
				db.execSQL("drop table mytable");
				db.execSQL("alter table tmp_mytable rename to mytable");
				break;
			}
			
			default:
				break;
		}
	}
}
           

檢查某表列是否存在

/**
 * 檢查某表列是否存在
 * @param db
 * @param tableName 表名
 * @param columnName 列名
 * @return
 */
private boolean checkColumnExist(SQLiteDatabase db, String tableName, String columnName) {
	boolean result = false ;
	Cursor cursor = null ;
	try{
		//查詢一行
		cursor = db.rawQuery( "SELECT * FROM " + tableName + " LIMIT 0", null );
		result = cursor != null && cursor.getColumnIndex(columnName) != -1 ;
	}catch (Exception e){
		Log.e(TAG,"checkColumnExists1..." + e.getMessage()) ;
	}finally{
		if(null != cursor && !cursor.isClosed()){
			cursor.close() ;
		}
	}

	return result ;
}
           

2. 降級

@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
	if(oldVersion == 3 && newVersion == 2){ // 從3降到2
		Log.i(TAG, "onDowngrade: 從3降到2");

		db.execSQL("create table tmp_mytable as select name, age from mytable");
		db.execSQL("drop table mytable");
		db.execSQL("alter table tmp_tb1 rename to mytable");
	}
}