Android持久化存储(3)SQLite数据库的使用
1.什么是SQlite
SQLite是由C語言編寫的一款輕型數據庫,因占用資源小,處理速度快,功能齊全,特別適用于移動設備,最重要的是開源,任何人都可以使用它,許多開源項目(PHP,Python)和當今兩大手機操作系統Android和iOS都使用了SQLite,造就了SQLite成為目前世界上最常見的數據庫引擎。
2.SQLite特點
除了占用資源小,處理速度快等優點,SQLite還有自己的特點,那就是支持弱數據類型,其他主流SQL數據庫通常支持強類型的數據,也就是每一列的類型都必須預先指定,如果輸入的數據和指定數據類型對不上則報錯,而SQLite采用的是弱類型,也就是說,創建一個表時指定某列的數據類型,但是你可以把任何數據類型插入該列,SQLite將檢查它的類型,如果該類型與關聯的列不匹配,則SQLite會嘗試將該值轉換成該列的類型,如果不能轉換,則該值將作為其本身具有的類型存儲。
SQLite支持數據類型如下:包括NULL、INTEGER、REAL、TEXT和BLOB
- NULL: 該值是空值;
- INTEGER: 該值是一個有符號整數,根據值的大小以1、2、3、4、6或8個字節存儲;
- REAL: 該值是一個浮點值,以8字節浮點數存儲;
- TEXT: 該值是一個文本字符串,使用數據庫編碼(UTF-8,UTF-16BE或UTF-16LE)存儲;
- BLOB: 值是BLOB數據塊,以輸入的數據格式進行存儲。
3.Android使用SQLite
上文已說,Android集成了SQLite數據庫,與此同時,Android還提供了使用SQLite數據庫的API,可通過這些API可很方便的進行數據庫操作。
3.1 SQLiteOpenHelper介紹
SQLiteOpenHelper是Android提供的SQLite幫助類,用于管理數據庫(包括創建、增,刪,改)和管理數據庫的版本,方便開發者操作SQLite,SQLiteOpenHelper基本方法如下(更相信請參考官方文檔https://developer.android.google.cn/reference/android/database/sqlite/SQLiteOpenHelper?hl=zh-cn)
| onCreate() | 首次創建數據庫時執行該方法,如果數據庫不存在則創建,onCreate()方法在初次調用時才會被調用。重寫onCreate()方法時,生成數據表結構 |
| onUpgrade() | 在需要升級數據庫時調用方法,該方法檢測數據庫傳入的版本號與當前的版本號是否相同,如果傳入的版本號高于之前的版本,觸發該方法 |
| getReadableDatabase() | 創建或者打開可讀數據庫 |
| getWritableDatabase() | 創建打開可讀/寫的數據庫 |
| close() | 關閉所有打開的數據庫對象 |
onCreat和onUpgrade是開發者實現,由SQLiteOpenHelper來調用。
其中onCreat是SQLiteOpenHelper在嘗試找數據庫時,如果沒有找到則執行該方法,那么什么時候SQLiteOpenHelper去找數據庫呢?是在創建SQLiteOpenHelper實例時,還是使用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()獲取SQLiteDatabase時?
我們可以用代碼來驗證一下,我們在繼承了SQLiteOpenHelper的MySQLiteOpenHelper代碼里面添加一個flag作為標記,flag初始值是0,然后在構造函數里賦值成100,最后在onCreate中添加打印代碼。然后我們在Activity里首先只實例化MySQLiteOpenHelper,不獲取數據庫,看是否觸發了onCreate里的打印信息。之后再添加java SQLiteDatabase sqLiteDatabase= mySQLiteOpenHelper.getWritableDatabase();獲取數據,再看打印信息。
Activity代碼如下:
package com.test.sqlitedemo;import androidx.appcompat.app.AppCompatActivity;import android.database.sqlite.SQLiteDatabase; import android.os.Bundle;public class MainActivity extends AppCompatActivity {MySQLiteOpenHelper mySQLiteOpenHelper;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mySQLiteOpenHelper=new MySQLiteOpenHelper(this,"test",1);} }MySQLiteOpenHelper代碼如下:
package com.test.sqlitedemo;import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log;public class MySQLiteOpenHelper extends SQLiteOpenHelper {private int flag=0;/**** @param context* @param name 數據庫名稱* @param version 數據庫版本號 當想升級數據庫時,填入大于當前版本號的數*/public MySQLiteOpenHelper(Context context, String name, int version) {super(context, name, null, version);//這里賦值成100flag=100;}/**** @param db SQLiteDatabase數據庫對象*/@Overridepublic void onCreate(SQLiteDatabase db) {Log.d("MySQLiteOpenHelper", "flag="+flag);//新建數據表db.execSQL("create table translog(_id integer primary key autoincrement, transid , amount,date)");flag=50;}/**** @param db* @param oldVersion 舊版本號* @param newVersion 新版本號*/@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {Log.i("MySQLiteOpenHelper", oldVersion + "------->" + newVersion);}}驗證結果是只實例化MySQLiteOpenHelper沒有觸發onCreate里的打印信息,在獲取數據庫的時候,SQLiteOpenHelper才會調用onCreate方法,關閉應用后再打開,也不會觸發打印信息,因為數據庫已經創建了。(這里驗證半天,然后發現官網用一句話說清楚了什么時候創建數據庫:
Create a helper object to create, open, and/or manage a database. This method always returns very quickly. The database is not actually created or opened until one of getWritableDatabase() or getReadableDatabase() is called
再來看onUpgrade方法,該方法在數據庫需要升級的時候調用,當傳入SQLiteOpenHelper構造版本參數大于當前的版本時,觸發該方法。升級數據庫是開發者經常要面對的,而且多數時候在升級數據庫時不能把之前的數據全部刪掉,開發者可在onUpgrade函數里添加搬運數據的代碼,在觸發升級后執行里面的搬運邏輯。
有人可能會問了,SQLiteOpenHelper難道僅僅是幫忙檢查數據庫是否存在和升級數據庫用到?如果只是這兩個功能,雖然能幫開發者省點工作量,但并沒有省多少工作啊!別忘了一句getWritableDatabase或者getWritableDatabase就可以獲得數據庫實例,換成其他平臺操作數據庫,還得慢慢拼湊數據庫連接語句,這也省了不少工作量。其他數據庫降級等也可以通過這個類操作,更具體請參考官方文檔。
3.2 創建數據庫
了解了SQLiteOpenHelper之后,便可以開始實踐數據庫之旅了。創建數據庫代碼見上邊的onCreate里的創建SQL語句。該語句創建一個名為translog的表,用來記錄交易日志,translog表有4列,分別是主鍵id,交易單號,金額和日期,注意到除了_id設置了為integer類型(因為要設置自增長主鍵),其他列都沒指定,執行不會報錯,這是SQLite弱數據類型的好處。
db.execSQL("create table translog(_id integer primary key autoincrement, transid , amount,date)");3.3 添加數據
往表里添加數據有兩種方法,一種使用原生SQL語句,往表里添加一條交易日志SQL語句如下
db.execSQL("INSERT INTO translog (transid, amount,date)"+ "VALUES ('955162020', 100.00,20200305)");還有第二種也稍微簡單移動的辦法,那就是使用SQLiteDatabase對象的提供的insert(), update(),delete(),query()方法。這些方法其實是做了一層封裝,最后還是內部轉換成SQL語句完成插入數據操作。
添加數據insert(String table,String nullColumnHack,ContentValues values)有3個參數,其中:
- 參數1 表名;
- 參數2 空列的默認值;
- 參數3 ContentValues類型數據;
其中第二個參數nullColumnHack是空列的默認值,但又可以填null,我們知道,SQL不允許在不指定一個列名的情況下插入,例如***INSERT INTO translog ()VALUES ()")***是錯的,如果第三個參數為空值,則會出現這種語法錯誤,第二個參數正是為了防止錯誤誕生,這樣第三個參數為空值時,則使用第二個參數作為默認字段。那為什么又可以設置為null?這是在第三個參數values不為Null并且元素的個數大于0可以填的 。這個參數的思路是,如果你懶得檢查第三個參數是否為空,那么填一個參數讓我管,如果你填了一個null不讓我管,那么你自己就要注意第三個參數不能為空。
第三個參數是ContentValues類型的數值,用法和map相似,提供了存取數據的put和get方法。注意鍵應該是表中存在的列名稱。
下面是插入代碼示例,注意到我們沒給主鍵添加值,我們已經設置主鍵是自增長列,當沒再ContentValues里給一個列put一個值時,相當于為該列添加一個null的值,主鍵自動增長,但是如果賦值了,則id設置成賦值后的值,建議讓id自動增長,自己賦值不注意的話會導致id重復而報錯。
//實例化常量值 ContentValues cValue = new ContentValues(); //添加交易單號 cValue.put("transid","9551620200307"); //添加消費金額 cValue.put("amount",100.00); //添加交易日期 cValue.put("date",2020-03-07); //調用insert()方法插入數據 db.insert("translog",null,cValue);3.4 更新數據
更新數據當然也可用原生SQL語句然后使用execSQL() 方法可以達到目的,或者使用update()函數,函數的說明如下
int update(String table, ContentValues values, String whereClause, String[] whereArgs)- 參數1 表名;
- 參數2 ContentValues類型數據;
- 參數3 WHERE表達式(String),指定需進行數據更新的行,其中?號是占位符,如果傳遞null將更新所有行;
- 參數4 填充第三個參數里的占位符,String[]數組類型;
例如想更新交易記錄***9551620200307***的消費金額,可用如下代碼實現
ContentValues replaceAmount = new ContentValues(); //要修改的金額 replaceAmount.put("amount","50.00"); //指定第3個參數里占位符?的內容,最后將翻譯成transid=9551620200307 String[] parms=new String[] {"9551620200307"}; //執行更新 db.update("translog", replaceAmount, "transid=?", parms);3.5 刪除數據
經過熟悉增改函數后,發現函數都是有固定討論的,刪除函數delete和update函數參數很像,只不過少了要增加的參數,因為刪除是破壞,破壞起來簡單。
public int delete (String table, String whereClause, String[] whereArgs)- 參數1 表名;
- 參數2 WHERE表達式(String),指定需進行刪除的行,其中?號是占位符,如果傳遞null將刪除所有行;
- 參數3 填充第二個參數里的占位符,String[]數組類型;
例如想刪除交易記錄***9551620200307***,可用如下代碼實現
//指定第3個參數,也就是占位符?的內容,最后將翻譯成transid=9551620200307 String[] parms=new String[] {"9551620200307"}; //執行更新 db.delete("translog", "transid=?", parms);3.6 查詢數據
查詢方法放到最后,因為查詢比較復雜,如果是查詢全部數據還好,否則要指定各種條件。和前面的功能一樣,查詢也有兩種方式,但要注意的是,原生查詢SQL不支持db.execSQL,而是使用單獨為查詢設置的db.rawQuery方法,rawQuery方法兩種類型,其中簡單一點的原型如下:
Cursor rawQuery (String sql, String[] selectionArgs)其中第一個參數是SQL語句,第二個參數用于填充第一個參數里的占位符。rawQuery返回Cursor,最后再通過Cursor找到想要的查詢結果。示例代碼如下:
Cursor cursor = db.rawQuery("select * from transid",null); while(cursor.moveToNext()){int id=cursor.getInt(cursor.getColumnIndex("id"));String transid=cursor.getString(cursor.getColumnIndex("transid"));String amount=cursor.getString(cursor.getColumnIndex("amount"));String date=cursor.getString(cursor.getColumnIndex("date")); }rawQuery比較適用于簡單查詢,如果要設置更多條件,使用這個方法將會非常復雜。這個時候建議使用query方法,雖然參數比前面介紹的數據庫增刪改要多得多,但要比原生SQL查詢語句可讀性強。
來看其中一個query原型:
Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit);參數說明
- distinct:指定是否過濾結果里的重復值,true過濾,false不過濾;
- table:表名
- columns:指定想要查詢的列的名稱集;
- selection:WHERE之后的條件語句,可以使用占位符
- selectionArgs:如果selection里有占位符,使用該參數填充
- groupBy:指定分組的列名對,指定的話結果將按照列名分組
- having:指定分組條件,配合groupBy使用
- orderBy:指定排序的列名
- limit:用于限定返回結果行數
除了table必須指定,其他參數都可以設置為null,這樣等同于一句簡單的select * from table。
結束語
SQLite因性能優秀占用體積小且開源被Android采用,如果Android程序需要保存大量數據,選擇使用SQLite是一個明智的方案,Android針對SQLite封裝了簡單易用的API,其中SQLiteOpenHelper類用于創建和更新數據庫,通過SQLiteOpenHelper,開發者不用糾結于創建數據庫連接等繁瑣的操作。在Android上,既可以選擇使用原生SQL語句,也可以使用封裝好的API操作,API包含了數據庫的增刪改查基本操作,通過使用API,不僅方便開發者,也增強了程序的可讀性。不過,Android封裝的API還是有點繁瑣,目前已有大量好用的第三方庫,可以更加方便操作數據庫,這將會在后續介紹。
總結
以上是生活随笔為你收集整理的Android持久化存储(3)SQLite数据库的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android持久化存储(2)Share
- 下一篇: android sina oauth2.