Android開發之利用SQLite進行數據存儲
1.SQLite數據庫簡單介紹
SQLite。是一款輕型的數據庫,是遵守ACID的關系型數據庫管理系統,它包括在一個相對小的C庫中。它是D.RichardHipp建立的公有領域項目。它的設計目標是嵌入式的,並且眼下已經在非常多嵌入式產品中使用了它,它占用資源非常的低,在嵌入式設備中。可能僅僅須要幾百K的內存就夠了。它能夠支持Windows/Linux/Unix等等主流的操作系統,同一時候能夠跟非常多程序語言相結合,比方 Tcl、C#、PHP、Java等,還有ODBC接口,相同比起M
ysql、PostgreSQL這兩款開源的世界著名數據庫管理系統來講,它的處理速度比他們都快。
SQLite第一個Alpha版本號誕生於2000年5月。
至2015年已經有15個年頭,SQLite也迎來了一個版本號 SQLite 3已經公布。
總結 : SQLite作為移動終端的數據庫是非常合適的。占用內存小、輕量級、處理速度快…
2.Android中怎樣使用SQLite
2.1 創建SQLiteOpenHelper對象。並創建表
新建一個類,命名為MySQLiteOpenHelper,並將其繼承自SQLiteOpenHelper:
新建后會報錯,由於沒有加入構造方法,加入構造方法:
package com.example.sqllitetest;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
public class MySQLOpenHelper extends SQLiteOpenHelper {
public MySQLOpenHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
// TODO Auto-generated constructor stub
}
/** * 數據庫創建時會調用,在這里運行創建表的語句 */
@Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
}
/** * 數據庫升級時,此方法會調用,在這里運行數據庫更新操作 */
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
/** * 數據庫打開時,此方法會調用 */
@Override
public void onOpen(SQLiteDatabase db) {
// TODO Auto-generated method stub
super.onOpen(db);
}
}
在上面的onCreate()。方法中運行一條創建表名為person的語句
db.execSQL("create table person (_id integer primary key autoincrement, name char(10), age integer(3), phone integer(20))");
注意:事實上在sqlite中除了id主鍵以外。全部的字段都沒有明白的類型限制,舉個列子,我們向integer類型的字段中插入char類型的數據也是能夠的
那么既然這樣,我們創建數據庫時,指定的數據類型意圖何在?
這些是為了。讓我們程序猿了解某個字段的類型限制。實際編碼時,還是要指定字段明白類型的,方便日后維護和理解。
新建一個測試類SqliteTestCase.java:
package com.example.sqllitetest;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.test.AndroidTestCase;
public class SqlliteTestCase extends AndroidTestCase {
private MySQLOpenHelper oh;
private SQLiteDatabase db;
public void test() {
System.out.println("This is test method!");
}
/** * 測試框架初始化完成過后,在測試方法調用之前調用此方法 */
@Override
protected void setUp() throws Exception {
super.setUp();
// 獲得可寫的數據庫對象(若數據庫不存在。先創建數據庫。再獲取可讀可寫的數據庫對象;假設數據庫存在,就直接打開)
// *********************** 參數說明 ***********************
// 參數1 : 獲取Context對象,AndroidTestCase中為了方便測試,提供了getContext()方法
// 參數2 : 數據庫文件名稱
// 參數3 : 游標工廠對象,被用來創建游標對象。默覺得空
// 參數4 : 用來標識數據庫的版本號,用來與之前創建的時候做對照,當版本號大於,則調用onUpgrade()
oh = new MySQLOpenHelper(getContext(), "person.db", null, 1);
/** * ************** 第一種方法:獲取數據庫 ************** * 1.被用來創建或打開一個可讀寫的數據庫。 * 當它被第一次調用的時候,會依據new SQLOpenHelper()中的數據庫名和版本號號 * 當已存在相同數據庫文件。並且版本號號相同。則直接打開, * 反之,則調用創建或更新方法。 * * 2.數據一旦打開成功。將被緩存起來,當我們在須要錄入數據時,能夠在不論什么地方去調用返回 * 的對象去操作數據庫。注意:但不須要使用數據庫時。須要關閉數據庫 * * 3.但沒有權限時。抑或磁盤滿了的時候,這種方法將會被調用失敗,可是,若問題得到解決。就可以成功調用 * * 4.調用這種方法,若觸發更新操作,你就須要警覺了: * 由於數據庫的更新是個耗時的操作。我們不應該在應用的主線程中去調用它。包括ContentProvider.onCreate() * */
db = oh.getWritableDatabase();
/** * ************** 另外一種方法:獲取數據庫 ************** * 不要被名字誤導。這種方法相同可獲得可讀寫的數據庫對象。 * 它與上面方法的差別是,當遇到一些問題:(列如:磁盤滿了。這個時候調用不會失敗,而是將會返回一個僅僅讀的數據庫對象) */
db = oh.getReadableDatabase();
}
/** * 測試方法運行完成過后,調用此方法 */
@Override
protected void tearDown() throws Exception {
// TODO Auto-generated method stub
super.tearDown();
// 關閉數據庫
db.close();
}
}
注意:當我們new SQLiteOpenHelper()打開某個數據庫時。傳入的版本號號不應比之前創建這個數據庫的時候綁定的版本號號低。否則會出現以下的錯誤,版本號號僅僅能遞增
運行test()方法,顯示為綠色,說明沒有錯誤
通過DDMS–>File Explore,查看data/data/項目包名/databases文件夾下:
導出文件。在SQLite Expert軟件中打開:
能夠看到,數據庫和表都被正確創建。
2.2 通過SQLiteDatabase對象運行增刪改查操作
2.2.1加入數據操作
加入數據有兩種方法:
- 第一種方法 : 通過手寫sql語句,運行execSQL();方法;
在SqliteTestCase.java中加入insert()方法
public void insert() {
db.execSQL("insert into person (name, age, phone) values(?, ? , ?)", new Object[]{"張三", 18, "180199678455"}); db.execSQL("insert into person (name, age, phone) values(?
, ? , ?)", new Object[]{"趙四", 16, "180199678455"}); db.execSQL("insert into person (name, age, phone) values(?
, ? , ?)", new Object[]{"Android", 15, "180199678455"}); }
運行結果,數據成功插入表中:
- 另外一種方法 : 通過Android API,將數據封裝到contentValues中。
在SqliteTestCase.java中加入insertByApi()方法:
public void insertApi() {
// 把全部的數據封裝到contentValues中
ContentValues values = new ContentValues();
values.put("name", "zhangsan");
values.put("age", 78);
values.put("phone", "13812235689");
// 參數說明:
// 第一個參數table : 表名
// 第二個參數nullColumnHack :能夠指定為null,若為null,當你values中無值。則不會插入行
// 若你指定了nullColumnHack的值,即便你的values中無值,也會
// 插入null值到你的字段下
// 第三個參數values : ContentValues對象
db.insert("person", null, values);
}
導出db文件,刷新:
2.2.1刪除數據操作
刪除數據相同有兩種方法:
- 第一種方法 : 通過手寫sql語句,運行execSQL();方法。
public void delete() {
// 刪除id為1的行
db.execSQL("delete from person where _id = ?", new Object[]{1});
}
- 另外一種方法 : 通過Android API;
public void deleteApi() {
// 刪除表中age = 78, id = 4的記錄
// 返回值為受影響的行,刪除了多少行
int i = db.delete("person", "age = ? and _id = ?
", new String[]{"78", "4"}); }
2.2.1改動數據操作
改動數據相同有兩種方法:
- 第一種方法 : 通過手寫sql語句。運行execSQL()方法:
public void update() {
// 改動id為2的phone值為110
db.execSQL("update person set phone = ? where _id = ?", new Object[]{"110", 2});
}
查看結果:
- 另外一種方法 : 通過Android API;
public void updateApi() {
// 通過ContentValues來指定改動后的值
ContentValues values = new ContentValues();
values.put("phone", "120");
// 返回值為受影響的行
int i = db.update("person", values, "_id = ? and age = ?", new String[]{"3", "15"});
}
查看結果:
2.2.1查詢數據操作
查詢數據相同有兩種方法:
- 第一種方法 : rawQuery()方法:
public void query() {
Cursor cursor = db.rawQuery("select name, age, phone from person ", null);
while (cursor.moveToNext()) {
// 不推薦用這樣的下標的方式來獲取值,一旦后期字段的位置有所改動,維護起來比較麻煩
// String name = cursor.getString(0);
// int age = cursor.getInt(1);
// String phone = cursor.getString(2);
// 推薦使用
String name = cursor.getString(cursor.getColumnIndex("name"));
int age = cursor.getInt(cursor.getColumnIndex("age"));
String phone = cursor.getString(cursor.getColumnIndex("phone"));
System.out.println("name : " + name + "; age = " + age + ";" + " phone = " + phone);
}
}
logcat輸出結果:
- 另外一種方法 : 通過Android API。
public void queryApi() {
Cursor cursor = db.query("person", null, null, null, null, null, null);
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("name"));
int age = cursor.getInt(cursor.getColumnIndex("age"));
String phone = cursor.getString(cursor.getColumnIndex("phone"));
System.out.println("name : " + name + "; age = " + age + ";" + " phone = " + phone);
}
}
2.3 SQLiteDatabase之事務transaction
應用場景:當須要保證多條語句同一時候運行成功,否則。回滾
這里。我們簡單的模擬一下。假設我們須要id為2的age加1歲,同一時候又要保證id為3的age減1歲
原本數據在數據庫中是這樣:
假設事務運行成功后。name為趙四的age將變為17,而name為Android的age變為14。反之,兩條數據的全部屬性值不變。
首先。我們人為的導致運行失敗:
加入方法:
public void transaction() {
try {
// 開啟事務
db.beginTransaction();
ContentValues values = new ContentValues();
values.put("age", 17);
db.update("person", values, "_id = ?", new String[]{"2"});
values.clear();
values.put("age", 14);
db.update("person", values, "_id = ?", new String[]{"3"});
int i = 1 / 0; // 這里有錯,將導致事務運行失敗
// 設置事務運行成功
db.setTransactionSuccessful();
} catch (Exception e) {
// TODO: handle exception
} finally {
// 結束事務,同一時候提交。假設已經設置事務運行成功,則sql語句生效。反之,則回滾
db.endTransaction();
}
}
導出db文件,刷新,看到數據無不論什么變化:
再將錯誤去掉。運行方法:
結果例如以下,運行成功: