SQLite 是一款輕量級(jí)的關(guān)系型數(shù)據(jù)庫,它的運(yùn)算速度非??欤?占用資源很少,通常只需幾百 K 的內(nèi)存就足夠了,因而特別適合在移動(dòng)設(shè)備上使用。它不僅支持標(biāo)準(zhǔn)的 SQL 語法,還遵循了數(shù)據(jù)庫的 ACID 事務(wù)。這套數(shù)據(jù)庫可是內(nèi)置于 Android 系統(tǒng)中的哦O(∩_∩)O哈哈~
當(dāng)需要存儲(chǔ)大量復(fù)雜的關(guān)系型數(shù)據(jù)時(shí),就要用到關(guān)系型數(shù)據(jù)庫啦。
1 創(chuàng)建數(shù)據(jù)庫
Android 提供了一個(gè) SQLiteOpenHelper 幫助類,借助這個(gè)類可以非常簡(jiǎn)單地對(duì)數(shù)據(jù)庫進(jìn)行創(chuàng)建與升級(jí)。

SQLiteOpenHelper 是一個(gè)抽象類,所以我們需要?jiǎng)?chuàng)建一個(gè)類去繼承它并且重寫它的兩個(gè)抽象方法,即 onCreate() 和 onUpgrade(),去實(shí)現(xiàn)創(chuàng)建、升級(jí)數(shù)據(jù)庫的邏輯。
創(chuàng)建或打開一個(gè)現(xiàn)有的數(shù)據(jù)庫(若已存在則打開,否則創(chuàng)建一個(gè)新庫),并返回一個(gè)可對(duì)數(shù)據(jù)庫進(jìn)行讀寫操作的對(duì)象的實(shí)例方法有兩種(getReadableDatabase、getWriableDatabase),它們之間的區(qū)別是,當(dāng)數(shù)據(jù)庫不可寫入的情況下(如磁盤空間已滿):
| 方法 | 區(qū)別 |
|---|---|
| getReadableDatabase() | 返回的對(duì)象將以只讀的方式去打開數(shù)據(jù)庫。 |
| getWriableDatabase() | 拋出異常。 |
SQLiteOpenHelper 中的一個(gè)構(gòu)造函數(shù)是這樣的:
SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version)
| 參數(shù) | 說明 |
|---|---|
| context | 上下文 |
| name | 數(shù)據(jù)庫名 |
| factory | 自定義游標(biāo),不需要就傳入 null |
| version | 當(dāng)前數(shù)據(jù)庫的版本號(hào),用于升級(jí)數(shù)據(jù)庫 |
構(gòu)建出 SQLiteOpenHelper 的實(shí)例后,再調(diào)用它的 getReadableDatabase() 或 getWritableDatabase() 方法就能夠創(chuàng)建數(shù)據(jù)庫咯,數(shù)據(jù)庫文件會(huì)存放在 /data/data/<package name>/databases/ 的目錄下。 此時(shí),重寫的 onCreate() 方法也會(huì)得到執(zhí)行,可以在這里處理一些創(chuàng)建表的邏輯。
假設(shè)我們需要建立一個(gè)名為 People.db 的數(shù)據(jù)庫,然后在這個(gè)數(shù)據(jù)庫中新建一張 people 表,表中有 id(主鍵)、名字、年齡、體重等字段。建表語句如下:
create table people(
id integer primary key autoincrement,
name text,
age integer,
weight real)
| 數(shù)據(jù)類型 | 說明 |
|---|---|
| integer | 整型 |
| real | 浮點(diǎn)型 |
| text | 文本類型 |
| blob | 二進(jìn)制類型 |
primary key autoincrement:表示設(shè)置某個(gè)字段為主鍵并且自增長(zhǎng)。
PeopleDatabaseHelper 類:
public class PeopleDatabaseHelper extends SQLiteOpenHelper {
/**
* 建表語句
*/
public static final String CREATE_SQL="create table people( id integer primary key autoincrement,name text,age integer,weight real)";
private Context context;
public PeopleDatabaseHelper(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_SQL);
Toast.makeText(context, "建表成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
我們?cè)?onCreate 方法中 使用了 execSQL 來執(zhí)行建表語句 ,這樣就可以在創(chuàng)建數(shù)據(jù)庫之后,創(chuàng)建需要的表。
調(diào)用語句:
final PeopleDatabaseHelper helper=new PeopleDatabaseHelper(this,"People.db",null,1);
helper.getWritableDatabase();
可以使用 adb shell 來查看數(shù)據(jù)庫與表的創(chuàng)建情況。
adb 是 Android SDK 自帶的調(diào)試工具,可以直接對(duì)連接在電腦上的手機(jī)或者模擬器進(jìn)行開發(fā)調(diào)試工作。它的安裝方法請(qǐng)參見 在 Android Device Monitor 的 File Explorer 中,無法打開某些文件夾的解決方法
在命令行界面,使用命令 adb shell 進(jìn)入設(shè)備控制臺(tái)(先使用 adb root 獲得訪問權(quán)限):

進(jìn)入目錄命令的格式為:
cd /data/data/[包名]/databases/
這里為 cd /data/data/net.deniro.android.databasetest/databases/

使用命令 ls 查看當(dāng)前目錄下的文件:

*.db:是我們創(chuàng)建的數(shù)據(jù)庫。
*.db-journal:是為了讓數(shù)據(jù)庫能夠支持事務(wù)所產(chǎn)生的臨時(shí)日志文件,一般情況下,它為 0 字節(jié)。
使用 sqlite3 數(shù)據(jù)庫名 來打開數(shù)據(jù)庫,并使用 .table 命令來查看目前的數(shù)據(jù)庫中有哪些表:

使用命令 .schema 可以查看這些表的創(chuàng)建語句:

鍵入 .exit 或 .quit 就可以退出 sqlite,再次鍵入 .exit 就會(huì)退出設(shè)備控制臺(tái),是不是很簡(jiǎn)單呀O(∩_∩)O哈哈~
2 升級(jí)數(shù)據(jù)庫
可以在繼承自 SQLiteOpenHelper 類的 onUpgrade 方法中,對(duì)數(shù)據(jù)庫進(jìn)行升級(jí)。
假設(shè)我們需要新增一張行業(yè)表,建表語句為:
create table Industry(
id integer primary key autoincrement,
industry_name text,
industry_code integer)
修改原來的代碼:
/**
* 創(chuàng)建行業(yè)表
*/
public static final String CREATE_INDUSTRY_SQL = "create table Industry( id integer primary key autoincrement, industry_name text, industry_code integer)";
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_PEOPLE_SQL);
db.execSQL(CREATE_INDUSTRY_SQL);
Toast.makeText(context, "建表成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
dropTable(db, "People");
dropTable(db, "Industry");
onCreate(db);
}
/**
* 刪除表
*
* @param db
* @param tableName 表名稱
* @return
*/
private void dropTable(SQLiteDatabase db, String tableName) {
db.execSQL("drop table if exists " + tableName);
}
這里首先在 onCreate 方法中,創(chuàng)建行業(yè)表;接著在 onUpgrade 方法中,刪除相應(yīng)的表,并調(diào)用 onCreate,重新創(chuàng)建表結(jié)構(gòu)。
最后在 PeopleDatabaseHelper 的構(gòu)造函數(shù)中,把版本號(hào)(最后一個(gè)參數(shù))改為 2:

創(chuàng)建成功后,可以通過之前介紹的 adb shell 方法查看新創(chuàng)建的表結(jié)構(gòu):

3 新增數(shù)據(jù)
對(duì)數(shù)據(jù)進(jìn)行的操作一般有四種,即 CRUD:添加(Create),查詢(Retrieve),更新(Update),刪除(Delete)。
SQLiteOpenHelper 中的 getReadableDatabase() 或 getWritableDatabase() 方法都會(huì)返回一個(gè) SQLiteDatabase 對(duì)象,借助這個(gè)對(duì)象可以對(duì)數(shù)據(jù)進(jìn)行 CRUD 操作。
SQLiteDatabase 類包含 insert 方法:
public long insert(String table, String nullColumnHack, ContentValues values)
| 參數(shù)名 | 說明 |
|---|---|
| table | 表名 |
| nullColumnHack | 在未指定添加數(shù)據(jù)的情況下給某些可為空的列自動(dòng)賦值,一般傳入 null。 |
| values | 是一個(gè)ContentValues 對(duì)象,它包含一系列的 put() 方法重載,用于向 ContentValues 中添加數(shù)據(jù)(列名與列的內(nèi)容一一對(duì)應(yīng))。 |
新增操作:
SQLiteDatabase db= helper.getWritableDatabase();
//新增
ContentValues values=new ContentValues();
values.put("name","deniro");
values.put("age",22);
values.put("weight",70);
db.insert("People",null,values);
因?yàn)?id 為自增長(zhǎng)的列,所以這里無須賦值。
查看結(jié)果:
select * from People;

4 更新數(shù)據(jù)
SQLiteDatabase 類包含 update 方法:
public int update(String table, ContentValues values, String whereClause, String[] whereArgs)
| 參數(shù)名 | 說明 |
|---|---|
| table | 表名 |
| values | ContentValues 對(duì)象,存放要更新的數(shù)據(jù) |
| whereClause | where SQL 語句(占位符形式) |
| whereArgs | where SQL 語句中的參數(shù),與語句中的占位符一一對(duì)應(yīng) |
調(diào)用代碼:
SQLiteDatabase db= helper.getWritableDatabase();
//修改
ContentValues values=new ContentValues();
values.put("age",24);
db.update("People",values,"name = ?",new String[]{"deniro"});
這里把所有 name = 'deniro' 的數(shù)據(jù)中的 age 都更新為 24。
更新結(jié)果:

5 刪除數(shù)據(jù)
SQLiteDatabase 類中包含 delete 方法來刪除數(shù)據(jù):
public int delete(String table, String whereClause, String[] whereArgs)
| 參數(shù)名 | 說明 |
|---|---|
| table | 表名 |
| whereClause | where SQL 語句(占位符形式)【可選】 |
| whereArgs | where SQL 語句中的參數(shù),與語句中的占位符一一對(duì)應(yīng)【可選】 |
調(diào)用代碼:
SQLiteDatabase db = helper.getWritableDatabase();
//刪除
db.delete("People", "name = ?", new String[]{"deniro"});
執(zhí)行后,在 sqlite 命令中就會(huì)發(fā)現(xiàn),數(shù)據(jù)被刪除咯。
6 查詢數(shù)據(jù)
SQLiteDatabase 中提供了 query() 方法用于查詢數(shù)據(jù)。 這個(gè)方法的參數(shù)非常復(fù)雜,最短的一個(gè)方法重載也需要傳入七個(gè)參數(shù)。

public Cursor query(String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having,
String orderBy)
| 參數(shù)名 | 說明 | 示例 |
|---|---|---|
| table | 表名 | from table_name |
| columns | 列名 | select column1,column2 |
| selection | where 條件 | where column = value |
| selectionArgs | where 條件中的占位符參數(shù)值 | - |
| groupBy | 需要分組的列 | group by column |
| having | 分組條件 | having column = value |
| orderBy | 排序條件 | order by column1,column2 |
返回 Cursor 對(duì)象,查詢到的數(shù)據(jù)可以從這個(gè)對(duì)象中取出。
調(diào)用代碼:
SQLiteDatabase db = helper.getWritableDatabase();
//查詢
Cursor cursor = db.query("People", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
Log.d(TAG, "name: " + cursor.getString(cursor.getColumnIndex("name")));
Log.d(TAG, "age: " + cursor.getInt(cursor.getColumnIndex("age")));
} while (cursor.moveToNext());
}
cursor.close();
得到 Cursor 對(duì)象后,使用 moveToFirst 把數(shù)據(jù)指針移到第一行,然后進(jìn)入循環(huán)。getColumnIndex 獲取某一列在表中的位置索引,然后把這個(gè)索引傳入相應(yīng)類型的取值方法中(比如 int 型,使用 getInt 方法),就可以讀取數(shù)據(jù)啦。最后記得關(guān)閉 cursor。
7 使用原生 SQL 操作數(shù)據(jù)庫
我們也可以直接使用原生 SQL 來完成前面說過的新增、修改與刪除操作,這就是 SQLiteDatabase 類中的 execSQL 方法:
public void execSQL(String sql, Object[] bindArgs)
| 參數(shù)名 | 說明 |
|---|---|
| sql | SQL 語句 |
| bindArgs | 占位符綁定的參數(shù) |
查詢數(shù)據(jù)可以使用 SQLiteDatabase 類中的 rawQuery方法:
public Cursor rawQuery(String sql, String[] selectionArgs)
參數(shù)說明與 execSQL 方法類似,是不是很簡(jiǎn)單呀O(∩_∩)O哈哈~