iT邦幫忙

DAY 15
2

以「寶寶聯絡簿」為例,適合 Android 初學者的學習筆記系列 第 15

寶寶生活記錄 App (Day15 Database 3)

昨天我們集中在 Non-query SQL 敘述 (insert/update/delete) 的介紹。今天我們會介紹 SQLiteDatabase [1] 針對跟查詢 (Query) 相關的 SQL 敘述所提供的 Methods,包含了 rawQuery 和 query,這兩個 Methods 都會回傳一個 Cursor 物件 [2],因此今天也會對 Cursor 做一個簡單的說明。

首先我們來看今天要討論的程式碼:

package lincyu.babylog.db;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import java.util.ArrayList;

public class BabyDB {

    private static final String BABYID = "_babyid";
    private static final String NAME = "_name";
    private static final String NICKNAME = "_nickname";
    private static final String BIRTHDAY = "_birthday";
    private static final String GENDER = "_gender";
    private static final String HEADSHOT = "_headshot";

    static final String BABY_TABLE = "babytable";
    static final String CREATE_BABY_TABLE = "create table " +
            BABY_TABLE + "(" +
            BABYID + " INTEGER PRIMARY KEY " +
            "AUTOINCREMENT DEFAULT 1 , " +
            NAME + ", " +
            NICKNAME + ", " +
            BIRTHDAY + ", " +
            GENDER + ", " +
            HEADSHOT + ");";

    public synchronized static void addBaby(Context context, Baby baby) {
        addBaby(context, baby.babyid, baby.name, baby.nickname, baby.headshot,
                baby.birthday, baby.gender);
    }

	public synchronized static void addBaby(Context context, int babyid,
            String name, String nickname, String headshot,
            long birthday, int gender) {
		
		DBOpenHelper dbHelper = new DBOpenHelper(context);
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		
		ContentValues cv = new ContentValues();
		
		cv.put(NAME, name);
		cv.put(NICKNAME, nickname);
        cv.put(HEADSHOT, headshot);
        cv.put(BIRTHDAY, birthday);
        cv.put(GENDER, gender);

		if (babyid == -1) { // New Record
			db.insert(BABY_TABLE, null, cv);
		} else { // Existing Record
			db.update(BABY_TABLE, cv, BABYID + "=" + babyid, null);
		}
		db.close();
	}

    public synchronized static void removeBaby(Context context, int babyid) {

        DBOpenHelper dbHelper = new DBOpenHelper(context);
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        db.delete(BABY_TABLE, BABYID + "=" + babyid, null);
        db.close();
    }

    public synchronized static ArrayList<Baby> getAllBabies(Context context) {
        ArrayList<Baby> babylist = new ArrayList<Baby>();

        DBOpenHelper dbHelper = new DBOpenHelper(context);
        SQLiteDatabase db = dbHelper.getWritableDatabase();

        // Cursor c = db.rawQuery("select * from " + BABY_TABLE + ";", null);
        Cursor c = db.query(BABY_TABLE, null, null, null, null, null, null);

        getRecordsKernel(c, babylist);
        c.close();
        db.close();
        return babylist;
    }

    public synchronized static Baby getBabyById(Context context, int babyid) {

        DBOpenHelper dbHelper = new DBOpenHelper(context);
        SQLiteDatabase db = dbHelper.getWritableDatabase();

        Cursor c = db.query(BABY_TABLE, null, BABYID + "=" + babyid, null, null, null, null);
        Baby baby = new Baby();
        if (c.getCount() == 1) {
            c.moveToFirst();
            baby = db2record(c);
        }
        c.close();
        db.close();
        return baby;
    }


	private static void getRecordsKernel(Cursor c, ArrayList<Baby> babylist) {
		c.moveToFirst();
		for (int i = 0; i < c.getCount(); i++) {
			Baby baby = db2record(c);
			babylist.add(baby);
			c.moveToNext();
		}
	}
	
	private static Baby db2record(Cursor c) {
		
		int babyid = -1;
		
		try {
			babyid = c.getInt(c.getColumnIndex(BABYID));
		} catch (Exception e) {
			babyid = -1;
		}
		String name = null;
		try {
			name = c.getString(c.getColumnIndex(NAME));
		} catch (Exception e) {
			name = "";
		}
        String nickname = null;
        try {
            nickname = c.getString(c.getColumnIndex(NICKNAME));
        } catch (Exception e) {
            nickname = "";
        }
        String headshot = null;
        try {
            headshot = c.getString(c.getColumnIndex(HEADSHOT));
        } catch (Exception e) {
            headshot = "";
        }
		long birthday = 0;
		try {
			birthday = c.getLong(c.getColumnIndex(BIRTHDAY));
		} catch (Exception e) {
			birthday = 0;
		}
		int gender = 0;
		try {
			gender = c.getInt(c.getColumnIndex(GENDER));
		} catch (Exception e) {
			gender = Baby.GENDER_UNKNOWN;
		}
		Baby baby = new Baby(babyid, name, nickname, headshot,
                birthday, gender);
		return baby;
	}
}

66 ~ 79 行是對資料庫做「查詢 (Query)」的範例,69 ~ 70 行應該已經不需要說明了,簡單地說就是打開資料庫,並產生一個 SQLiteDatabase 物件,讓我們可以對資料庫進行存取。「查詢 」資料庫的方法有兩種,72 行示範了 rawQuery 的使用方法,rawQuery 的第 1 個參數是一個字串,此字串是一個 SQL 的 select 敘述,由於這系列的文章焦點不再資料庫,因此關於 select 的語法請讀者自行參考相關書籍或網路上的教學文章,至於 rawQuery 的第 2 個參數的用法跟昨天所介紹 whereArgs 是一樣的,select 敘述可搭配 where 設定查詢條件,條件可接問號「?」,這些問號的對應值就是於第 2 個參數中設定。在我們的範例中,我們沒有設定任何條件,也就是要取出所有的記錄,因此第 2 個參數直接填上 null 即可。

除了使用 rawQuery,我們亦可使用 SQLiteDatabase 這個物件的 query 方法來做查詢,query 所需要的參數比較多,query 有多重定義 (Method Overloading),程式中使用的是下面這個定義:

public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)

第 1 個參數是表格名稱,第 2 個參數是想要取出的 Columns,填上 null 代表我們要取出所有欄位,也就是 select *,第 3 和第 4 個參數是跟 where 子句相關的參數,我們已經看過很多次了,就不再敘述,由於我們要取出所有記錄,因此這兩個參數也都填上 null,第 5 和第 6 個參數跟 SQL 語法的 GROUP BY 和 HAVING 相關,由於沒有使用到,因此也是填上 null,最後第 7 個參數則是排序的依據,由於在程式中沒特別去做排序,因此又是 null。事實上第 72 和第 73 行的執行結果會是一樣的。

不管是 rawQuery 或 Query 都會回傳一個 Cursor 物件 [2],讀者可以把 Cursor 想成是指向查詢後新表格的游標,例如原本的表格有 5 欄 6 列,可是執行完 select 後的結果,可能只剩下 3 欄 2 列,Cursor 就是指向這個 3 欄 2 列的新表格,Cursor有許多Methods,有一些Methods是用來存取「欄(Column)」,有一些Methods是用來存取「列(Row)」,例如 getCount 是回傳符合查詢結果的「列」的數目,亦即記錄的數目,若想要得知表格有多少「欄」,則需要呼叫 getColumnCount 方法。

程式 98 ~ 105 行是利用 Cursor 來讀取查詢結果的範例,首先第 99 行呼叫 Cursor 物件的 moveToFirst 讓 Cursor 指向查詢結果的第 1 筆記錄,接著利用 for 回圈一筆一筆的讀取,其中第 103 行的 moveToNext 會讓 Cursor 移動到下一筆記錄,而真正把記錄的值讀出來是寫在 107 ~ 149 行的 db2record,Cursor 類別的 getInt, getLong, getString, getFloat 等 Methods 能讓讀出不同資料型態的值,然而它們所需要的參數都是 columnindex,而我們有的是 Column Name,不過只要利用 Cursor 物件的 getColumnIndex 就能把 Column Name 轉成 Column Index。db2record 會回傳一個 Baby 物件,最後程式第 102 行把這個 Baby 物件加入一個 Baby 動態陣列 (ArrayList)。

相信經過這幾天的介紹,讀者對於 Android 中資料庫的存取方法應該已經有初步的了解,更多關於 Android 資料庫存取的議題可參考讀者的書籍 [3]。

參考資料

[1] SQLiteDatabase | Android Developers, http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html

[2] Cursor | Android Developers, http://developer.android.com/reference/android/database/Cursor.html

[3] 林致宇, Android程式設計入門與應用(附範例光碟), 全華出版社, ISBN: 9789572194126, http://www.opentech.com.tw/search/bookinfo.asp?isbn=9789572194126&companyID=04383129


上一篇
寶寶生活記錄 App (Day14 Database 2)
下一篇
寶寶生活記錄 App (Day16 如何從 assets 資料夾讀取一個已經存在的資料庫)
系列文
以「寶寶聯絡簿」為例,適合 Android 初學者的學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言