天天看點

android開發之sqlite資料庫更新

上一篇文章中我們借助mysqlitehelper已經建立好了news這張表,這也是demo.db這個資料庫的第一個版本。然而,現在需求發生了變更,我們的軟體除了能看新聞之外,還應該允許使用者評論,是以這時就需要對資料庫進行更新,添加一個comment表。

該怎麼做呢?添加一個comment表的建表語句,然後在oncreate()方法中去執行它?沒錯,這樣的話,兩張表就會同時建立了,代碼如下所示:

android開發之sqlite資料庫更新

public class mysqlitehelper extends sqliteopenhelper {  

    public static final string create_news = "create table news ("  

            + "id integer primary key autoincrement, "  

            + "title text, "  

            + "content text, "  

            + "publishdate integer,"  

            + "commentcount integer)";  

    public static final string create_comment = "create table comment ("  

            + "content text)";  

    public mysqlitehelper(context context, string name, cursorfactory factory,  

            int version) {  

        super(context, name, factory, version);  

    }  

    @override  

    public void oncreate(sqlitedatabase db) {  

        db.execsql(create_news);  

        db.execsql(create_comment);  

    public void onupgrade(sqlitedatabase db, int oldversion, int newversion) {  

}  

這對于第一次安裝我們軟體的使用者來說是完全可以正常工作的,但是如果有的使用者已經安裝過上一版的軟體,那麼很遺憾,comment表是建立不出來的,因為之前資料庫就已經建立過了,oncreate()方法是不會重新執行的。

對于這種情況我們就要用更新的方式來解決了,看到mysqlitehelper構造方法中的第四個參數了嗎,這個就是資料庫版本号的辨別,每當版本号增加的時候就會調用onupgrade()方法,我們隻需要在這裡處理更新表的操作就行了。比較簡單粗暴的方式是将資料庫中現有的所有表都删除掉,然後重新建立,代碼如下所示:

android開發之sqlite資料庫更新

    ......  

        db.execsql("drop table if exists news");  

        oncreate(db);  

可以看到,當資料庫更新的時候,我們先把news表删除掉,然後重新執行了一次oncreate()方法,這樣就保證資料庫中的表都是最新的了。

但是,如果news表中本來已經有資料了,使用這種方式更新的話,就會導緻表中的資料全部丢失,是以這并不是一種值得推薦的更新方法。那麼更好的更新方法是什麼樣的呢?這就稍微有些複雜了,需要在onupgrade()方法中根據版本号加入具體的更新邏輯,我們來試試來吧。比如之前的資料庫版本号是1,那麼在onupgrade()方法中就可以這樣寫:

android開發之sqlite資料庫更新

        switch (oldversion) {  

        case 1:  

            db.execsql(create_comment);  

        default:  

        }  

可以看到,這裡在onupgrade()方法中加入了一個switch判斷,如果oldversion等于1,就再建立一個comment表。現在隻需要調用如下代碼,表就可以得到建立或更新了:

android開發之sqlite資料庫更新

sqliteopenhelper dbhelper = new mysqlitehelper(this, "demo.db", null, 2);  

sqlitedatabase db = dbhelper.getwritabledatabase();  

這裡我們将版本号加1,如果使用者是從舊版本更新過來的,就會新增一個comment表,而如果使用者是直接安裝的新版本,就會在oncreate()方法中把兩個表一起建立了。

ok,現在軟體的第二版本也釋出出去了,可是就在釋出不久之後,突然發現comment表中少了一個字段,我們并沒有記錄評論釋出的時間。沒辦法,隻好在第三版中修複這個問題了,那我們該怎麼樣去添加這個字段呢?主要需要修改comment表的建表語句,以及onupgrade()方法中的邏輯,代碼如下所示:

android開發之sqlite資料庫更新

            + "id integer primary key autoincrement, "   

            + "content text, "   

            + "publishdate integer)";  

        case 2:  

            db.execsql("alter table comment add column publishdate integer");  

可以看到,在建表語句當中我們新增了publishdate這一列,這樣當執行oncreate()方法去建立表的時候,comment表中就會有這一列了。那麼如果是從舊版本更新過來的呢?也沒有問題,我們在onupgrade()方法中已經把更新邏輯都處理好了,當oldversion等于2的時候,會執行alter語句來添加publishdate這一列。現在調用以下代碼來建立或更新資料庫:

android開發之sqlite資料庫更新

sqliteopenhelper dbhelper = new mysqlitehelper(this, "demo.db", null, 3);  

将資料庫版本号設定成3,這樣就可以保證資料庫中的表又是最新的了。

這裡我們要注意一個細節,switch中每一個case的最後都是沒有使用break的,為什麼要這麼做呢?這是為了保證在跨版本更新的時候,每一次的資料庫修改都能被全部執行到。比如使用者目前是從第二版程式更新到第三版程式的,那麼case 2中的邏輯就會執行。而如果使用者是直接從第一版程式更新到第三版程式的,那麼case 1和case 2中的邏輯都會執行。使用這種方式來維護資料庫的更新,不管版本怎樣更新,都可以保證資料庫的表結構是最新的,而且表中的資料也完全不會丢失了。

現在我們已經學習了新增表和新增列這兩種更新方式,那麼如果是某張表中的某一列已經沒有用了,我想把這一列删除掉該怎麼寫呢?很遺憾,sqlite并不支援删除列的功能,對于這情況,多數軟體采取的作法是無視它,反正以後也用不到它了,留着也占不了什麼空間,是以針對于這種需求,确實沒什麼簡單的解決辦法。

這大概就是傳統開發當中更新資料庫表的方式了,雖說能寫出這樣的代碼表示你已經對資料庫的更新操作了解的比較清楚了,但随着版本越來越多,onupgrade()方法中的邏輯也會變得愈發複雜,稍微一不留神,也許就會産生錯誤。是以,如果能讓代碼自動控制更新邏輯,而不是由人工來管理,那就是再好不過了,那麼下面我們就來學習一下怎樣使用litepal來進行更新表的操作。

通過上一篇文章的學習,我們已經知道litepal是一款orm模式的架構了,已經熟悉建立表流程的你,相信對于更新表也一定會輕車熟路的。那麼為了模仿傳統更新表方式中的需求,現在我們也需要建立一張comment表。第一步該怎麼辦呢?相信你也許早就已經猜到了,那當然是先建立一個comment類了,如下所示:

android開發之sqlite資料庫更新

package com.example.databasetest.model;  

public class comment {  

    private int id;  

    private string content;  

    // 自動生成get、set方法   

    ...  

ok,comment類中有id和content這兩個字段,也就意味着comment表中會有id和content這兩列。

接着修改litepal.xml中的配置,在映射清單中新增cooment類,并将版本号加1,如下所示:

android開發之sqlite資料庫更新

<?xml version="1.0" encoding="utf-8"?>  

<litepal>  

    <dbname value="demo" ></dbname>  

    <version value="2" ></version>  

    <list>  

        <mapping class="com.example.databasetest.model.news"></mapping>  

        <mapping class="com.example.databasetest.model.comment"></mapping>  

    </list>  

</litepal>  

沒錯,就是這麼簡單,僅僅兩步,更新的操作就已經完成了,現在我們隻需要操作一下資料庫,comment表就會自動生成了,如下所示:

android開發之sqlite資料庫更新

sqlitedatabase db = connector.getdatabase();  

那麼我們還是通過.table指令來檢視一下結果,如下圖所示:

android開發之sqlite資料庫更新

ok,comment表已經出來了,那麼再通過pragma指令來檢視一下它的表結構吧:

android開發之sqlite資料庫更新

沒有問題,comment表中目前有id和content這兩列,和comment模型類中的字段是保持一緻的。

那麼現在又來了新的需求,需要在comment表中添加一個publishdate列,該怎麼辦呢?不用懷疑,跟着你的直覺走,相信你已經猜到應該在comment類中添加這樣一個字段了吧,如下所示:

android開發之sqlite資料庫更新

    private date publishdate;  

然後呢?剩下的操作已經非常簡單了,隻需要在litepal.xml中對版本号加1就行了,如下所示:

android開發之sqlite資料庫更新

    <version value="3" ></version>  

這樣當我們下一次操作資料庫的時候,publishdate列就應該會自動添加到comment表中。調用connector.getdatabase()方法,然後重新查詢comment表結構,如下所示:

android開發之sqlite資料庫更新

可以看到,publishdate這一列确實已經成功添加到comment表中了。

通過這兩種更新方式的對比,相信你已經充分體會到了使用litepal進行更新表操作所帶來的便利了吧。我們不需要去編寫任何與更新相關的邏輯,也不需要關心程式是從哪個版本更新過來的,唯一要做的就是确定好最新的model結構是什麼樣的,然後将litepal.xml中的版本号加1,所有的更新邏輯就都會自動完成了。litepal确實将資料庫表的更新操作變得極度簡單,使很多程式員可以從維護資料庫表更新的困擾中解脫出來。

然而,litepal卻明顯做到了更好。前面我們提到過關于删除列的問題,最終的結論是無法解決,因為sqlite是不支援删除列的指令的。但是如果使用litepal,這一問題就可以簡單地解決掉,比如說publishdate這一列我們又不想要了,那麼隻需要在comment類中把它删除掉,然後将版本号加1,下次操作資料庫的時候這個列就會不見了。

那麼有的朋友可能會問了,不是說sqlite不支援删除列的指令嗎?那litepal又是怎樣做到的呢?其實litepal并沒有删除任何一列,它隻是先将comment表重命名成一個臨時表,然後根據最新的comment類的結構生成一個新的comment表,再把臨時表中除了publishdate之外的資料複制到新的表中,最後把臨時表删掉。是以,看上去的效果好像是做到了删除列的功能。

這也是使用架構的好處,如果沒有架構的幫助,我們顯然不會為了删除一個列而大廢周章地去寫這麼多的代碼,而使用架構的話,具體的實作邏輯我們已經不用再關心,隻需要控制好模型類的資料結構就可以了。

另外,如果你想删除某一張表的話,操作也很簡單,在litepal.xml中的映射清單中将相應的類删除,表自然也就不存在了。其它的一些更新操作也都是類似的,相信你已經能舉一反三,這裡就不再贅述了。

好了,今天對litepal的介紹就到這裡吧,下篇文章當中我們會學習使用litepal來進行表關聯的操作。