本文目錄:
1. 創(chuàng)建數(shù)據(jù)庫
2. 升級數(shù)據(jù)庫
3. 添加數(shù)據(jù)
4. 更新數(shù)據(jù)
5. 刪除數(shù)據(jù)
6. 查詢數(shù)據(jù)
分割線
(本文原理內(nèi)容來自《Android第一行代碼(第二版)》)
介紹:
SQLite是一款輕量級的關系型數(shù)據(jù)庫短纵,它的運算速度非城玻快伪冰,占用源很少脚作,通常只需要幾百KB的內(nèi)存就足夠了,因而特別適合在移動設備上使用役听。
SQLite不僅支持標準的SQL語法羔砾,還遵循數(shù)據(jù)庫的ACID事務躏救,所以只要你以前使用過其他的關系型數(shù)據(jù)庫,就可以很快地上手 SQLite肌访。
SQLite比一般的數(shù)據(jù)庫要簡單得多找默,它甚至不用設置用戶名和密碼就可以使用。
Android正是把這個功能極為強大的數(shù)據(jù)庫嵌入到了系統(tǒng)當中吼驶,使得本地持久化的功能有了一次質(zhì)的飛躍惩激。
1. 創(chuàng)建數(shù)據(jù)庫
Android提供了一個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
中有兩個構造方法可供重寫,一般使用參數(shù)少的那個構造方法即可膏秫。
這個構造方法中接收4個參數(shù):
第一個參數(shù)
是 Context右遭,必須要有它才能對數(shù)據(jù)庫進行操作。
第二個參數(shù)
是數(shù)據(jù)庫名缤削,創(chuàng)建數(shù)據(jù)庫時使用的就是這里指定的名稱窘哈。
第三個參數(shù)
允許我們在查詢數(shù)據(jù)的時候返回一個自定義的 Cursor,一般都是傳入 null亭敢。
第四個參數(shù)
表示當前數(shù)據(jù)庫的版本號滚婉,可用于對數(shù)據(jù)庫進行升級操作。
構建出SQLiteOpenHelper
的實例之后帅刀,再調(diào)用它的 getReadableDatabase()
或 getWritableDatabase()
方法就能夠創(chuàng)建數(shù)據(jù)庫了让腹,數(shù)據(jù)庫文件會存放在/data/data/<package name>/databases/
日錄下远剩。此時重寫的 onCreate()
方法也會得到執(zhí)行,所以通常會在這里去處理一些創(chuàng)建表的邏輯骇窍。
Demo:
我們希望創(chuàng)建一個名為
BookStore
的數(shù)據(jù)庫瓜晤,并在這個庫中創(chuàng)建一張Book
表,表中有id腹纳、作者痢掠、價格、頁數(shù)和書名只估。
(SQLite中integer
代表整型,real
表示浮點型着绷,text
表示文本類型蛔钙,blob
表示二進制類型)
- 新建MyDatabaseHelper類繼承自SQLiteOpenHelper
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)";
private Context mContext;
public MyDatabaseHelper(Context context, String name,SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
這里將建表語句定義為字符串常量,并在onCreate()
方法中調(diào)用了execSQL()
方法執(zhí)行建表語句荠医。
- activity_main.xml文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"/>
</LinearLayout>
布局里只加入了一個按鈕吁脱,用于創(chuàng)建數(shù)據(jù)庫
- MainActivity代碼
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
//創(chuàng)建數(shù)據(jù)
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dbHelper.getWritableDatabase();
}
});
}
}
這里我們在onCreate()
方法中構建了一個MyDatabaseHelper
對象,并且通過構造函數(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
表已經(jīng)創(chuàng)建成功了废封,因為當你再次點擊Create database
按鈕時,不會有Toast
彈出丧蘸。
2. 升級數(shù)據(jù)庫
現(xiàn)在已經(jīng)有了一個Book表用于存放書的各種詳細數(shù)據(jù)漂洋,如果想再添加一張Category表用于記錄圖書的分類,怎么辦呢力喷?
- 在MyDatabaseHelper中添加語句:
此時運行程序刽漂,Category
表并沒有創(chuàng)建成功,因為之前我們的BookStore.db數(shù)據(jù)庫已經(jīng)存在了弟孟,不管怎樣點擊Create database按鈕爽冕,MyDatabaseHelper中的onCreate()方法都不會執(zhí)行,也就無法創(chuàng)建新表了披蕉。
解決這個問題就要用到升級數(shù)據(jù)庫
功能
- 在MyDatabaseHelper的onUpgrade()函數(shù)中加入以下語句
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
- 現(xiàn)在我們需要讓onUpgrade()方法得到執(zhí)行颈畸,修改SQLiteOpenHelper構造方法的第四個參數(shù)乌奇,之前設置的1,現(xiàn)在傳入一個大于1的值眯娱,表示我們對數(shù)據(jù)庫進行升級了礁苗。
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
//創(chuàng)建數(shù)據(jù)
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dbHelper.getWritableDatabase();
}
});
}
}
重新運行程序,Category表就創(chuàng)建成功了徙缴。
3. 添加數(shù)據(jù)
SQLiteDatabase中提供了一個insert()
方法试伙,用于添加數(shù)據(jù)。
它接受3個參數(shù)
第一個參數(shù)
:表名
第二個參數(shù)
:用于在未指定添加數(shù)據(jù)的情況下給某些可為空的列自動賦值NULL于样,我們一般直接傳入null
第三個參數(shù)
:一個ContentValues
對象疏叨,它提供了一系列put()方法重載,用于向ContentValues中添加數(shù)據(jù)穿剖,只需將表中的每個列名以及對應的數(shù)據(jù)傳入即可蚤蔓。
Demo:
- activity_main.xml中加入Add data按鈕控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"/>
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add data"/>
</LinearLayout>
- 更改MainActivity代碼,onCreate()中加入代碼
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
//創(chuàng)建數(shù)據(jù)
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dbHelper.getWritableDatabase();
}
});
//添加數(shù)據(jù)
Button addData = (Button) findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
// 開始組裝第一條數(shù)據(jù)
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
db.insert("Book", null, values); // 插入第一條數(shù)據(jù)
values.clear();
// 開始組裝第二條數(shù)據(jù)
values.put("name", "The Lost Symbol");
values.put("author", "Dan Brown");
values.put("pages", 510);
values.put("price", 19.95);
db.insert("Book", null, values); // 插入第二條數(shù)據(jù)
}
});
}
}
在添加數(shù)據(jù)按鈕的點擊事件里面糊余,我們先獲取到了SQLiteDatabase對象秀又,然后使用ContentValues
來對要添加的數(shù)據(jù)進行組裝。
這里只對Book表里其中四列的數(shù)據(jù)進行了組裝贬芥,id那一列沒并沒給它賦值吐辙。這是因為在前面創(chuàng)建表的時候,我們就將id列設置為自增長了蘸劈,它的值會在入庫的時候自動生成昏苏,所以不需要手動給它賦值了。
接下來調(diào)用了insert()
方法將數(shù)據(jù)添加到表當中威沫,注意這里我們實際上添加了兩條數(shù)據(jù)捷雕,上述代碼中使用 Contentvalues分別組裝了兩次不同的內(nèi)容,并調(diào)用了兩次 insert()方法壹甥。
重新運行程序救巷,點擊ADD DATA
按鈕便可在表中添加數(shù)據(jù)了
4. 更新數(shù)據(jù)
關于更新數(shù)據(jù),SQLiteDatabase中提供了一個update()
方法句柠,用于對數(shù)據(jù)更新浦译。
該方法接受4個參數(shù)
第一個參數(shù)
:表名
第二個參數(shù)
:Contentvalues對象,在這里把要更新的數(shù)據(jù)組裝進去
第三個參數(shù)
和第四個參數(shù)
:用于約束更新某一行或某幾行的數(shù)據(jù)(默認更新所有行)
Demo:
- activity_main.xml中加入update data按鈕控件
<Button
android:id="@+id/update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Update data"/>
- 更改MainActivity代碼溯职,onCreate()中加入代碼
//更新數(shù)據(jù)
Button updateData = (Button) findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99);
db.update("Book", values, "name = ?", new String[] { "The Da Vinci Code" });
}
});
這里在更新數(shù)據(jù)按鈕的點擊事件里面構建了一個 ContentValues
對象精盅,并且只給它指定了一組數(shù)據(jù),說明我們只是想把價格這一列的數(shù)據(jù)更新成10.99谜酒。然后調(diào)用了SQLiteDatabase的update()
方法去執(zhí)行具體的更新操作叹俏,可以看到,這里使用了第三僻族、第四個參數(shù)來指定具體更新哪幾行粘驰。第三個參數(shù)對應的是SQL語句的 where部分屡谐,表示更新所有name等于?的行,而?是一個占位符蝌数,可以通過第四個參數(shù)提供的一個字符串數(shù)組為第三個參數(shù)中的每個占位符指定相應的內(nèi)容愕掏。因此上述代碼想表達的意圖是將名字是 The Da Vinci Code
的這本書的價格改成10.99。
5. 刪除數(shù)據(jù)
SQLiteDatabase中提供了一個delete()
方法顶伞,用于刪除數(shù)據(jù)饵撑。
該方法接受3個參數(shù)
第一個參數(shù)
:表名
第二個參數(shù)
和第三個參數(shù)
:用于約束刪除某一行或某幾行的數(shù)據(jù)(默認刪除所有行)
Demo:
- activity_main.xml中加入delete data按鈕控件
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete data" />
- 更改MainActivity代碼,onCreate()中加入代碼
//刪除數(shù)據(jù)
Button deleteButton = (Button) findViewById(R.id.delete_data);
deleteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book", "pages > ?", new String[] { "500" });
}
});
程序指定刪除那些頁數(shù)超過500頁的書
6. 查詢數(shù)據(jù)
SQLiteDatabase中提供了一個query()
方法唆貌,用于對數(shù)據(jù)進行查詢滑潘。
該方法參數(shù)比較復雜,最短的一個重載方法也需要7個參數(shù)
第一個參數(shù)
:表名
第二個參數(shù)
:指定查詢的列名(默認所有列)
第三個參數(shù)
:和第四個參數(shù)
:用于約束查詢某一行或某幾行的數(shù)據(jù)(默認查詢所有行)
第五個參數(shù)
:指定需要group by的列锨咙,不指定則不進行group by操作
第六個參數(shù)
:對group by之后的數(shù)據(jù)進一步過濾
第七個參數(shù)
:指定查詢結果的排序方式
Demo:
- activity_main.xml中加入query data按鈕控件
<Button
android:id="@+id/query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query data" />
- 更改MainActivity代碼语卤,onCreate()中加入代碼
//查詢數(shù)據(jù)
Button queryButton = (Button) findViewById(R.id.query_data);
queryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 查詢Book表中所有的數(shù)據(jù)
Cursor cursor = db.query("Book", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
// 遍歷Cursor對象,取出數(shù)據(jù)并打印
String name = cursor.getString(cursor.getColumnIndex("name"));
String author = cursor.getString(cursor.getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex("pages"));
double price = cursor.getDouble(cursor.getColumnIndex("price"));
Log.d("MainActivity", "book name is " + name);
Log.d("MainActivity", "book author is " + author);
Log.d("MainActivity", "book pages is " + pages);
Log.d("MainActivity", "book price is " + price);
} while (cursor.moveToNext());
}
cursor.close();
}
});
我們首先在查詢按鈕的點擊事件里面調(diào)用了SQLiteDatabase的query()方法查詢數(shù)據(jù)蓖租。這里的query()方法非常簡單粱侣,只是使用了第一個參數(shù)指明去查詢Book表羊壹,后面的參數(shù)全部為null蓖宦,表示希望查詢這張表中的所有數(shù)據(jù)。雖然這張表中目前只剩下一條數(shù)據(jù)了油猫。査詢完之后就得到了一個Cursor
對象稠茂,接著我們調(diào)用它的 moveToFirst()
方法將數(shù)據(jù)的指針移動到第一行的位置,然后進入了一個循環(huán)當中情妖,去遍歷查詢到的每一行數(shù)據(jù)睬关。在這個循環(huán)中可以通過Cursor的getColumnIndex()
方法獲取到某一列在表中對應的位置索引,然后將這個索引傳入到相應的取值方法中毡证,就可以得到從數(shù)據(jù)庫中讀取到的數(shù)據(jù)了电爹。接著我們使用Log將數(shù)據(jù)打印出來。最后別忘了調(diào)用close()
方法關閉Cursor料睛。
重新運行程序丐箩,可以看到logcat打印的內(nèi)容如下: