Android数据库高手秘籍
原文出處:作者:郭霖,http://blog.csdn.net/column/details/android-database-pro.html
升級表
然而大家都知道,創建表只是數據庫操作中最基本的一步而已,我們在一開始創建的表結構,隨著需求的變更,到了后期是極有可能需要修改的。因此,升級表的操作對于任何一個項目也是至關重要的,那么今天我們就一起來學習一下,在Android傳統開發當中升級表的方式
上一篇文章中我們借助MySQLiteHelper已經創建好了news這張表,這也是demo.db這個數據庫的第一個版本。然而,現在需求發生了變更,我們的軟件除了能看新聞之外,還應該允許用戶評論,所以這時就需要對數據庫進行升級,添加一個comment表。
該怎么做呢?添加一個comment表的建表語句,然后在onCreate()方法中去執行它?沒錯,這樣的話,兩張表就會同時創建了,代碼如下所示:
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 (" + "id integer primary key autoincrement, " + "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); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }這對于第一次安裝我們軟件的用戶來說是完全可以正常工作的,但是如果有的用戶已經安裝過上一版的軟件,那么很遺憾,comment表是創建不出來的,因為之前數據庫就已經創建過了,onCreate()方法是不會重新執行的。
對于這種情況我們就要用升級的方式來解決了,看到MySQLiteHelper構造方法中的第四個參數了嗎,這個就是數據庫版本號的標識,每當版本號增加的時候就會調用onUpgrade()方法,我們只需要在這里處理升級表的操作就行了。比較簡單粗暴的方式是將數據庫中現有的所有表都刪除掉,然后重新創建,代碼如下所示:
public class MySQLiteHelper extends SQLiteOpenHelper { ...... @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_NEWS); db.execSQL(CREATE_COMMENT); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("drop table if exists news"); onCreate(db); } }可以看到,當數據庫升級的時候,我們先把news表刪除掉,然后重新執行了一次onCreate()方法,這樣就保證數據庫中的表都是最新的了。
但是,如果news表中本來已經有數據了,使用這種方式升級的話,就會導致表中的數據全部丟失,所以這并不是一種值得推薦的升級方法。那么更好的升級方法是什么樣的呢?這就稍微有些復雜了,需要在onUpgrade()方法中根據版本號加入具體的升級邏輯,我們來試試來吧。比如之前的數據庫版本號是1,那么在onUpgrade()方法中就可以這樣寫:
public class MySQLiteHelper extends SQLiteOpenHelper { ...... @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_NEWS); db.execSQL(CREATE_COMMENT); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { switch (oldVersion) { case 1: db.execSQL(CREATE_COMMENT); default: } } }可以看到,這里在onUpgrade()方法中加入了一個switch判斷,如果oldVersion等于1,就再創建一個comment表。現在只需要調用如下代碼,表就可以得到創建或升級了:
SQLiteOpenHelper dbHelper = new MySQLiteHelper(this, "demo.db", null, 2); SQLiteDatabase db = dbHelper.getWritableDatabase();這里我們將版本號加1,如果用戶是從舊版本升級過來的,就會新增一個comment表,而如果用戶是直接安裝的新版本,就會在onCreate()方法中把兩個表一起創建了。
OK,現在軟件的第二版本也發布出去了,可是就在發布不久之后,突然發現comment表中少了一個字段,我們并沒有記錄評論發布的時間。沒辦法,只好在第三版中修復這個問題了,那我們該怎么樣去添加這個字段呢?主要需要修改comment表的建表語句,以及onUpgrade()方法中的邏輯,代碼如下所示:
public class MySQLiteHelper extends SQLiteOpenHelper { ...... public static final String CREATE_COMMENT = "create table comment (" + "id integer primary key autoincrement, " + "content text, " + "publishdate integer)"; ...... @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { switch (oldVersion) { case 1: db.execSQL(CREATE_COMMENT); break; case 2: db.execSQL("alter table comment add column publishdate integer"); break; default: } } }可以看到,在建表語句當中我們新增了publishdate這一列,這樣當執行onCreate()方法去創建表的時候,comment表中就會有這一列了。那么如果是從舊版本升級過來的呢?也沒有問題,我們在onUpgrade()方法中已經把升級邏輯都處理好了,當oldVersion等于2的時候,會執行alter語句來添加publishdate這一列?,F在調用以下代碼來創建或升級數據庫:
SQLiteOpenHelper dbHelper = new MySQLiteHelper(this, "demo.db", null, 3); SQLiteDatabase db = dbHelper.getWritableDatabase();將數據庫版本號設置成3,這樣就可以保證數據庫中的表又是最新的了
現在我們已經學習了新增表和新增列這兩種升級方式,那么如果是某張表中的某一列已經沒有用了,我想把這一列刪除掉該怎么寫呢?很遺憾,SQLite并不支持刪除列的功能,對于這情況,多數軟件采取的作法是無視它,反正以后也用不到它了,留著也占不了什么空間,所以針對于這種需求,確實沒什么簡單的解決辦法
這大概就是傳統開發當中升級數據庫表的方式了,雖說能寫出這樣的代碼表示你已經對數據庫的升級操作理解的比較清楚了,但隨著版本越來越多,onUpgrade()方法中的邏輯也會變得愈發復雜,稍微一不留神,也許就會產生錯誤。因此,如果能讓代碼自動控制升級邏輯,而不是由人工來管理,那就是再好不過了,那么下面我們就來學習一下怎樣使用LitePal來進行升級表的操作
建立表關聯
關聯關系的基礎知識
喜歡把所有的代碼都寫在一個類里的程序員肯定是個新手。沒錯,任何一個像樣的程序都不可能僅僅只有一個類的,同樣地,任何一個像樣的數據庫也不可能僅僅只有一張表。我們都知道,在面向對象的編程語言中,多個類之間可以相互關聯引用,共同完成某項功能。那么在數據庫當中,多個表之間可以相互關聯嗎?當然可以!只不過表與表之間的關聯關系要比對象之間的關聯關系復雜一些,也更加難懂,但是作為數據庫的基本功,還是應該了解清楚的,那么我們就先來學習一下數據庫表關聯的基礎知識。
表與表之間的關聯關系一共有三種類型,一對一、多對一、和多對多,下面我們分別對這三種類型展開進行討論。
一對一
表示兩個表中的數據必須是一一對應的關系。這種場景其實并不是很常見,我們還是通過例子來直觀地體會一下,例子仍然是在之前文章的基礎上展開的。
現在我們已經創建好了news這張表,里面主要記錄了新聞的標題和內容,那么除了標題和內容之外,有些新聞還可能帶有一些導語和摘要,我們把這兩個字段放在一張introduction表中,作為新聞的簡介。那么很顯然,news表和introduction表就是一對一的關系了,因為一條新聞只能對應一個簡介,一個簡介也只能屬于一條新聞。它們之間的對應關系大概如下圖描述的一樣:
可以看到,News1對應了Introduction2,News2對應了Introduction3,News3對應了Introduction1,但不管怎么樣,它們都是一對一的關系。
那么這種一對一的關系,在編程語言中該怎么體現出來呢?相信熟悉面向對象設計的你,一定很輕松就能想出來吧,只需要在News類中持有一個Introduction類的引用,然后在Introduction類中也持有一個News類的引用,這樣它們之間自然就是一對一的關系了。
沒錯,對象之間的一對一關系非常簡單易懂,那么難點就在于,如何在數據庫表中建立這樣的一對一關系了。由于數據庫并不像面向對象的語言一樣支持相互引用,如果想讓兩張表之間建立一對一的關系,一般就只能通過外鍵的方式來實現了。因此,一對一關系的表結構就可以這樣設計:
請注意,introduction表中有一個news_id列,這是一個外鍵列,里面應該存放一個具體的新聞id,這樣一條introduction就能對應一條news,也就實現一對一的關系了,如下圖所示:
由此我們就能夠看出,id為1的introduction對應著id為2的news,id為2的introduction對應著id為3的news,id為3的introduction對應著id為1的news。需要注意的是,一對一的關系并沒有強制要求外鍵必須加在哪一張表上,你可以在introduction表中加一個news_id作為外鍵,也可以在news表中加一個introduction_id作為外鍵,不管使用哪一種,都可以表示出它們是一對一的關聯關系。
多對一
表示一張表中的數據可以對應另一張表中的多條數據。這種場景比起一對一關系就要常見太多了,在我們平時的開發工作中多對一關系真的是比比皆是。比如說現在我們的數據庫中有一個news表,還有一個comment表,它們兩個之間就是典型的多對一關系,一條新聞可以有很多條評論,但是一條評論只能是屬于一條新聞的。它們的關系如下圖所示:
而這種多對一的關系在編程語言中是非常容易體現出來的,比如Java中就有專門集合類,如List、Set等,使用它們的話就能輕松簡單地在對象之間建立多對一的關系,我們稍后就會看到。那么,這里的難點仍然是在數據庫表中如何建立這樣的多對一關系。現在說難點其實已經不難了,因為前面我們已經學會了一對一關系的建立方法,而多對一也是類似的。沒錯,數據庫表中多對一的關系仍然是通過外鍵來建立的,只不過一對一的時候外鍵加在哪一張表上都可以,但多對一的時候關鍵必須要加在多方的表中。因此,多對一關系的表結構就可以這樣設計:
在comment表中有一個news_id列,這是一個外鍵列,里面應該存放一個具體的新聞id,并且允許多條comment都存放同一個新聞id,這樣一條評論就只能對應一條新聞,但一條新聞卻可以有多條評論,也就實現多對一的關系了,如下圖所示:
由此我們就可以看出,id為1、2、3的三條評論是屬于第一條新聞的,而id為4、5的兩條評論是屬于第二條新聞的。
多對多
表示兩張關聯表中的數據都可以對應另一張表中的多條數據。這種場景也不算是很常見,但比一對一關系要稍微更加常用一些。舉個例子,我們都知道新聞網站是會將新聞進行種類劃分的,這樣用戶就可以選擇自己喜歡的那一類新聞進行瀏覽,比如說網易新聞中就會有頭條、科技、娛樂、手機等等種類。每個種類下面當然都會有許多條新聞,而一條新聞也可能是屬于多個種類的,比如iPhone6發布的新聞既可以屬于手機種類,也可以屬于科技種類,甚至還可以上頭條。因此,新聞和種類之間就是一種多對多的關系,如下圖所示:
可以看到,News1是屬于Category1的,而News2和News3都是既屬于Category1也屬于Category2,如此復雜的關聯關系該如何表示呢?在面向對象的編程語言中一切都是那么的簡單,只需要在News類中使用集合類聲明擁有多個Category,然后在Category類中也使用集合類聲明擁有多個News就可以了,我們稍后就會看到。而難點仍然是留在了數據庫上,兩張表之間如何建立多對多的關聯關系呢,還是用外鍵嗎?肯定不行了,多對多的情況只能是借助中間表來完成了。也就是說,我們需要多建立一張表,這張表沒什么其它作用,就是為了存放news表和category表之間的關聯關系的,如下圖所示:
注意這里我們建立一張名為category_news的中間表,中間表的命名并沒有什么強制性的約束,但一個良好的命名規范可以讓你一眼就明白這張表是用來做什么的。中間表里面只有兩列,而且也只需要有兩列,分別是news表的外鍵和category表的外鍵,在這里存放新聞和種類相應的id,就可以讓它們之間建立關聯關系了,如下圖所示:
由此我們就可以看出,第一條新聞是屬于第一個種類的,而第二和第三條新聞,則既屬于第一個種類,也屬于第二個種類。反過來也可以這樣看,第一個種類下面有第一、第二、第三這三條新聞,而第二個種類下面只有第二、第三這兩條新聞。不管怎么看,多對多的關系都是成立的。
好了,三種關聯關系都講完了,那我們來簡單總結一下吧。雖說上面介紹了花了很大的篇幅講解數據庫的表關聯知識,但其實最后的結論是非常簡單的,大家可以當成口訣一樣背下來。即一對一關聯的實現方式是用外鍵,多對一關聯的實現方式也是用外鍵,多對多關聯的實現方式是用中間表。記下了這個口訣,在很多數據庫設計的時候,你都可以發揮得更加游刃有余
ORM
ORM,對象關系映射模式,那么什么是對象關系映射呢?簡單點說,我們使用的編程語言是面向對象語言,而我們使用的數據庫則是關系型數據庫,那么將面向對象的語言和面向關系的數據庫之間建立一種映射關系,這就是對象關系映射了。
但是我們為什么要使用對象關系映射模式呢?這主要是因為大多數的程序員都很擅長面向對象編程,但其中只有少部分的人才比較精通關系型數據庫。而且數據庫的SQL語言晦澀難懂,就算你很精通它,恐怕也不喜歡經常在代碼中去寫它吧?而對象關系映射模式則很好地解決了這個問題,它允許我們使用面向對象的方式來操作數據庫,從而可以從晦澀難懂的SQL語言中解脫出來。
那么接下來我們就看一看LitePal中是如何建表的吧。根據對象關系映射模式的理念,每一張表都應該對應一個模型(Model),也就是說,如果我們想要建一張news表,就應該有一個對應的News模型類。新建一個News類,如下所示:
package com.example.databasetest.model; public class News { }然后,表中的每一列其實就是對應了模型類中的一個字段,比如news表中有id、title、content、publishdate、commentcount這幾個列,那么在News類中就也應該有這幾個字段,代碼如下所示:
public class News { private int id; private String title; private String content; private Date publishDate; private int commentCount; // 自動生成get、set方法 ... }其中id這個字段可寫可不寫,因為即使不寫這個字段,LitePal也會在表中自動生成一個id列,畢竟每張表都一定要有主鍵的嘛
總結
以上是生活随笔為你收集整理的Android数据库高手秘籍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaScript 知识图谱
- 下一篇: 登录和oauth机制