page.title=在 SQL 資料庫中儲存資料 page.tags=資料儲存空間 helpoutsWidget=true trainingnavtop=true @jd:body

本課程示範

  1. 定義結構描述與合約
  2. 使用 SQL Helper 建立資料庫
  3. 將資訊置入資料庫中
  4. 讀取資料庫中的資訊
  5. 刪除資料庫中的資訊
  6. 更新資料庫

您也應該閱讀

對於重複資料或結構化資料 (例如連絡資訊),將其儲存至資料庫是理想的選擇。 本課程假設您已大體熟悉 SQL 資料庫,並協助您開始在 Android 上使用 SQLite 資料庫。 {@link android.database.sqlite} 套件中提供在 Android 上使用資料庫所需的 API。

定義結構描述與合約

SQL 資料庫的其中一項主要準則是結構描述,即針對資料庫組織方式的正式宣告。 在您建立資料庫所用的 SQL 陳述式中,會反映結構描述。 您可能會發現,建立伴隨類別 (也稱為合約類別) 會非常有益,該類別會以系統化的自我記錄方式,明確指定結構描述的配置。

合約類別是常數 (可為 URI、表格與欄定義名稱) 的容器。 藉由合約類別,您可在同一套件內的所有其他類別中使用相同的常數。 您可藉此在一個位置變更欄名稱,然後將其傳播到全部程式碼中。

組織合約類別的良好方式,是將適用於整個資料庫的全域定義置於類別的根層級, 然後,針對列舉欄的每個表格建立內部類別。

注意:透過實作 {@link android.provider.BaseColumns} 介面,您的內部類別可繼承主索引鍵欄位 (稱為 {@code _ID}),某些 Android 類別 (諸如 cursor adaptor) 希望其具備該欄位。 此操作並非必需的操作,但是可協助您的資料庫與 Android 架構協調運作。

例如,以下程式碼片段會定義單一表格的表格名稱與欄名稱:

public final class FeedReaderContract {
    // To prevent someone from accidentally instantiating the contract class,
    // give it an empty constructor.
    public FeedReaderContract() {}

    /* Inner class that defines the table contents */
    public static abstract class FeedEntry implements BaseColumns {
        public static final String TABLE_NAME = "entry";
        public static final String COLUMN_NAME_ENTRY_ID = "entryid";
        public static final String COLUMN_NAME_TITLE = "title";
        public static final String COLUMN_NAME_SUBTITLE = "subtitle";
        ...
    }
}

使用 SQL Helper 建立資料庫

定義資料庫的外觀之後,您應實作多種方法以建立並維護資料庫與表格。 以下所示是可建立及刪除表格的某些典型陳述式:

private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";
private static final String SQL_CREATE_ENTRIES =
    "CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
    FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
    FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
    ... // Any other options for the CREATE command
    " )";

private static final String SQL_DELETE_ENTRIES =
    "DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;

如同在裝置的內部儲存空間儲存檔案一樣,Android 會將您的資料庫儲存在與應用程式關聯的私用磁碟空間內。 依預設,其他應用程式無法存取此區域,因此您的資料安全無虞。

{@link android.database.sqlite.SQLiteOpenHelper} 類別中提供一組有用的 API。若使用此類別取得資料庫的參考,只有在需要執行且並非處於應用程式啟動期間時,系統才會執行資料庫的建立與更新操作 (執行時間可能很長)。 您只需呼叫 {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} 或 {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} 即可。

注意:由於這些操作的時間可能很長,因此請確保您在背景執行緒 (例如 {@link android.os.AsyncTask} 或 {@link android.app.IntentService}) 中呼叫 {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} 或 {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}。

若要使用 {@link android.database.sqlite.SQLiteOpenHelper},請建立可覆寫 {@link android.database.sqlite.SQLiteOpenHelper#onCreate onCreate()}、{@link android.database.sqlite.SQLiteOpenHelper#onUpgrade onUpgrade()} 與 {@link android.database.sqlite.SQLiteOpenHelper#onOpen onOpen()} 回呼方法的子類別。 您還可以實作 {@link android.database.sqlite.SQLiteOpenHelper#onDowngrade onDowngrade()},但這並非必需的操作。

例如,以下展示了 {@link android.database.sqlite.SQLiteOpenHelper} (使用上述某些命令) 的實作:

public class FeedReaderDbHelper extends SQLiteOpenHelper {
    // If you change the database schema, you must increment the database version.
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "FeedReader.db";

    public FeedReaderDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_ENTRIES);
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // This database is only a cache for online data, so its upgrade policy is
        // to simply to discard the data and start over
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        onUpgrade(db, oldVersion, newVersion);
    }
}

若要存取您的資料庫,請啟動 {@link android.database.sqlite.SQLiteOpenHelper} 的子類別:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

將資訊置入資料庫中

透過將 {@link android.content.ContentValues} 物件傳遞至 {@link android.database.sqlite.SQLiteDatabase#insert insert()} 方法,可將資料插入至資料庫:

// Gets the data repository in write mode
SQLiteDatabase db = mDbHelper.getWritableDatabase();

// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_CONTENT, content);

// Insert the new row, returning the primary key value of the new row
long newRowId;
newRowId = db.insert(
         FeedEntry.TABLE_NAME,
         FeedEntry.COLUMN_NAME_NULLABLE,
         values);

{@link android.database.sqlite.SQLiteDatabase#insert insert()} 的第一個引數即為表格名稱。 第二個引數將提供欄的名稱, 在 {@link android.content.ContentValues} 為空時,架構可在該欄中插入 NULL (若您將其設為 {@code "null"},則在無值時,架構不會插入列)。

讀取資料庫中的資訊

若要從資料庫進行讀取,請使用 {@link android.database.sqlite.SQLiteDatabase#query query()} 方法,然後向其傳遞您的選取條件與所需的欄。該方法會合併 {@link android.database.sqlite.SQLiteDatabase#insert insert()} 與 {@link android.database.sqlite.SQLiteDatabase#update update()} 的元素,對您希望擷取的資料 (而非要插入的資料) 進行定義的欄清單除外。 將在 {@link android.database.Cursor} 物件中,為您傳回查詢結果。

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
    FeedEntry._ID,
    FeedEntry.COLUMN_NAME_TITLE,
    FeedEntry.COLUMN_NAME_UPDATED,
    ...
    };

// How you want the results sorted in the resulting Cursor
String sortOrder =
    FeedEntry.COLUMN_NAME_UPDATED + " DESC";

Cursor c = db.query(
    FeedEntry.TABLE_NAME,  // The table to query
    projection,                               // The columns to return
    selection,                                // The columns for the WHERE clause
    selectionArgs,                            // The values for the WHERE clause
    null,                                     // don't group the rows
    null,                                     // don't filter by row groups
    sortOrder                                 // The sort order
    );

若要查看游標指示的列,請使用其中一種 {@link android.database.Cursor} move 方法,您必須始終先呼叫該方法,然後再開始讀取值。 一般而言,您應呼叫 {@link android.database.Cursor#moveToFirst} 來執行啟動,如此會將「讀取位置」置於結果中的第一個項目。 對於每列,您可以呼叫其中一種 {@link android.database.Cursor} get 方法 (例如 {@link android.database.Cursor#getString getString()} 或 {@link android.database.Cursor#getLong getLong()}),以讀取欄的值。 對於每種 get 方法,您必須傳遞所需欄的索引位置,可以呼叫 {@link android.database.Cursor#getColumnIndex getColumnIndex()} 或 {@link android.database.Cursor#getColumnIndexOrThrow getColumnIndexOrThrow()} 取得該位置。例如:

cursor.moveToFirst();
long itemId = cursor.getLong(
    cursor.getColumnIndexOrThrow(FeedEntry._ID)
);

刪除資料庫中的資訊

若要刪除表格中的列,您需要提供識別這些列的選取條件。 資料庫 API 可提供建立選取條件的機制 (能防止 SQL 插入)。 該機制會將選取規格分為選取子句與選取引數。 子句可定義要查看的欄,您也可以藉此合併欄測試。 引數是要測試的值,繫結在子句中。由於對結果的處理方式不同於規則 SQL 陳述式,因此結果不會遭受 SQL 插入。

// Define 'where' part of query.
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { String.valueOf(rowId) };
// Issue SQL statement.
db.delete(table_name, selection, selectionArgs);

更新資料庫

若您需要修改資料庫值的子集,請使用 {@link android.database.sqlite.SQLiteDatabase#update update()} 方法。

更新表格會合併 {@link android.database.sqlite.SQLiteDatabase#insert insert()} 的內容值語法與 {@link android.database.sqlite.SQLiteDatabase#delete delete()} 的 {@code where} 語法。

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// New value for one column
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);

// Which row to update, based on the ID
String selection = FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selectionArgs = { String.valueOf(rowId) };

int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs);