天天看點

使用SQLiteOpenHelper的正确姿勢SQLiteOpenHelper是什麼SQLiteOpenHelper的幾個誤區SQLiteOpenHelper的小Demo總結

      前段時間寫android用到SQLIteOpenHelper時,踩了一些小坑,仔細思考了一下,踩坑的根源在于,我沒有正确的了解SQLiteOpenHelper這個類。那麼,我們一起來看看SQLiteOpenHelper究竟是個什麼東東,以及如何正确地使用SQLiteOpenHelper,希望可以幫助博友們了解,避免踩到了類似的坑。

      本文的将首先介紹下SQLiteOpenHelper的作用,然後引申出一些在使用SQLiteOpenHelper時需要注意的事項,最後,通過一個小demo示範下SQLiteOpenHelper操作資料庫。

SQLiteOpenHelper是什麼

    望文生義,從名字上看來,至少可以得到三個方面的資訊。第一,和SQLite資料庫有關。第二,和打開一個資料庫有關。第三,這是一個幫助類。

       當然,這些都是從名字上聯想到的,難免有主觀臆斷之嫌,哈哈。我們來看看官方的說法:A helper class to manage database creation and version management. 是以,官方把SQLiteOpenHelper的作用解釋為:一個資料庫建立和版本管理的幫助類。

  引起我注意的是,類名起的和解釋不是很切合。既然是管理的幫助類,并且是管理資料庫的建立和版本,那麼為什麼類名包含"Open"這個單詞呢?為什麼不叫SQLiteManageHelper,或者更準确的,SQLiteCreationAndUpgradeHelper呢?且聽下文分解。

  我們知道,我們可以通過sql來操縱資料庫,做增删改查。但是呢,如果是我們從頭開始寫一遍資料庫邏輯的話,其實不隻sql操作。一個簡單的流程是這樣的:打開資料庫->sql操作資料庫->關閉資料庫。而打開資料庫,又牽涉到了不少細節。比如:

       1.  這個資料庫是否存在?如果不存在,就要考慮先建立資料庫,建立各種資料表。

       2.  這個資料庫是否需要更新,比如目前的資料庫結構已經不能滿足我們的需求了,我們需要更改資料庫的結構,這個時候就要先更新資料庫,更新資料庫的結構。

       3.  這個資料庫是否之前已經打開?一般來說,我們操作資料庫結束之後,需要關閉資料庫,以節約資源。但是如果我們之前打開的資料庫還要繼續使用,這時候再執行打開操作時,直接傳回這個資料庫就行了。

       這些細節實作起來,還是挺繁瑣的,然而這些邏輯都是套路,一招鮮吃遍天,不需要每次都從頭寫一遍。于是,是時候SQLiteOpenHelper出場了。

       SQLiteOpenHelper把打開資料庫的一系列需要考慮到的細節邏輯都封裝了起來,這也是為什麼包含了Open這個單詞的原因了。因為SQLiteOpenHelper的主要工作就是把打開資料庫的邏輯幫我們實作了。

       當然,打開資料庫中涉及到的一些業務方面的邏輯,這是需要我們自己去實作的,SQLiteOpenHelper把這些用相應的接口分離了出來。

       onCreate:  建立資料庫時會調用,我們需要在裡面建立我們的資料表。onCreate隻會調用一次,如果資料庫已經存在,那麼打開資料庫是不會回調onCreate的。

       onUpgrade:  更新資料庫時會調用,我們可以在這裡面做更新操作。關于更新,還有一點需要注意的,後面會提到。

       onDowngrade: 降級資料庫時會調用,既然存在更新的場景,也可能存在降級的場景,比如我們可能覺得新版的需求沒有舊版的好,想要恢複以前的需求,這時候就需要降級處理了。

        onOpen: 打開資料庫時會調用到。和onCreate有所差別的是,onCreate隻在建立資料庫時會調用,而onOpen在每次打開資料庫時都會調用。

        onConfigure:配置資料庫連接配接。比如設定外鍵限制等。

SQLiteOpenHelper的幾個誤區

       引入正題,我們來看看幾個使用SQLiteOpenHelper可能踩到的坑或者誤區。

       誤區一:建立SQLiteOpenHelper對象的時候,會建立資料庫或者連接配接資料庫

       不知道用過SQLiteOpenHelper的朋友們有沒有和我一樣,曾經犯過這樣的錯誤,以為建立SQLiteOpenHelper對象的時候,就會建立相應的資料庫,或者連接配接已經存在的資料庫。為什麼會産生這樣的誤解呢?仔細想了想,根源出在構造函數上。

public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
            DatabaseErrorHandler errorHandler)
           

dbName是資料庫的名字,version是資料庫的版本。既然構造SQLiteOpenHelper的時候,又是傳dbName,又是傳version的,我就想當然的以為這時候會建立資料庫并且連接配接上資料庫了。然後打開源碼發現,什麼都沒發生[看不下去]

public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
            DatabaseErrorHandler errorHandler) {
        if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);

        mContext = context;
        mName = name;
        mFactory = factory;
        mNewVersion = version;
        mErrorHandler = errorHandler;
    }
           

SQLiteOpenHelper的構造函數裡隻是簡單的記錄下傳過來的參數。

      真正建立資料庫的時機是,顯示調用打開資料庫的api才會執行。也就是getReadableDatabase()和getWriteableDatabase()。這兩個方法都是調用了

private SQLiteDatabase getDatabaseLocked(boolean writable)
           

而建立資料庫,配置資料庫,更新資料庫,打開資料庫這些邏輯都是在這個方法裡面實作的。

     誤區二:在onCreate, onOpen等回調裡面調用getReadableDatabase或getWriteableDatabase

     因為前面解釋得很清楚了,getReadableDatabase或getWriteableDatabase是觸發(建立資料庫、打開資料庫、更新資料庫、降級資料庫、配置資料庫)的入口,如果在這些回調裡面,又觸發了getReadableDatabase或getWriteableDatabase的話,你沒看錯,這變成遞歸調用了。

     是以,不能再onCreate, onOpen, onConfigure, onUpgrade, onDowngrade回調裡面調用打開資料庫的方法。

     誤區三:更新資料庫時,隻需要考慮從最近的一個版本更新到最新的版本

     這種做法是錯誤的。舉個栗子。

     假設我們的app一共發了三個版本A,B,C,并且版本A的資料庫version是1,版本B的資料庫version升到了2,版本C的資料庫version升到了3。

     版本B裡面的資料庫更新邏輯是

@Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        // 在student表裡面加了一個age字段
    }
           

     版本C裡面的資料庫更新邏輯是

@Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        // 在student表裡面加了一個hobby字段
    }
           

     這會導緻一個很嚴重的問題。假如使用者從app的版本A更新到版本B再更新到版本C,一切沿着我們的設想,沒有問題。但是如果使用者在收到版本B的更新時,拒絕了更新請求;而收到版本C的更新時,同意了更新。那麼問題來了,student表裡面增加了hobby字段,但少了age字段!因為使用者拒絕了版本B的更新,意味着沒有執行版本B裡面的資料庫更新邏輯。

     正确的做法是這樣的,在版本C裡面,判斷老版本,如果是1,說明是從版本A更新過來的,那麼就應該添加age, hobby字段;如果是2,說明是從版本B更新過來的,那麼隻需要添加hobby字段,因為版本B裡面已經有age字段,不需要添加了。

@Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        if (oldVersion == 1) {
            // 在student表裡面加了age, hobby字段
        } else if (oldVersion == 2) {
            // 在student表裡面加了hobby字段
        }
    }
           

     長話短說, 更新資料庫,一定要考慮以前的所有版本,對每個版本都進行适配。

     當然,有更新的場景,就有降級的場景。邏輯是類似的,降級的時候,要考慮所有的高版本,對每個高本版都進行适配。

SQLiteOpenHelper的小Demo

     做了一個小demo,實作了幾個小功能:

     1.建立資料庫student_db,建立資料表student

     2.添加一行資料到student表

     3.修改一行資料

     4.删除一行資料

     5.按名字查詢student表

     6.更新資料庫,給student表添加一個age字段,預設值是20歲

使用SQLiteOpenHelper的正确姿勢SQLiteOpenHelper是什麼SQLiteOpenHelper的幾個誤區SQLiteOpenHelper的小Demo總結

下面是demo的下載下傳連結,歡迎下載下傳:

Sqlite Demo

總結

    1.SQLiteOpenHelper名字裡包含Open,就是想告訴我們,SQLiteOpenHelper的主要工作就是把打開資料庫的邏輯幫我們實作了。

    2.建立SQLiteOpenHelper對象的時候,不會真正建立資料庫或者連接配接資料庫。調用getReadableDatabase或getWriteableDatabase才會觸發該操作。

    3.更新資料庫,一定要考慮以前的所有版本,對每個版本都進行适配。降級的時候,要考慮所有的高版本,對每個高本版都進行适配。

    4.在onCreate, onOpen等回調不能調用getReadableDatabase或getWriteableDatabase,會産生遞歸調用。

感謝您的耐心閱讀,以上如果有錯誤的地方或者了解有失偏頗,請留言指正,謝謝~~