本節(jié)例程下載地址:WillFlowDatabast
一敬飒、SQLite簡介
SQLite 是一款輕量級的關(guān)系型數(shù)據(jù)庫邪铲,它的運算速度非常快无拗,占用資源很少带到,通常只需要幾百 K 的內(nèi)存就足夠了,因而特別適合在移動設(shè)備上使用英染。SQLite 不僅支持標準的 SQL 語法揽惹,還遵循了數(shù)據(jù)庫的 ACID 事務(wù)被饿,所以只要你以前使用過其他的關(guān)系型數(shù)據(jù)庫,就可以很快地上手 SQLite搪搏。而 SQLite 又比一般的數(shù)據(jù)庫要簡單得多狭握,它甚至不用設(shè)置用戶名和密碼就可以使用。 Android 正是把這個功能極為強大的數(shù)據(jù)庫嵌入到了系統(tǒng)當中慕嚷,使得本地持久化的功能有了一次質(zhì)的飛躍哥牍。
前面我們所學(xué)的文件存儲和 SharedPreferences 存儲畢竟只適用于去保存一些簡單的數(shù)據(jù)和鍵值對,當需要存儲大量復(fù)雜的關(guān)系型數(shù)據(jù)的時候喝检,你就會發(fā)現(xiàn)以上兩種存儲方式很難應(yīng)付得了嗅辣。比如我們手機的短信程序中可能會有很多個會話,每個會話中又包含了很多條信息內(nèi)容挠说,并且大部分會話還可能各自對應(yīng)了電話簿中的某個聯(lián)系人澡谭。很難想象如何用文件或者 SharedPreferences 來存儲這些數(shù)據(jù)量大、結(jié)構(gòu)性復(fù)雜的數(shù)據(jù)吧损俭?但是使用數(shù)據(jù)庫就可以做得到蛙奖。那么我們就趕快來看一看, Android 中的 SQLite 數(shù)據(jù)庫到底是如何使用的杆兵。
二雁仲、創(chuàng)建數(shù)據(jù)庫
Android 為了讓我們能夠更加方便地管理數(shù)據(jù)庫,專門提供了一個 SQLiteOpenHelper 幫助類琐脏,借助這個類就可以非常簡單地對數(shù)據(jù)庫進行創(chuàng)建和升級攒砖。
首先我們要知道 SQLiteOpenHelper 是一個抽象類,這意味著如果我們想要使用它的話日裙,就需要創(chuàng)建一個自己的幫助類去繼承它吹艇。 SQLiteOpenHelper 中有兩個抽象方法,分別是 onCreate() 和 onUpgrade()昂拂,我們必須在自己的幫助類里面重寫這兩個方法受神,然后分別在這兩個方法中去實現(xiàn)創(chuàng)建、升級數(shù)據(jù)庫的邏輯格侯。
SQLiteOpenHelper 中還有兩個非常重要的實例方法鼻听,getReadableDatabase() 和 getWritableDatabase()。這兩個方法都可以創(chuàng)建或打開一個現(xiàn)有的數(shù)據(jù)庫(如果數(shù)據(jù)庫已存在則直接打開联四,否則創(chuàng)建一個新的數(shù)據(jù)庫)撑碴,并返回一個可對數(shù)據(jù)庫進行讀寫操作的對象。不同的是碎连,當數(shù)據(jù)庫不可寫入的時候(如磁盤空間已滿) getReadableDatabase()方法返回的對象將以只讀的方式去打開數(shù)據(jù)庫灰羽,而 getWritableDatabase()方法則將出現(xiàn)異常驮履。
SQLiteOpenHelper 中有兩個構(gòu)造方法可供重寫鱼辙,一般使用參數(shù)少一點的那個構(gòu)造方法即可廉嚼。這個構(gòu)造方法中接收四個參數(shù):
第一個參數(shù)是 Context,這個沒什么好說的倒戏,必須要有它才能對數(shù)據(jù)庫進行操作怠噪。
第二個參數(shù)是數(shù)據(jù)庫名,創(chuàng)建數(shù)據(jù)庫時使用的就是這里指定的名稱杜跷。
第三個參數(shù)允許我們在查詢數(shù)據(jù)的時候返回一個自定義的 Cursor傍念,一般都是傳入 null。
第四個參數(shù)表示當前數(shù)據(jù)庫的版本號葛闷,可用于對數(shù)據(jù)庫進行升級操作憋槐。
構(gòu)建出 SQLiteOpenHelper 的實例之后,再調(diào)用它的 getReadableDatabase() 或 getWritableDatabase()方法就能夠創(chuàng)建數(shù)據(jù)庫了淑趾,數(shù)據(jù)庫文件會存放在/data/data/<package name>/databases/目錄下阳仔。此時,重寫的 onCreate() 方法也會得到執(zhí)行扣泊,所以通常會在這里去處理一些創(chuàng)建表的邏輯近范。接下來還是讓我們通過例子的方式來更加直觀地體會 SQLiteOpenHelper 的用法吧。
這里我們希望創(chuàng)建一個名為 BookStore.db 的數(shù)據(jù)庫延蟹,然后在這個數(shù)據(jù)庫中新建一張 Book 表评矩,表中有 id( 主鍵)、作者阱飘、價格斥杜、 頁數(shù)和書名等列。創(chuàng)建數(shù)據(jù)庫表當然還是需要用建表語句的俯萌,這里也是要考驗一下我們的 SQL 基本功了果录,Book 表的建表語句如下所示:
create table Book (
id integer primary key autoincrement,
author text,
price real,
pages integer,
name text)
只要你對 SQL 方面的知識稍微有一些了解,上面的建表語句對你來說應(yīng)該都不難咐熙。SQLite 不像其他的數(shù)據(jù)庫擁有眾多繁雜的數(shù)據(jù)類型弱恒,它的數(shù)據(jù)類型很簡單:integer 表示整型,real 表示浮點型棋恼, text 表示文本類型返弹, blob 表示二進制類型。 另外爪飘,上述建表語句中我們還使用了 primary key 將 id 列設(shè)為主鍵义起,并用 autoincrement 關(guān)鍵字表示 id 列是自增長的。然后需要在代碼中去執(zhí)行這條 SQL語句师崎,才能完成創(chuàng)建表的操作默终。
新建 MyDatabaseHelper,代碼如下:
/**
* Created by : WGH.
*/
public class MyDatabaseHelper extends SQLiteOpenHelper {
private Context mContext;
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, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
this.mContext = context;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(CREATE_BOOK);
Toast.makeText(mContext, "創(chuàng)建數(shù)據(jù)庫成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
我們把建表語句定義成了一個字符串常量,然后在 onCreate() 方法中又調(diào)用了 SQLiteDatabase 的 execSQL() 方法去執(zhí)行這條建表語句齐蔽,并彈出一個 Toast 提示創(chuàng)建成功两疚,這樣就可以保證在數(shù)據(jù)庫創(chuàng)建完成的同時還能成功創(chuàng)建 Book 表。
修改 activity_main.xml 中的代碼含滴,如下所示:
<Button
android:id="@+id/buttonDataBase"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="創(chuàng)建數(shù)據(jù)庫"
android:textColor="#0babf5"
android:textSize="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
最后修改 MainActivity 中的代碼诱渤,如下所示:
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper myDatabaseHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.buttonDataBase);
myDatabaseHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
myDatabaseHelper.getWritableDatabase();
}
});
}
}
這里我們在 onCreate() 方法中構(gòu)建了一個 MyDatabaseHelper 對象,并且通過構(gòu)造函數(shù)的參數(shù)將數(shù)據(jù)庫名指定為 BookStore.db谈况,版本號指定為 1勺美,然后在 Create database 按鈕的點擊事件里調(diào)用了 getWritableDatabase() 方法。這樣當?shù)谝淮吸c擊 Create database 按鈕時碑韵,就會檢測到當前程序中并沒有 BookStore.db 這個數(shù)據(jù)庫赡茸,于是會創(chuàng)建該數(shù)據(jù)庫并調(diào)用 MyDatabaseHelper 中的 onCreate() 方法,這樣 Book 表也就得到了創(chuàng)建祝闻,然后會彈出一個 Toast 提示創(chuàng)建成功坛掠。再次點擊 Create database 按鈕時,會發(fā)現(xiàn)此時已經(jīng)存在 BookStore.db 數(shù)據(jù)庫了治筒,因此不會再創(chuàng)建一次屉栓。
編譯運行看效果:
此時 BookStore.db 數(shù)據(jù)庫和 Book 表應(yīng)該都已經(jīng)創(chuàng)建成功了,因為當我們再次點擊按鈕時不會再有 Toast 彈出耸袜∮讯啵可是又回到了之前的那個老問題,怎樣才能證實它們的確是創(chuàng)建成功了堤框?如果還是使用 File Explorer域滥,那么最多你只能看到 databases 目錄下出現(xiàn)了一個 BookStore.db 文件, Book 表是無法通過 File Explorer 看到的蜈抓。因此這次我們準備換一種查看方式启绰,使用 adb shell 來對數(shù)據(jù)庫和表的創(chuàng)建情況進行檢查。
adb 是 Android SDK 中自帶的一個調(diào)試工具沟使, 使用這個工具可以直接對連接在電腦上的手機或模擬器進行調(diào)試操作委可。更詳細的介紹你可以參看這里:。腊嗡。着倾。
打開命令行界面,輸入 adb shell燕少,就會進入到設(shè)備的控制臺卡者,然后使用 cd 命令切換到 /data/data/com.example.databasetest/databases/ 目錄下, 并使用 ls 命令查看到該目錄里的文件:
這個目錄下出現(xiàn)了兩個數(shù)據(jù)庫文件客们,一個正是我們創(chuàng)建的 BookStore.db崇决,而另一個 BookStore.db-journal 則是為了讓數(shù)據(jù)庫能夠支持事務(wù)而產(chǎn)生的臨時日志文件材诽,通常情況下這個文件的大小都是 0 字節(jié)。
接下來我們就要借助 sqlite 命令來打開數(shù)據(jù)庫了恒傻,只需要鍵入 sqlite3岳守,后面加上數(shù)據(jù)庫名即可:
然后就已經(jīng)打開了 BookStore.db 數(shù)據(jù)庫,現(xiàn)在就可以對這個數(shù)據(jù)庫中的表進行管理了碌冶。首先來看一下目前數(shù)據(jù)庫中有哪些表,鍵入.table 命令:
可以看到此時數(shù)據(jù)庫中有兩張表涝缝,android_metadata 表是每個數(shù)據(jù)庫中都會自動生成的扑庞,而另外一張 Book 表就是我們在 MyDatabaseHelper 中創(chuàng)建的了。這里還可以通過 .schema 命令來查看它們的建表語句:
由此證明拒逮, BookStore.db 數(shù)據(jù)庫和 Book 表確實已經(jīng)是創(chuàng)建成功了罐氨。之后鍵入 .exit 或 .quit 命令可以退出數(shù)據(jù)庫的編輯,再鍵入 exit 命令就可以退出設(shè)備控制臺了滩援。
三栅隐、升級數(shù)據(jù)庫
如果你足夠細心,一定會發(fā)現(xiàn) MyDatabaseHelper 中還有一個 onUpgrade() 方法玩徊,它是用于對數(shù)據(jù)庫進行升級的租悄,它在整個數(shù)據(jù)庫的管理工作當中起著非常重要的作用。目前我們已經(jīng)有一張 Book 表用于存放書的各種詳細數(shù)據(jù)恩袱,如果我們想再添加一張 Category 表用于記錄書籍的分類比如: Category 表中有 id(主鍵)泣棋、分類名和分類代碼這幾個列,那么建表語句就可以寫成:
create table Category (
id integer primary key autoincrement,
category_name text,
category_code integer)
將這條建表語句添加到 MyDatabaseHelper 中畔塔,代碼如下:
private static final String CREATE_BOOK = "create table book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)";
private 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, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
this.mContext = context;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(CREATE_BOOK);
sqLiteDatabase.execSQL(CREATE_CATEGORY);
Toast.makeText(mContext, "創(chuàng)建數(shù)據(jù)庫成功", Toast.LENGTH_SHORT).show();
}
其實這個時候我們?nèi)c擊創(chuàng)建數(shù)據(jù)庫按鈕是不能成功的潭辈,因為此時 BookStore.db 數(shù)據(jù)庫已經(jīng)存在了,之后不管我們怎樣點擊創(chuàng)建數(shù)據(jù)庫按鈕澈吨, MyDatabaseHelper 中的 onCreate() 方法都不會再次執(zhí)行把敢,因此新添加的表也就無法得到創(chuàng)建了。所以我們需要運用 SQLiteOpenHelper 的升級功能解決這個問題谅辣。
修改 MyDatabaseHelper 中的代碼修赞,如下所示:
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
sqLiteDatabase.execSQL("drop table if exists Book");
sqLiteDatabase.execSQL("drop table if exists Category");
onCreate(sqLiteDatabase);
}
可以看到,我們在 onUpgrade() 方法中執(zhí)行了兩條 DROP 語句桑阶,如果發(fā)現(xiàn)數(shù)據(jù)庫中已經(jīng)存在 Book 表或 Category 表了榔组,就將這兩張表刪除掉,然后再調(diào)用 onCreate() 方法去重新創(chuàng)建联逻。這里先將已經(jīng)存在的表刪除掉搓扯,是因為如果在創(chuàng)建表時發(fā)現(xiàn)這張表已經(jīng)存在了,就會直接報錯包归。
接下來我們讓 onUpgrade( )方法能夠執(zhí)行:看一下 SQLiteOpenHelper 的構(gòu)造方法里接收的第四個參數(shù)锨推,它表示當前數(shù)據(jù)庫的版本號,之前我們傳入的是 1,現(xiàn)在只要傳入一個比 1 大的數(shù)换可,就可以讓 onUpgrade() 方法得到執(zhí)行了椎椰。
修改 MainActivity 中的代碼,如下所示:
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper myDatabaseHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
myDatabaseHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
...
}
}
這里將數(shù)據(jù)庫版本號指定為 2沾鳄,表示我們對數(shù)據(jù)庫進行升級了】現(xiàn)在重新運行程序,并點擊按鈕译荞,這時就會再次彈出和剛才一樣的創(chuàng)建成功提示瓤的。為了驗證一下 Category 表是不是已經(jīng)創(chuàng)建成功了,我們在 adb shell 中打開 BookStore.db 數(shù)據(jù)庫吞歼,然后鍵入.table 命令圈膏,結(jié)果如圖所示:
由此可以看出, Category 表已經(jīng)創(chuàng)建成功了篙骡,同時也說明我們的升級功能的確起到了作用稽坤。
點此進入:GitHub開源項目“愛閱”。
感謝優(yōu)秀的你跋山涉水看到了這里糯俗,歡迎關(guān)注下讓我們永遠在一起尿褪!