Android數(shù)據(jù)存儲(四)

這篇文章是基于Android數(shù)據(jù)存儲(三)的基礎(chǔ)上進(jìn)行補(bǔ)充的,主要介紹事務(wù)的使用以及改進(jìn)數(shù)據(jù)庫升級的邏輯。

事務(wù)

事務(wù)(Transaction)是并發(fā)控制的基本單位。所謂事務(wù),它是一個操作序列,這些操作要么都執(zhí)行,要么都不執(zhí)行,它是一個不可分割的工作單位。比如你正在進(jìn)行一次轉(zhuǎn)賬操作,銀行會將轉(zhuǎn)賬的金額先從你的賬戶中扣除,然后再向收款方的賬戶中添加等量的金額。看上去好像沒什么問題吧?可是,如果當(dāng)你賬戶中的金額剛剛被扣除,這時由于一些異常原因?qū)е聦Ψ绞湛钍?,這一部分錢就憑空消失了!當(dāng)然銀行肯定已經(jīng)充分考慮到了這種情況,它會保證扣錢和收款的操作要么一起成功,要么都不會成功,而使用的技術(shù)當(dāng)然就是事務(wù)了。

接下來來看一下如何在Android中使用事務(wù)吧,打開上一篇的DatabaseTest項(xiàng)目,在原來的基礎(chǔ)上進(jìn)行修改。比如Book表中的數(shù)據(jù)已經(jīng)很老了,現(xiàn)在需要全部替換成新的數(shù)據(jù),可以先使用delete()方法將Book表中的數(shù)據(jù)先進(jìn)行刪除,然后再調(diào)用insert()方法將新的數(shù)據(jù)添加進(jìn)Book表中。假如在我們成功刪除舊數(shù)據(jù)準(zhǔn)備添加新數(shù)據(jù)的時候,突然出現(xiàn)異常,那么新數(shù)據(jù)就無法添加到Book表中,Book表中什么數(shù)據(jù)都沒有了,這樣肯定是不行的。我們要保證的是刪除舊數(shù)據(jù)和添加數(shù)據(jù)新數(shù)據(jù)要么一起完成,要么繼續(xù)保留原來的舊數(shù)據(jù)。

在布局中添加一個Replace data按鈕,在MainActivity中綁定Button的點(diǎn)擊監(jiān)聽。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    ……
    private MyDatabaseHelper databaseHelper;
    private ContentValues values = new ContentValues();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ……
        databaseHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
    }

    @Override
    public void onClick(View v) {    
        switch (v.getId()) {        
            ……
            case R.id.replaece_data:
                db = databaseHelper.getWritableDatabase();
                db.beginTransaction();//開啟事務(wù)
                try {
                    db.delete("Book", null, null);
                    //拋出異常,讓事務(wù)失敗,看看舊數(shù)據(jù)是否會被刪除
                    if (true) {
                        throw new NullPointerException();
                    }
                    values.clear();
                    values.put("name", "Game of Thrones");
                    values.put("author", "George Martin");
                    values.put("pages", 720);
                    values.put("price", 20.85);
                    db.insert("Book", null, values);
                    db.setTransactionSuccessful();//事務(wù)已經(jīng)執(zhí)行成功
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    db.endTransaction();//結(jié)束事務(wù)
                }
                break;
            default:        
                break;
        }
    }
}

上述代碼就是Android中事務(wù)的標(biāo)準(zhǔn)用法,首先調(diào)用SQLiteDatabase的beginTransaction()方法來開啟一個事務(wù),然后在一個異常捕獲的代碼塊中去執(zhí)行具體的數(shù)據(jù)庫操作,當(dāng)所有的操作都完成之后,調(diào)用setTransactionSuccessful()表示事務(wù)已經(jīng)執(zhí)行成功了,最后在 finally代碼塊中調(diào)用endTransaction()來結(jié)束事務(wù)。

注意觀察,我們在刪除舊數(shù)據(jù)的操作完成后手動拋出了一個NullPointerException,這樣添加新數(shù)據(jù)的代碼就執(zhí)行不到了。不過由于事務(wù)的存在,中途出現(xiàn)異常會導(dǎo)致事務(wù)的失敗,此時舊數(shù)據(jù)應(yīng)該是刪除不掉的,接下來就來驗(yàn)證一下。

現(xiàn)在可以運(yùn)行一下程序并點(diǎn)擊Replacedata按鈕,你會發(fā)現(xiàn),Book表中存在的還是之前的舊數(shù)據(jù)。然后將手動拋出異常的那行代碼去除,再重新運(yùn)行一下程序,此時點(diǎn)擊一下Replace data按鈕就會將Book表中的數(shù)據(jù)替換成新數(shù)據(jù)了。

改進(jìn)數(shù)據(jù)庫升級

上一篇我們也對數(shù)據(jù)庫進(jìn)行了升級,不過對升級的處理有些簡單粗暴了。我們?yōu)榱私o數(shù)據(jù)庫添加一張新的表,在onUpgrade()方法中把已經(jīng)存在的表先刪除了,然后強(qiáng)制執(zhí)行一遍onCreate()方法。你要知道實(shí)際開發(fā)中這樣的處理是不行的。想象以下場景,比如你編寫的某個應(yīng)用已經(jīng)成功上線,并且還擁有了不錯的下載量?,F(xiàn)在由于添加新功能的原因,使得數(shù)據(jù)庫也需要一起升級,然后用戶更新了這個版本之后發(fā)現(xiàn)以前程序中存儲的本地?cái)?shù)據(jù)全部丟失了!那么很遺憾,你的用戶群體可能已經(jīng)流失一大半了。

難道產(chǎn)品發(fā)布出去之后還不能升級數(shù)據(jù)庫了?當(dāng)然不是,其實(shí)只需要進(jìn)行一些合理的控制,就可以保證在升級數(shù)據(jù)庫的時候數(shù)據(jù)并不會丟失了。下面就來學(xué)習(xí)一下如何實(shí)現(xiàn)這樣的功能,每一個數(shù)據(jù)庫版本都會對應(yīng)一個版本號, 當(dāng)指定的數(shù)據(jù)庫版本號大于當(dāng)前數(shù)據(jù)庫版本號的時候, 就會進(jìn)入到onUpgrade()方法中去執(zhí)行更新操作。這里需要為每一個版本號賦予它各自改變的內(nèi)容,然后在onUpgrade()方法中對當(dāng)前數(shù)據(jù)庫的版本號進(jìn)行判斷,再執(zhí)行相應(yīng)的改變就可以了。

下面就來看看如何實(shí)現(xiàn)合理升級數(shù)據(jù)庫的操作,打開MyDatabaseHelper類進(jìn)行修改。假如說第一個版本的數(shù)據(jù)庫只有一張Book表,那么這樣寫就可以實(shí)現(xiàn)了

public class MyDatabaseHelper extends SQLiteOpenHelper {

    private static final String CREATE_BOOK = "create table Book(" +
            "id integer primary key autoincrement," +
            "name text," +
            "author text," +
            "pages integer," +
            "price real)" ;

    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        this.context = context;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        Toast.makeText(context, "Database Created succeeded", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

以上代碼就可以實(shí)現(xiàn)Book表的創(chuàng)建了,假如在第2個版本的數(shù)據(jù)庫中需要加入Category表,那么只需要將代碼這樣修改即可。

public class MyDatabaseHelper extends SQLiteOpenHelper {

    ……
    private static final String CREATE_CATEGORY = "create table Category(" +
        "id integer primary key autoincrement," +
        "category_name text," +
        "category_cade integer)";**
    ……

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);
        Toast.makeText(context, "Database Created succeeded", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion) {
            case 1:
                db.execSQL(CREATE_CATEGORY);
            default:
        }
    }
}

這樣就可以實(shí)現(xiàn)Category表的添加而不影響B(tài)ook表的數(shù)據(jù)了,為什么在onCreate()方法和onUpgrade()方法都完成Category表的創(chuàng)建。原因很簡單,假如你是在APP中的數(shù)據(jù)庫升級為第2個版本之后才注冊的新用戶,那么你一開始創(chuàng)建的數(shù)據(jù)庫就是第2個版本的數(shù)據(jù)庫,那么你在創(chuàng)建數(shù)據(jù)庫的時候就會調(diào)用onCreate()方法,這樣就會同時創(chuàng)建Bookb表和Category表;但是那些一開始就是用這個APP的用戶就不會再執(zhí)行onCreate()方法了,因?yàn)閿?shù)據(jù)庫已經(jīng)存在了,他們只會從第1個版本升級為第2個版本,所以在onUpgrade()中也需要實(shí)現(xiàn)創(chuàng)建Category表,這樣才能老用戶在不丟失數(shù)據(jù)的情況下升級到與新用戶的相同的數(shù)據(jù)庫。

需要注意的是每個case語句最后都不加上break,這樣不管你是從哪個版本升級到最新的數(shù)據(jù)庫,都不會缺失其中每一次的升級。比如說你從第1個版本升級到第4個版本,那么你不僅僅執(zhí)行case1就可以了,你還要執(zhí)行case2和case3才會升級到和目標(biāo)相同的數(shù)據(jù)庫。

如果文章對你有所幫助,那么請您點(diǎn)一下?
由于本人水平有限,如有錯誤,歡迎大家指正。如果你在操作過程中發(fā)現(xiàn)一些沒有講到的錯誤或者問題,歡迎在評論留言,一起探討,共同學(xué)習(xí)進(jìn)步!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容