Android学习笔记36:使用SQLite方式存储数据
在Android中一共提供了5種數(shù)據(jù)存儲(chǔ)方式,分別為:
(1)Files:通過FileInputStream和FileOutputStream對文件進(jìn)行操作。具體使用方法可以參閱博文《Android學(xué)習(xí)筆記34:使用文件存儲(chǔ)數(shù)據(jù)》。
(2)Shared?Preferences:常用來存儲(chǔ)鍵值對形式的數(shù)據(jù),對系統(tǒng)配置信息進(jìn)行保存。具體使用方法可以參閱博文《Android學(xué)習(xí)筆記35:使用Shared?Preferences方式存儲(chǔ)數(shù)據(jù)》。
(3)Content?Providers:數(shù)據(jù)共享,用于應(yīng)用程序之間數(shù)據(jù)的訪問。
(4)SQLite:Android自帶的輕量級(jí)關(guān)系型數(shù)據(jù)庫,支持SQL語言,用來存儲(chǔ)大量的數(shù)據(jù),并且能夠?qū)?shù)據(jù)進(jìn)行使用、更新、維護(hù)等操作。
(5)Network:通過網(wǎng)絡(luò)來存儲(chǔ)和獲取數(shù)據(jù)。
本篇博文介紹第四種方式,通過Android自帶的SQLite數(shù)據(jù)庫存儲(chǔ)數(shù)據(jù)。
?
1.SQLite簡介
SQLite是一款開源的、嵌入式關(guān)系型數(shù)據(jù)庫,第一個(gè)版本Alpha發(fā)布于2000年。SQLite在便攜性、易用性、緊湊性、高效性和可靠性方面有著突出的表現(xiàn)。
SQLite和C/S模式的數(shù)據(jù)庫軟件不同,它是一款嵌入式數(shù)據(jù)庫,沒有獨(dú)立運(yùn)行的進(jìn)程,與所服務(wù)的應(yīng)用程序在應(yīng)用程序進(jìn)程空間內(nèi)共生共存。它的代碼與應(yīng)用程序代碼也是在一起的,或者說嵌入其中,作為托管它的程序的一部分。因此不存在數(shù)據(jù)庫的客戶端和服務(wù)器,使用SQLite一般只需要帶上它的一個(gè)動(dòng)態(tài)庫,就可以享受它的全部功能。
數(shù)據(jù)庫服務(wù)器在程序中的好處是不需要網(wǎng)絡(luò)配置或管理。將數(shù)據(jù)庫客戶端與服務(wù)器運(yùn)行在同一個(gè)進(jìn)程中,可以省去不少的操作及麻煩:不用擔(dān)心防火墻或者地址解析;不用浪費(fèi)時(shí)間管理復(fù)雜的授權(quán)和權(quán)限;可以減少網(wǎng)絡(luò)調(diào)用相關(guān)的消耗;可以簡化數(shù)據(jù)庫管理并使程序更容易部署。
SQLite數(shù)據(jù)庫通過數(shù)據(jù)庫級(jí)上的獨(dú)占性和共享鎖來實(shí)現(xiàn)獨(dú)立事務(wù)處理。這意味著多個(gè)進(jìn)程可以在同一時(shí)間從同一數(shù)據(jù)庫讀取數(shù)據(jù),但是只有一個(gè)可以寫入數(shù)據(jù)。在某個(gè)進(jìn)程向數(shù)據(jù)庫執(zhí)行寫操作之前,必須獲得獨(dú)占鎖定。在發(fā)出獨(dú)占鎖定后,其他的讀寫操作將不會(huì)再發(fā)生。
此外,SQLite數(shù)據(jù)庫中的所有信息(比如表、視圖、觸發(fā)器等)都包含在一個(gè)文件內(nèi),方便管理和維護(hù)。SQLite數(shù)據(jù)庫還支持大部分操作系統(tǒng),除電腦上使用的操作系統(tǒng)之外,很多手機(jī)上使用的操作系統(tǒng)同樣可以運(yùn)行。同時(shí),SQLite數(shù)據(jù)庫還提供了多語言的編程接口,供開發(fā)者使用。
?
2.SQL基本命令
SQL是與關(guān)系型數(shù)據(jù)庫通信的唯一方式。它專注于信息處理,是為構(gòu)建、讀取、寫入、排序、過濾、映射、分組、聚集和通常的管理信息而設(shè)計(jì)的聲明式語言。
在講解SQL基本命令之前,有必要先了解一下SQLite所支持的數(shù)據(jù)類型都有哪些。
2.1?SQLite支持的數(shù)據(jù)類型
SQLite采用動(dòng)態(tài)數(shù)據(jù)存儲(chǔ)類型,會(huì)根據(jù)存入的值自動(dòng)進(jìn)行判斷。SQLite支持以下5種數(shù)據(jù)類型:
(1)NULL:空值
(2)INTEGER:帶符號(hào)的整型
(3)REAL:浮點(diǎn)型
(4)TEXT:字符串文本
(5)BLOB:二進(jìn)制對象
2.2?SQL基本命令
表是探索SQLite中SQL的起點(diǎn),也是關(guān)系型數(shù)據(jù)庫中信息的標(biāo)準(zhǔn)單位,所有的操作都是以表為中心的。那么如何使用SQL命令創(chuàng)建一張表呢?
2.2.1創(chuàng)建表
表是由行和列組成的,列稱為字段,行稱為記錄。
使用CREATE命令可以創(chuàng)建表,CREATE命令的一般格式為:
CREATE?[TEMP/TEMPORARY]?TABLE?table_name?(column_definitions?[,?constraints]);
其中,[]中的內(nèi)容是可選的,用TEMP或TEMPORARY關(guān)鍵字聲明的表是臨時(shí)表,這種表只存活于當(dāng)前會(huì)話,一旦連接斷開,就會(huì)被自動(dòng)銷毀。如果沒有明確指出創(chuàng)建的表是臨時(shí)表,則創(chuàng)建的是基本表,將會(huì)在數(shù)據(jù)庫中持久存在,這也是數(shù)據(jù)庫中最常見的表。
CREATE?TABLE命令至少需要一個(gè)表名和一個(gè)字段名,上述命令中的table_name表示表名,表名必須與其他標(biāo)識(shí)符不同。column_definitions由用逗號(hào)分隔的字段列表組成,每個(gè)字段定義包括一個(gè)名稱、一個(gè)域(類型)和一個(gè)逗號(hào)分隔的字段約束。其中,域是指存儲(chǔ)在該列的信息的類型,約束用來控制什么樣的值可以存儲(chǔ)在表中或特定的字段中。
一條創(chuàng)建表的命令示例如下:
1 CREATE TABLE tab_student (studentId INTEGER PRIMARY KEY AUTOINCREMENT, 2 studentName VARCHAR(20), 3 studentAge INTEGER);如上,我們創(chuàng)建了一個(gè)名為tab_student的表,該表包含3個(gè)字段:studentId、?studentName和studentAge,其數(shù)據(jù)類型分別為:INTEGER、VARCHAR和INTEGER。
此外,通過使用關(guān)鍵字PRIMARY?KEY,我們指定了字段studentId所在的列是主鍵。主鍵確保了每一行記錄在某種方式上與表中的其他行記錄是不同的(唯一的),進(jìn)而確保了表中的所有字段都是可尋址的。
SQLite為主鍵提供自增長功能,當(dāng)定義字段類型為INTEGER?PRIMARY?KEY時(shí),SQLite將為該字段創(chuàng)建默認(rèn)值,該默認(rèn)值確保整數(shù)值是唯一的。SQLite使用64-bit單符號(hào)整數(shù)主鍵,因此,該字段的最大值是9,223,372,036,854,775,807。當(dāng)達(dá)到最大值時(shí),SQLite會(huì)自動(dòng)搜索該字段還未使用的值,并作為要插入的值。從表中刪除記錄時(shí),rowid可能被回收并在后面的插入中使用。因此,新創(chuàng)建的rowid不一定是按照嚴(yán)格順序增長的。如果想要SQLite使用唯一的自動(dòng)主鍵值,而不是填補(bǔ)空白,可以在主鍵定義INTEGER?PRIMARY?KEY中加入關(guān)鍵字AUTOINCREMENT。AUTOINCREMENT關(guān)鍵字阻止rowid回收,它將為新插入的記錄產(chǎn)生新的(不是回收的)rowid。
2.2.2插入記錄
使用INSERT命令可以一次插入一條記錄,INSERT命令的一般格式為:
INSERT?INTO?tab_name?(column_list)?VALUES?(value_list);
其中,tab_name指明將數(shù)據(jù)插入到哪個(gè)表中,column_list是用逗號(hào)分隔的字段名稱,這些字段必須是表中存在的,value_list是用逗號(hào)分隔的值列表,這些值是與column_list中的字段一一對應(yīng)的。
比如,向剛才創(chuàng)建的tab_student表中插入一條記錄,便可以使用如下的語句完成:
INSERT INTO tab_student (studentId, studentName, studentAge) VALUES (1, “jack”, 23);
通過以上的語句,便插入了一條studentName=”jack”,?studentAge=”23”的記錄,該記錄的主鍵為studentId=1。
2.2.3更新記錄
使用UPDATE命令可以更新表中的記錄,該命令可以修改一個(gè)表中一行或者多行中的一個(gè)或多個(gè)字段。UPDATE命令的一般格式為:
UPDATE?tab_name?SET?update_list?WHERE?predicate;
其中,update_list是一個(gè)或多個(gè)字段賦值的列表,字段賦值的格式為column_name=value。WHERE子句使用斷言識(shí)別要修改的行,然后將更新列應(yīng)用到這些行。
比如,要更新剛才插入到tab_student表中的記錄,便可以使用如下的語句完成:
UPDATE tab_student SET studentName=”tom”, studentAge=”25” WHERE studentId=1;
通過以上的語句,便可以將剛才插入的主鍵為studentId=1的記錄更新為studentName=”tom”,?studentAge=”25”了。
2.2.4刪除記錄
使用DELETE命令可以刪除表中的記錄,DELETE命令的一般格式為:
DELETE?FROM?table_name?WHERE?predicate;
其中,table_name指明所要?jiǎng)h除的記錄位于哪個(gè)表中。和UPDATE命令一樣,WHERE子句使用斷言識(shí)別要?jiǎng)h除的行。
比如,要?jiǎng)h除剛才插入的記錄,便可以使用如下的語句完成:
DELETE FROM tab_student WHERE studentId=1;
2.2.5查詢記錄
SELECT命令是查詢數(shù)據(jù)庫的唯一命令。SELECT命令也是SQL命令中最大、最復(fù)雜的命令。
SELECT命令的通用形式如下:
SELECT?[distinct]?heading?
FROM?tables?
WHERE?predicate?
GROUP?BY?columns?
HAVING?predicate?
ORDER?BY?columns?
LIMIT?count,offset;
其中,每個(gè)關(guān)鍵字(如FROM、WHERE、HAVING等)都是一個(gè)單獨(dú)的子句,每個(gè)子句由關(guān)鍵字和跟隨的參數(shù)構(gòu)成。GROUP?BY和HAVING一起工作可以對GROUP?BY進(jìn)行約束。ORDER?BY使記錄集在返回之前按一個(gè)或多個(gè)字段的值進(jìn)行排序,可以指定排序方式為ASC(默認(rèn)的升序)或DESC(降序)。此外,還可以使用LIMIT限定結(jié)果集的大小和范圍,count指定返回記錄的最大數(shù)量,offset指定偏移的記錄數(shù)。
在上述的SELECT命令通用形式中,除了SELECT之外,所有的子句都是可選的。目前最常用的SELECT命令由三個(gè)子句組成:SELECT、FROM、WHERE,其基本語法形式如下:
SELECT?heading?FROM?tables?WHERE?predicate;
比如,要查詢剛才插入的記錄,便可以使用如下的語句完成:
SELECT studentId, studentName, studentAge FROM tab_student WHERE studentId=1;
至此,我們介紹了SQL中最基本和最常用的CREATE、INSERT、UPDATE、DELETE和SELECT命令。當(dāng)然了,這里只是對其進(jìn)行了簡單的介紹,有關(guān)SQLite中SQL命令的詳細(xì)使用方法,可以參閱《SQLite權(quán)威指南》一書的第三章和第四章。
?
3.數(shù)據(jù)庫操作輔助類SQLiteOpenHelper
Android提供了一個(gè)重要的類SQLiteOpenHelper,用于輔助用戶對SQLite數(shù)據(jù)庫進(jìn)行操作。
SQLiteOpenHelper的構(gòu)造函數(shù)原型如下:
public?SQLiteOpenHelper(Context?context,?String?name,?SQLiteDatabase.CursorFactory?factory,?int?version);
其中,參數(shù)context表示應(yīng)用程序運(yùn)行的環(huán)境,包含應(yīng)用程序所需的共享資源。參數(shù)name表示Android的數(shù)據(jù)庫名字。參數(shù)factory是SQLiteDatabase.CursorFactory類對象,用于存儲(chǔ)查詢Android?SQLite數(shù)據(jù)庫的結(jié)果集。參數(shù)version表示應(yīng)用程序所用的數(shù)據(jù)庫的版本,該版本并非SQLite的真正版本,而是指定應(yīng)用程序中的SQLite數(shù)據(jù)庫的版本,當(dāng)該版本號(hào)發(fā)生變化時(shí),將會(huì)觸發(fā)SQLiteOpenHelper類中的onUpgrade()或onDowngrade()方法。
SQLiteOpenHelper類的所有方法如圖1所示。
圖1?SQLiteOpenHelper類的方法
其中,close()方法用于關(guān)閉SQLiteOpenHelper對象中的SQLite數(shù)據(jù)庫;getReadableDatabase()方法和getWriteableDatabase()方法類似,getReadableDatabase()方法以只讀狀態(tài)打開SQLiteOpenHelper對象中指定的SQLite數(shù)據(jù)庫,任何想要修改數(shù)據(jù)庫的操作都是不允許的;getWriteableDatabase()方法也是打開數(shù)據(jù)庫,但是允許數(shù)據(jù)庫正常的讀/寫操作;在一個(gè)不存在的數(shù)據(jù)庫上調(diào)用任何方法時(shí),都會(huì)隱式的調(diào)用SQLiteOpenHelper對象的onCreate()方法;當(dāng)應(yīng)用程序第一次訪問數(shù)據(jù)庫時(shí),則會(huì)調(diào)用onOpen()方法,但是,如果版本號(hào)發(fā)生了變化的話,則會(huì)調(diào)用onUpgrade()或onDowngrade()方法。
?
4.數(shù)據(jù)庫類SQLiteDatabase
SQLiteDatabase類用來完成對數(shù)據(jù)庫的操作任務(wù),比如表的選擇、插入、更新和刪除語句等。
SQLiteDatabase類中常用的用于執(zhí)行SQL語句的方法有以下一些。
(1)execSQL()方法:
public?void?execSQL?(String?sql);
public?void?execSQL?(String?sql,?Object[]?bindArgs);
(2)query()方法:
public?Cursor?query?(String?table,?String[]?columns,?String?selection,?String[]?selectionArgs,?String ?groupBy,?String? having,String?orderBy,?String?limit);
public?Cursor?query?(boolean?distinct,?String?table,?String[]?columns,?String?selection,?String[]?selectionArgs,?String groupBy,?String?having,?String?orderBy,?String?limit,?CancellationSignal?cancellationSignal);
public?Cursor?query?(String?table,?String[]?columns,?String?selection,?String[]?selectionArgs,?String?groupBy,?String? having,String?orderBy);
public?Cursor?query?(boolean?distinct,?String?table,?String[]?columns,?String?selection,?String[]?selectionArgs,?String groupBy,?String?having,?String?orderBy,?String?limit);
(3)queryWithFactory()方法:
public?Cursor?queryWithFactory?(SQLiteDatabase.CursorFactory?cursorFactory,?boolean?distinct,?String?table,?String[]columns,?String?selection,?String[]?selectionArgs,?String?groupBy,?String?having,?String? orderBy,?String? limit,CancellationSignal?cancellationSignal);
public?Cursor?queryWithFactory?(SQLiteDatabase.CursorFactory?cursorFactory,?boolean?distinct,?String?table,?String[]columns,?String?selection,?String[]?selectionArgs,?String?groupBy,?String?having,?String?orderBy,?String?limit);
(4)rawQuery()方法:
public?Cursor?rawQuery?(String?sql,?String[]?selectionArgs,?CancellationSignal?cancellationSignal);
public?Cursor?rawQuery?(String?sql,?String[]?selectionArgs);
(5)rawQueryWithFactory()方法:
public?Cursor?rawQueryWithFactory?(SQLiteDatabase.CursorFactory?cursorFactory,?String?sql,?String[]? selectionArgs,String?editTable);
public?Cursor?rawQueryWithFactory?(SQLiteDatabase.CursorFactory?cursorFactory,?String?sql,?String[]? selectionArgs,String?editTable,?CancellationSignal?cancellationSignal);
其中,execSQL()方法都有一個(gè)參數(shù)sql,這個(gè)參數(shù)是一個(gè)SQL語句。第二個(gè)參數(shù)bindArgs接收一個(gè)數(shù)組,數(shù)組中的每個(gè)成員捆綁了一個(gè)查詢。execSQL()方法用于運(yùn)行那些沒有返回值的查詢語句,比如創(chuàng)建、插入、更新和修改表。
query()方法和queryWithFactory()方法是在數(shù)據(jù)庫中運(yùn)行一些輕量級(jí)的單查詢語句,參數(shù)包括table、columns、groupBy、having、orderBy、limit等SQL語句關(guān)鍵字。這些方法允許將SQL語句傳遞給相關(guān)方法,而不必直接使用SQL語句。
rawQuery()方法和rawQueryWithFactory()方法也都有一個(gè)參數(shù)sql,用于執(zhí)行SQL查詢語句,返回值是Cursor對象。這兩個(gè)方法都有一個(gè)版本能夠接收一個(gè)字符串?dāng)?shù)組selectionArgs作為參數(shù),通過這個(gè)參數(shù),SQLiteDatabase對象將把捆綁的SQL語句中的問號(hào)(?)用這個(gè)數(shù)組中的值代替,并按照一一對應(yīng)的位置關(guān)系進(jìn)行取代。
SQLiteDatabase類提供了大約50個(gè)方法,除此之外還有一些用于打開數(shù)據(jù)庫的方法(如openDatabase()、openOrCreateDatabase()等),用于管理SQLite事務(wù)的方法(如beginTransaction()、endTransaction()等),用于測試數(shù)據(jù)庫是否被鎖住的方法(如isDbLockedByCurrentThread()、isDbLockedByOtherThread()等),以及獲取數(shù)據(jù)庫基本信息的方法(如getMaximumSiza()、getVersion()等)。這里就不一一介紹了,具體可以參閱SQLiteDatabase類的API幫助文檔。
?
5.游標(biāo)類Cursor
在Android中,查詢數(shù)據(jù)是通過Cursor類來實(shí)現(xiàn)的,當(dāng)我們使用SQLiteDatabase.query()或SQLiteDatabase.rawQuery()方法時(shí),會(huì)得到一個(gè)Cursor對象,Cursor指向的就是每一條記錄,它提供了很多有關(guān)查詢的方法,如圖2所示。
圖2?Cursor類的常用方法
?
6.封裝接口
有了以上的基礎(chǔ),我們便可以按照MVC的架構(gòu),封裝一個(gè)接口層,在該接口層中實(shí)現(xiàn)對SQLite數(shù)據(jù)庫的具體操作。
以下分別以添加數(shù)據(jù)、更新數(shù)據(jù)、查詢數(shù)據(jù)為例講解其具體的實(shí)現(xiàn)方法。在實(shí)現(xiàn)這些方法之前,我們首先需要?jiǎng)?chuàng)建一張表。這里我創(chuàng)建了一個(gè)名為MySQLiteOpenHelper的類,讓它繼承自SQLiteOpenHelper類,并實(shí)現(xiàn)了SQLiteOpenHelper類的onCreate()方法,在該方法里實(shí)現(xiàn)創(chuàng)建一張表的操作,具體源代碼如下:
1 /* 2 * Function : 創(chuàng)建表 3 * Author : 博客園-依舊淡然 4 */ 5 public void onCreate(SQLiteDatabase db) { 6 db.execSQL("CREATE TABLE tab_student (studentId INTEGER PRIMARY KEY AUTOINCREMENT, " +"studentName VARCHER(20), " +
"studentAge INTEGER)"); 7 }
通過以上的代碼,我們創(chuàng)建了一張名為“tab_student”的表,并在該表中創(chuàng)建了三個(gè)字段,分別為:studentId、studentName和studentAge。并且指定了studentId字段作為該表的主鍵。
6.1添加數(shù)據(jù)
添加數(shù)據(jù)可以使用SQLiteDatabase.execSQL(String?sql,?Object[]?bindArgs)方法來實(shí)現(xiàn),具體如下:
1 /* 2 * Function : 添加數(shù)據(jù) 3 * Author : 博客園-依舊淡然 4 */ 5 public void addStudentInfo(Student student) { 6 db = mySQLiteOpenHelper.getWritableDatabase(); 7 db.execSQL("INSERT INTO tab_student (studentId, studentName, studentAge) values (?, ?, ?)", 8 new Object[] {student.getStudentId(), student.getStudentName(), student.getStudentAge()}); 9 }其中,通過第二個(gè)參數(shù)bindArgs,使SQL語句中的問號(hào)(?)與這個(gè)數(shù)組中的值形成一一對應(yīng)關(guān)系,從而將值寫入到“tab_student”表中的對應(yīng)字段中。
6.2更新數(shù)據(jù)
更新數(shù)據(jù)的方法與添加數(shù)據(jù)的方法大致相同,具體如下:
1 /* 2 * Function : 更新數(shù)據(jù) 3 * Author : 博客園-依舊淡然 4 */ 5 public void updateStudentInfo(Student student) { 6 db = mySQLiteOpenHelper.getWritableDatabase(); 7 db.execSQL("UPDATE tab_student SET studentName = ?, studentAge = ? WHERE studentId = ?", new Object[] {student.getStudentName(), student.getStudentAge(), student.getStudentId()}); 8 }6.3查詢數(shù)據(jù)
查詢數(shù)據(jù)時(shí),因?yàn)樾枰祷夭樵兊慕Y(jié)果,所以需要使用SQLiteDatabase.rawQuery()方法將查詢的結(jié)果返回,具體如下:
1 /*2 * Function : 查詢數(shù)據(jù)3 * Author : 博客園-依舊淡然4 */5 public Student findStudentInfo(int id) {6 db = mySQLiteOpenHelper.getWritableDatabase();7 String sql = "SELECT studentId, studentName, studentAge FROM tab_student WHERE studentId = ?";8 Cursor cursor = db.rawQuery(sql, new String[] {String.valueOf(id)});9 if(cursor.moveToNext()) { 10 return new Student(cursor.getInt(cursor.getColumnIndex("studentId")),cursor.getString(cursor.getColumnIndex("studentName")), 11 cursor.getInt(cursor.getColumnIndex("studentAge"))); 12 } 13 return null; 14 }
可以看出,通過使用SQLiteDatabase.rawQuery()方法可以將查詢到的結(jié)果存入Cursor對象中。然后,我們可以使用Cursor對象的getXXX()方法將查詢結(jié)果從Cursor對象中取出來。
當(dāng)然了,我們還可以根據(jù)實(shí)際的需要,去實(shí)現(xiàn)更多的接口方法,比如,刪除數(shù)據(jù)、獲取數(shù)據(jù)列表、獲取數(shù)據(jù)個(gè)數(shù)等等。
封裝好了以上的這些接口方法,便可以很方便的在程序中直接調(diào)用這些方法,不必再去關(guān)心底層數(shù)據(jù)庫的調(diào)用,而將精力放在UI界面的設(shè)計(jì)實(shí)現(xiàn)上。
轉(zhuǎn)載于:https://www.cnblogs.com/printN/p/6592479.html
與50位技術(shù)專家面對面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的Android学习笔记36:使用SQLite方式存储数据的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Integration testing
- 下一篇: Luogu P1198 [JSOI200