Android入門筆記 - 資料存放區 - SQLite,單元測試
今天我們來接觸一個輕輕輕量級資料庫(SQLite),為什麼要加3個輕呢?因為它確實很輕。 Sqlite是專門未嵌入式裝置準備的輕量級資料庫,麻雀雖小,五髒俱全,sqlite的功能卻一點都不少。它和其他的資料庫:MySql,SqlServer,Oracle等資料庫的最大區別我覺得就是Sqlite只能運行在終端,不能用在伺服器上,這也體現了它為嵌入式裝置工作的初衷。
好了,來看看今天的內容:
SQLiteAndroid單元測試
今天我們不僅要介紹sqlite,還要簡單介紹一下怎麼使用android的單元測試。如果不知道什麼是單元測試,那就去找google吧!
在android中使用 sqlite資料庫,一般要通過一個協助類: SQLiteOpenHelper,這個類中有兩個方法:onCreate() ,和 onUpgrade()。
onCreate() 在資料庫第一次被建立時才調用。onUpgrade()當資料庫版本更新時調用。
是什麼意思呢?來看看代碼:
package db;import android.content.ContentValues;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteDatabase.CursorFactory;import android.database.sqlite.SQLiteOpenHelper;import android.transition.Transition;import android.util.Log;public class DBOpenHelper extends SQLiteOpenHelper {private static final String TAG = "DBOpenHelper";private Context mContext;private String mDBname;private String mTableName = "students";private SQLiteDatabase mDatabase;private int mVersion;private String CREATE_TABLE = "create table if not exists " + mTableName+ " (_id integer primary key, name text)";public DBOpenHelper(Context context, String dbname, CursorFactory factory,int version) {super(context, dbname, factory, version);this.mContext = context;this.mDBname = dbname;this.mVersion = version;}@Overridepublic void onCreate(SQLiteDatabase db) {Log.e(TAG, "onCreate");db.execSQL(CREATE_TABLE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {Log.e(TAG, "onUpgrade");}public long insert(String data) {if (mDatabase == null) {mDatabase = getWritableDatabase();}ContentValues values = new ContentValues();values.put("name", data);return mDatabase.insert(mTableName, "_id", values);}public int delete(int id) {if (mDatabase == null) {mDatabase = getWritableDatabase();}return mDatabase.delete(mTableName, "_id" + "=" + id, null);}public int update(int id, String data) {if (mDatabase == null) {mDatabase = getWritableDatabase();}ContentValues values = new ContentValues();values.put("name", data);return mDatabase.update(mTableName, values, "_id" + "=" + id, null);}public Cursor getItemAt(int id) {if (mDatabase == null) {mDatabase = getWritableDatabase();}Cursor cursor = mDatabase.query(mTableName, new String[] { "_id","name" }, "_id" + "=" + id, null, null, null, null, null);if (cursor != null) {cursor.moveToFirst();}return cursor;}public Cursor getAll() {if (mDatabase == null) {mDatabase = getWritableDatabase();}Cursor cursor = mDatabase.query(mTableName, new String[] { "_id","name" }, null, null, null, null, null);return cursor;}}在使用SqliteOpenHelper時,這三個方法是必須的:
public DBOpenHelper(Context context, String dbname, CursorFactory factory,int version) {super(context, dbname, factory, version);this.mContext = context;this.mDBname = dbname;this.mVersion = version;}@Overridepublic void onCreate(SQLiteDatabase db) {Log.e(TAG, "onCreate");db.execSQL(CREATE_TABLE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {Log.e(TAG, "onUpgrade");}構造方法完成 Context, 資料庫名稱,資料庫版本 的初始化,CursorFactory一般用不到,直接穿為null。
在onCreate()我們調用 db.execSQl( CREATE_TABLE) ; 初始化了一張表 “students”,那麼這個DBOpenHelper類也就是為這張表工作了。
至於 onUpdrade()是在調用 建構函式時,version的值 大於 上一個值的時候就會被觸發,然後 onUpgrade()中主要做表的資料修改列啊,備份資料等工作,這裡不講。
然後我們來看看資料庫的增刪改查是怎麼完成的:
增:
使用android提供的ContentValues 可以輕易的初始化要添加的對象,它相當於一個鍵值對,如果有多項,依次用put方法添加就可以了,注意key一定要與資料庫中的列對應。
這裡的 mDatabase 是一個SqliteDatabase 對象,專門用來操作資料庫的,可通過 SqliteOpenHelper. getWritableDatabase()獲得,然後通過調用 insert()方法就可以插入資料。這裡大家需要注意,使用sqlite時,表中必須有一個欄位叫 “_id",而且是主鍵自增長,這就是insert的第二個參數的值。
相當於執行 sql語句:
insert into students(_id, name) values(id, name);
public long insert(String data) {if (mDatabase == null) {mDatabase = getWritableDatabase();}ContentValues values = new ContentValues();values.put("name", data);return mDatabase.insert(mTableName, "_id", values);}
刪:
第二個參數是字串,相當於: "where _id=id " 只是 delete方法自己做了拼接。
相當於執行 sql語句:
delete from students where _id=id;
public int delete(int id) {if (mDatabase == null) {mDatabase = getWritableDatabase();}return mDatabase.delete(mTableName, "_id" + "=" + id, null);}
改:
update方法就需要提供 條件, 以及修改的值,對應於 values 和 ”_id"=id 兩個參數。
相當於執行 sql語句:
update students where _id=id set name = "data";
public int update(int id, String data) {if (mDatabase == null) {mDatabase = getWritableDatabase();}ContentValues values = new ContentValues();values.put("name", data);return mDatabase.update(mTableName, values, "_id" + "=" + id, null);}
查:
尋找需要注意的是android中使用到了遊標作為結果集(java中的ResultSet),如果找到,返回到遊標在第一行資料的前面,所以需要先調用一次 cursor.moveToFirst() 或者 cursor.moveToNext() ,這樣就可以指向第一行資料。
public Cursor getItemAt(int id) {if (mDatabase == null) {mDatabase = getWritableDatabase();}Cursor cursor = mDatabase.query(mTableName, new String[] { "_id","name" }, "_id" + "=" + id, null, null, null, null, null);if (cursor != null) {cursor.moveToFirst();}return cursor;}public Cursor getAll() {if (mDatabase == null) {mDatabase = getWritableDatabase();}Cursor cursor = mDatabase.query(mTableName, new String[] { "_id","name" }, null, null, null, null, null);return cursor;}
好了,sqlite的操作看完了,我們來看看怎麼使用 DBOpenHelper這個類。這個時候我們使用到了Android的單元測試。
Android單元測試的配置方法:
1. 在AndroidMainefest.xml 檔案中添加註冊。
標籤之間添加:
根標籤下添加: android:name="android.test.InstrumentationTestRunner"> (注意加粗的部分填寫程式第一個Activity啟動的所在包名)
2. 測試類別必須繼承與 AndroidTestCase。
3. 每個測試方法都以 test+要測試的方法 名作為測試方法名, 並且需要拋出異常。
我們來看看代碼:
package Test;import db.DBOpenHelper;import android.database.Cursor;import android.test.AndroidTestCase;import android.util.Log;public class TestDBOpenHelper extends AndroidTestCase {private static final String TAG = "TestDBOpenHelper";public void TestInsert() throws Throwable{Log.e(TAG,"TestInsert");DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1);dbopenhelper.insert("stu1");dbopenhelper.insert("stu2");dbopenhelper.insert("stu3");dbopenhelper.insert("stu4");dbopenhelper.insert("stu5");}public void TestDelete(){DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1);dbopenhelper.delete(1);dbopenhelper.delete(2);}public void TestUpdate(){DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1);dbopenhelper.update(3, "333");dbopenhelper.update(4, "444");}public void TestGetItemAtId(){Log.e(TAG,"TestGetItemAtId");DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1);Cursor cursor = dbopenhelper.getItemAt(3);Log.e(TAG,"_id="+cursor.getInt(0));Log.e(TAG,"name="+cursor.getString(1));}public void TestGetAll(){Log.e(TAG,"TestGetAll");DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1);Cursor cursor = dbopenhelper.getAll();while(cursor.moveToNext()){Log.e(TAG,"_id="+cursor.getInt(0));Log.e(TAG,"name="+cursor.getString(1));}}}
寫好之後我們就可以啟動單元測試來測試我們寫的代碼:
如果正確就會綠條,有異常會顯示紅條:
單元測試的好處多多,關心代碼品質的同志們都應該學會怎麼使用單元測試,它是為了驗證每個代碼塊都能工作正常而產生的。
測試代碼應該很容易就看懂了:
這是在測試添加資料,只需要new一個 helper對象,然後調用insert方法就可以了,是不是很簡單:
測試插入:
public void TestInsert() throws Throwable{Log.e(TAG,"TestInsert");DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1);dbopenhelper.insert("stu1");dbopenhelper.insert("stu2");dbopenhelper.insert("stu3");dbopenhelper.insert("stu4");dbopenhelper.insert("stu5");}
測試刪除:
public void TestDelete(){DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1);dbopenhelper.delete(1);dbopenhelper.delete(2);}
測試尋找:
public void TestGetAll(){Log.e(TAG,"TestGetAll");DBOpenHelper dbopenhelper = new DBOpenHelper(mContext, "stu.db", null, 1);Cursor cursor = dbopenhelper.getAll();while(cursor.moveToNext()){Log.e(TAG,"_id="+cursor.getInt(0));Log.e(TAG,"name="+cursor.getString(1));}}剛剛我們看到,getAll() 函數返回到是一個 cursor,我們可以通過迴圈遍曆每個cursor中的對象。 得到cursor某一列的值 可通過 cursor.getXXX( index ) 來擷取,index是column的編號,從0開始,當然也可以通過列的名字來得到。總的來說 Cursor的一行就像一個Map一樣,存放資料庫中的一行資料的鍵值對。
好了,終於完了,最後還是要說一下,上面的例子是很不規範的,當然只是為了體現應該怎麼來使用Sqlite存取資料,在真實的開發中,我們傳入的內容應該是 一個對象,這裡需要和JavaBean結合,就是說資料庫中儲存的資料,其實是針對一個Class的屬性值來儲存的,而不是單個的值。資料庫中的每一行儲存一個對象的所有屬性,當然也可能是幾張表聯合。