標籤:
原文連結:http://www.orlion.ga/610/
一、事務
SQLite支援事務,看一下Android如何使用事務:比如 Book表中的資料都已經很老了,現在準備全部廢棄掉替換成新資料,可以先使用delete()方法將Book表中的資料刪除, 然後再使用insert()方法將新的資料添加到表中。我們要保證的是,刪除舊資料和添加新資料的操作必須一起完成,否則就還要繼續保留原來的舊資料。
Button replaceData = (Button) findViewById(R.id.replace_data);replaceData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {SQLiteDatabase db = dbHelper.getWritableDatabase();db.beginTransaction();db.delete("book", null, null);try {if (true) {// 手動拋出異常,讓事務失敗throw new Exception();}ContentValues values = new ContentValues();values.put("name", "book new");values.put("author", "orlion");values.put("pages", 200);values.put("price", 100);db.insert("book", null, values);db.setTransactionSuccessful(); // 事務已經執行成功} catch (Exception e) {e.printStackTrace();} finally {db.endTransaction(); // 結束事務}}});
上述代碼就是Android中事務的標準用法, 首先調用SQLiteDatabase的beginTransaction()方法來開啟一個事務,然後在一個異常捕獲的代碼塊中去執行具體的資料庫操作,當所有的操作都完成之後,調用 setTransactionSuccessful()表示事務已經執行成功了,最後在 finally代碼塊中調用 endTransaction()來結束事務。注意觀察,我們在刪除舊資料的操作完成後手動拋出了一個 NullPointerException,這樣添加新資料的代碼就執行不到了。不過由於事務的存在,中途出現異常會導致事務的失敗,此時舊資料應該是刪除不掉的。
二、升級資料庫的最佳寫法
// 這裡直接複製《第一行代碼原文》
Android入門(十)SQLite建立升級資料庫 一文中升級資料庫的方式是非常粗暴的,為了保證資料庫中的表是最新的,我們只是簡單地在 onUpgrade()方法中刪除掉了當前所有的表,然後強制重新執行了一遍 onCreate()方法。這種方式在產品的開發階段確實可以用,但是當產品真正上線了之後就絕對不行了。
每一個資料庫版本都會對應一個版本號碼, 當指定的資料庫版本號碼大於當前資料庫版本號碼的時候, 就會進入到 onUpgrade()
方法中去執行更新操作。這裡需要為每一個版本號碼賦予它各自改變的內容,然後在onUpgrade()方法中對當前資料庫的版本號碼進行判斷,再執行相應的改變就可以了。
接著就讓我們來類比一個資料庫升級的案例,還是由 MyDatabaseHelper類來對資料庫進行管理。第一版的程式要求非常簡單,只需要建立一張 Book表,MyDatabaseHelper中的代碼如下所示:
public class MyDatabaseHelper extends SQLiteOpenHelper { public static final String CREATE_BOOK = "create table Book (" + "id integer primary key autoincrement, " + "author text, " + "price real, " + "pages integer, " + "name text)"; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }}
不過,幾星期之後又有了新需求,這次需要向資料庫中再添加一張 Category表。於是,修改 MyDatabaseHelper中的代碼,如下所示:
public class MyDatabaseHelper extends SQLiteOpenHelper { public static final String CREATE_BOOK = "create table Book (" + "id integer primary key autoincrement, " + "author text, " + "price real, " + "pages integer, " + "name text)"; public static final String CREATE_CATEGORY = "create table Category (" + "id integer primary key autoincrement, " + "category_name text, " + "category_code integer)"; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); db.execSQL(CREATE_CATEGORY); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { switch (oldVersion) { case 1: db.execSQL(CREATE_CATEGORY); default: } }}
可以看到,在 onCreate()方法裡我們新增了一條建表語句,然後又在 onUpgrade()方法中添加了一個 switch判斷,如果使用者當前資料庫的版本號碼是 1,就只會建立一張 Category表。這樣當使用者是直接安裝的第二版的程式時,就會將兩張表一起建立。而當使用者是使用第二版的程式覆蓋安裝第一版的程式時,就會進入到升級資料庫的操作中,此時由於 Book表已經存在了,因此只需要建立一張 Category表即可。但是沒過多久,新的需求又來了,這次要給 Book表和 Category表之間建立關聯,需要在 Book表中添加一個 category_id的欄位。 再次修改 MyDatabaseHelper中的代碼, 如下所示:
public class MyDatabaseHelper extends SQLiteOpenHelper { public static final String CREATE_BOOK = "create table Book (" + "id integer primary key autoincrement, " + "author text, " + "price real, " + "pages integer, " + "name text, " + "category_id integer)"; public static final String CREATE_CATEGORY = "create table Category (" + "id integer primary key autoincrement, " + "category_name text, " + "category_code integer)"; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); db.execSQL(CREATE_CATEGORY); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { switch (oldVersion) { case 1: db.execSQL(CREATE_CATEGORY); case 2: db.execSQL("alter table Book add column category_id integer"); default: } }}
可以看到,首先我們在 Book表的建表語句中添加了一個 category_id列,這樣當使用者直接安裝第三版的程式時,這個新增的列就已經自動添加成功了。然而,如果使用者之前已經安裝了某一版本的程式,現在需要覆蓋安裝,就會進入到升級資料庫的操作中。在 onUpgrade()方法裡,我們添加了一個新的 case,如果當前資料庫的版本號碼是 2,就會執行 alter命令來為Book表新增一個 category_id列。
這裡請注意一個非常重要的細節,switch中每一個 case的最後都是沒有使用 break的,為什麼要這麼做呢?這是為了保證在跨版本升級的時候, 每一次的資料庫修改都能被全部執行到。比如使用者當前是從第二版程式升級到第三版程式的,那麼 case 2中的邏輯就會執行。而如果使用者是直接從第一版程式升級到第三版程式的,那麼 case 1和 case 2中的邏輯都會執行。使用這種方式來維護資料庫的升級,不管版本怎樣更新,都可以保證資料庫的表結構是最新的,而且表中的資料也完全不會丟失了。
Android入門(十二)SQLite事務、升級資料庫