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