SQLite資料庫介紹

SQlite簡介

SQLite是一般常用在嵌入式系統的資料庫,特色為支援精簡「SQL」指令、效能好、相對其它資料庫使用更少的記憶體,因為所有資料都儲存在一個檔案當中,所以在移轉資料時也非常便利。

在使用SQLite時,大部份為以下四個動作:
  • 新增記錄(Insert)。
  • 查詢紀錄(Query)。
  • 更改紀錄(Update)。
  • 刪除記錄(Delete)。

在Android下使用SQLite資料庫的方式根據是否要共享資料庫來設計存取的方式:
  • 不打算共享時,可以透過SQLite的API直接存取資料庫。
  • 要與其它應用程式共享資料時則必須透過內容提供者(Content Provider)來存取資料庫。


透過SQLiteOpenHelper操作資料庫

Android應用程式可以透過繼承SQLiteOpenHelper類別來建立自己的應用程式當中操作資料庫的類別。

  1. 在專案名稱上點擊滑鼠右鍵->New->Java Class。
  2. 在建立的新類別並繼承自SQLiteOpenHelper類別。 如:
    private class MyDBHelper extends SQLiteOpenHelper
    {
    }
    
    當輸入完上面程式碼後,在SQLiteOpenHelper下會出現紅色波浪的錯誤,表示未導入(import)該類別庫,可以將滑鼠移到該紅線下然後選取import SQLiteOpenHelper
  3. 在完成上面步驟後,會發現MyDBHelper下出現紅色波浪,這是因為SQLiteOpenHelper需要特定參數的建構式,將滑鼠移動到該紅線上,選取「Add constructor MyDBHelper(Context, String, CursorFactory, int)」後,Eclipse就會自動幫我們加入該建構式的程式碼:
    private class MyDBHelper extends SQLiteOpenHelper
    {
        public MyDBHelper(Context context, String name, CursorFactory factory, int version) 
        {
            super(context, name, factory, version);
            // TODO Auto-generated constructor stub
        }
    }
    
    建構式參數說明:
    context可以操作資料庫的內容本文,一般可直接傳入Activity物件。
    name要操作資料庫名稱,如果資料庫不存在,會自動被建立出來並呼叫onCreate()方法。
    factory用來做深入查詢用,入門時用不到。
    version版本號碼。
  4. 這時,會發現錯誤還沒消失,這是因為「SQLiteOpenHelper」還需要加入兩個方法,再次移到紅色波浪線上選取「Add upimplemented methods」,程式碼會變成:
    private class MyDBHelper extends SQLiteOpenHelper
    {
        public MyDBHelper(Context context, String name, CursorFactory factory, int version) 
        {
            super(context, name, factory, version);
            // TODO Auto-generated constructor stub
        }
    
        @Override
        public void onCreate(SQLiteDatabase db)
        {
            // TODO Auto-generated constructor stub
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
        {
            // TODO Auto-generated constructor stub
        }
    }
    
    onCreate()onUpgrade()這兩個方法用來處理建立資料表和變新資料表的動作,而這裡個方法被呼叫的時機為:
    • onCreate():當應用程式第一次執行時,如果資料庫不存在,則SQLiteOpenHelper會自動建立資料庫並呼叫onCreate()方法。
    • onUpgrade():當應用程式中建立繼承自SQLiteOpenHelper類別時,建構式第四個參數的版本如果變更了,該方法就會被呼叫。

  5. onCreate()方法內建立資料表:
    // 當資料庫不存在的時候, Android系統自動建立資料庫時, 這個方法會被觸發
    @Override
    public void onCreate(SQLiteDatabase db) 
    {
        // 建立要存放資料的資料表格(相當於Excel的sheet)
        // 1. SQL語法不分大小寫
        // 2. 這裡大寫代表的是SQL標準語法, 小寫字是資料表/欄位的命名
        db.execSQL("CREATE TABLE mytable(_id INTEGER PRIMARY KEY AUTOINCREMENT," +
                                         "name TEXT NOT NULL," +
                                         "content TEXT," + 
                                         "number INTEGER)");
    }
    

    當系統自動幫我們建立資料庫後會呼叫onCreate()方法,並傳入一個SQLiteDatabase物件,透過呼叫該物件的execSQL()方法,我們可以傳入一串SQL語法,用該語法來建立資料表(table)

    CREATE TABLE為SQL語法,代表要建立資料表。
    mytable我們要建立的資料表名稱。
    _id, name, content, number我們要建立的欄位名稱,這裏總共會建立四個欄位來存放一筆資料。
    INTEGER, TEXT, REAL每個欄位要存放的資料類型,INTEGER代表該欄位存放的是數字,TEXT代表是字串,REAL代表小數數字。
    PRIMARY KEY AUTOINCREMENTSQLite資料庫每一筆資料都要有的主鍵,裡面的資料是唯一的。
    NOT NULL該欄位的資料不可以為空,如果新增一筆資料時,沒有指定該欄位值,將會發生錯誤。
  6. 當應用程式因為改版而修改了資料庫內的表格結構時,因為onCreate()只有在第一次建立資料庫時會被呼叫一次而已,如果想要修改資料表,我們可以透過在建立SQLiteOpenHelper物件時,可以增加版本號碼,這樣onUpgrade()方法就會被呼叫,然後我們可以在onUpgrade()做資料表更改的動作。

    一般步驟為:
    1. 修改onCreate()建立新的資料表。
    2. onUpgrade()呼叫onCreate()建立新版的資料表。
    3. 將資料從就的資料表讀到新的資料表。
    4. 刪除舊的資料表。例:
      db.execSQL("DROP TABLE IF EXISTS mytable");

      onUpgrader()方法內,我們會多收到兩個參數,oldVersion為目前的資料庫版本,newVersion為新的版本,我們也可以根據自己定義的版本號碼來決定要怎麼修改資料庫。

    根據Android官方文件,onCreateonUpgrade方法會在第一次呼叫。

    原文網址

    注意

    onCreateonUpgrade方法內,你只能使用透過該方法傳進來的SQLiteDatabase物件來進行資料庫操作,且不需要去關閉(close())它。

操作資料庫

要對資料庫做操作前,我們必須要先取得SQLiteDatabase物件,我們可以透過SQLiteOpenHelper來取得兩種資料庫物件:

  • getWritableDatabase():取得可以寫入的資料庫物件。
  • getReadableDatabase():取得可以只能讀取的資料庫物件。

通常,如果你只是要查詢資料,並沒有要新增、修改或刪除資料的話,建議使用唯讀的資料庫物件就好了,因為當資料庫空間滿了,呼叫getWritableDatabase()方法將會造成例外,而getReadableDatabase()卻是沒問題的。

根據Android官方文件,兩個方法其實都取得的物件都可以讀寫資料庫,也就是其實getReadableDatabase()方法呼叫時也是使用getWritableDatabase()的方式來取得資料庫,不同的地方在於當你呼叫getWritableDatabase()時如果資料庫滿了,將會直接產生例外,而getReadableDatabase()方法內部會改為getReadableDatabase()方式來呼叫。
SQLiteDatabase提供四種方法包裝了SQL語法來讓五們更容易操作資料庫:
insert插入一筆新的資料
query查詢資料
update修改資料
delete刪除資料

新增資料(Insert)

public void add(String name, String content, int number)
{
    // 取出資料庫物件, 並且是可以寫入狀態
    // 當APP空間不夠時, 該方法會丟出例外
    SQLiteDatabase db = helper.getWritableDatabase();

    // 定義要新增的資料
    ContentValues values = new ContentValues();
    values.put("name", name);
    values.put("content", content);
    values.put("number", number);

    // 新增一筆資料到資料表(Table)
    db.insert("vocs", null, values);

    // 釋放SQLiteDatabase資源
    db.close();
}

我們必須使用ContentValues物件來存放要新增的一筆資料,透過呼叫put來加入每一個欄位要存放的資料,第一個參數是欄位名稱,第二個參數是要放入的資料;最後透過呼叫SQLiteDatabase.insert()方法來插入一筆資料到資料庫,該方法需要三個參數,第一個是要插入的資料表名稱,第二個參數不重要,一般給null即可,第三個參數則是要插入的ContentValues物件。

查詢資料(Query)

public class MyData
{
    public int _id;
    public String name;
    public String content;
    public long number;
}

public ArrayList getAllData()
{
    ArrayList result = new ArrayList();

    // 取得唯讀模式資料庫
    SQLiteDatabase db = helper.getReadableDatabase();

    // 透過query來查詢資料
    Cursor c = db.query("mytable",                                         // 資料表名字 
                        new String[]{"_id", "name", "content", "number"},  // 要取出的欄位資料 
                        null,                                              // 查詢條件式 
                        null,                                              // 查詢條件值字串陣列
                        null,                                              // Group By字串語法 
                        null,                                              // Having字串法 
                        "name",                                            // Order By字串語法(排序) 
                        null);                                             // Limit字串語法

    while(c.moveToNext())
    {
        MyData d = new MyData();
        d.id = c.getInt(c.getColumnIndex("_id"));    // 取出名字欄位資料
        d.name = c.getString(c.getColumnIndex("name"));    // 取出名字欄位資料
        d.content = c.getString(c.getColumnIndex("content"));    // 取出名字欄位資料
        d.number = c.getLong(c.getColumnIndex("number"));     // 取出翻卡片次數資料
        result.add(d);
    }

    // 釋放資源
    c.close();
    db.close();

    return result;
}

透過呼叫SQLiteDatabase.query()方法來查詢資料,該方法需要八個參數,如果不需要用到的參數,可直接傳入null

table要查詢的資料表名稱。
columns要取得的資料欄位。
selection查詢的條件(等同於SQL語法的WHERE)
selectionArgs查詢的條件參數(WHERE語法的參數)
groupBy等同於SQL的GROUP BY語句
having函數條件(等同於SQL的HAVING語句)
order-by排序方式(等同於SQL的ORDER BY語句)
limit限制要取得的資料筆數(等同於SQL的LIMIT語句)
注意:當使用到selection參數時,如果該欄位為字串型態,則等於(=)的右邊值需使用單引號包圍,如:
db.query("mytable", null, "name='aaron'", null, null, null, null, null);

query方法會回傳一個Cursor物件,透過呼叫Cursor.getCount()來知道總共取得了幾筆資料。

Cursor取得後,他的指標是在第-1筆,也就是直接利用Cursor.getString(0)等方式來取資料會出現錯誤。必須先使用Cursor.moveToFirst()到第一筆或是Cursor.moveToNext()到下一筆的方式將指標移至資料第0筆,當moveToNext()回傳false則表示已經讀到資料底了。

接著透過Cursor.getString()Cursor.getInt()Cursor.getLong()等方法,並傳入欄位索引值就可以取得該欄位資料,如果不知道該欄位索引值,可以透過呼叫Cursor.getColumnIndex()方法,並傳入該欄位名稱即可取得欄位索引值。

修改資料(Update)

public void update(String name, MyData d)
{
    // 取出資料庫物件, 並且是可以寫入狀態
    // 當APP空間不夠時, 該方法會丟出例外
    SQLiteDatabase db = helper.getWritableDatabase();

    // 定義要新增的資料
    ContentValues values = new ContentValues();
    values.put("name", d.name);
    values.put("content", d.content);
    values.put("number", d.number);

    // 更新資料到資料表(Table)
    db.update("vocs", values, "name='" + name + "'", null);

    // 釋放資源
    db.close();
}

SQLiteDatabase.update()方法需要三個參數,第一個為要更新的資料表,第二個參數為要更新的資料條件句(等同於SQL的WHERE語句),規則與query()方法的第三個參數相同,第三個參數為WHERE語句的參數。

注意:當你的第二個參數為null時表示要修改整個資料表的資料,也就是每一筆資料都會被改成一模一樣。

刪除資料(Delete)

範例:
public void delete(String voc)
{
    SQLiteDatabase db = helper.getReadableDatabase();
    db.delete("mytable", "name='" + name + "'", null);
    db.close();
}

刪除資料的SQLiteDatabase.delete()方法只有三個參數,第一個是要刪除資料的資料表,第二個參數是要刪除資料的條件句(也就是SQL的WHERE語句)該參數的規則與查詢和修該資料相同,第三個參數為WHERE語句的參數。

注意:當你的第二個參數為null時表示要刪除整個資料表的資料。

將SQLite資料庫檔案由Android裝置或模擬器中取出

# adb devices
# adb -s <device-no> shell
# run-as <your-package-name> 
# cd /data/data/<your-package-name>/databases/
adb shell "run-as <package-name> chmod 777 /data/data/<package-name>/databases/<file-name>"
adb pull /data/data/<package-name>/databases/<file-name"
adb shell "run-as <package-name> chmod 600 /data/data/<package-name>/databases/<file-name>"

Android 5.x+

adb shell "run-as {package-name} chmod 777 /data/data/{package-name}/databases/{file-name}"
adb exec-out run-as {package-name} cat databases/{file-name} > {output-file-name}
adb shell "run-as {package-name} chmod 600 /data/data/{package-name}/databases/{file-name}"

操作SQlite3命令列工具

執行SQlite3

# sqlite3 <your-db-name>

列出資料庫表單

.tables

顯示標單欄位結構

.schema <tablename>

顯示表單內所有資料

SELECT * FROM <tablename>;

取得SQlite3說明

.help

離開SQLite3

.exit