此章節內容來自Android官網。
定義架構和契約
SQL 數據庫的主要原則之一是架構:數據庫如何組織的正式聲明。 架構體現于您用于創建數據庫的 SQL 語句。您會發現它有助于創建伴隨類,即契約類,其以一種系統性、自記錄的方式明確指定您的架構布局。
契約類是用于定義 URI、表格和列名稱的常數的容器。 契約類允許您跨同一軟件包中的所有其他類使用相同的常數。 您可以在一個位置更改列名稱并使其在您整個代碼中傳播。
組織契約類的一種良好方法是將對于您的整個數據庫而言是全局性的定義放入類的根級別。 然后為枚舉其列的每個表格創建內部類。
注:通過實現 BaseColumns接口,您的內部類可繼承名為 _ID的主鍵字段,某些 Android 類(比如光標適配器)將需要內部類擁有該字段。 這并非必需項,但可幫助您的數據庫與 Android 框架協調工作。
例如,該代碼段定義了單個表格的表格名稱和列名稱:
public final class FeedReaderContract {
// To prevent someone from accidentally instantiating the contract class,
// make the constructor private.
private FeedReaderContract() {}
/* Inner class that defines the table contents */
public static class FeedEntry implements BaseColumns {
public static final String TABLE_NAME = "entry";
public static final String COLUMN_NAME_TITLE = "title";
public static final String COLUMN_NAME_SUBTITLE = "subtitle";
}
}
創建數據庫
在您定義了數據庫的外觀后,您應實現創建和維護數據庫和表格的方法。 這里有一些典型的表格創建和刪除語句:
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_TITLE + TEXT_TYPE + COMMA_SEP +
FeedEntry.COLUMN_NAME_SUBTITLE + TEXT_TYPE + " )";
private static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;
就像您在設備的內部存儲中保存文件那樣,Android 將您的數據庫保存在私人磁盤空間,即關聯的應用。 您的數據是安全的,因為在默認情況下,其他應用無法訪問此區域。
SQLiteOpenHelper類中有一組有用的 API。當您使用此類獲取對您數據庫的引用時,系統將只在需要之時而不是應用啟動過程中執行可能長期運行的操作:創建和更新數據庫。 您僅需調用 getWritableDatabase()或 getReadableDatabase() 即可。
注:由于它們可能長期運行,因此請確保您在后臺線程中調用 getWritableDatabase()或 getReadableDatabase(),比如使用 AsyncTask 或 IntentService。
要使用 SQLiteOpenHelper,請創建一個替換 onCreate()、onUpgrade()和onOpen() 回調方法的子類。您可能還希望實現 onDowngrade(),但這并非必需操作。
例如,下面是一個使用如上所示一些命令的 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);
}
}
要訪問您的數據庫,請實例化 SQLiteOpenHelper的子類:
FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());
增
通過將一個 ContentValues 對象傳遞至 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_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle);
// Insert the new row, returning the primary key value of the new row
long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);
insert()的第一個參數即為表格名稱。
第二個參數將指示框架在 ContentValues為空(即,您沒有 put任何值)時執行哪些操作。如果指定列名稱,則框架將插入一行并將該列的值設置為 null。如果指定 null(就像此代碼示例中一樣),則框架不會在沒有值時插入行。
查
要從數據庫中讀取信息,請使用 query()方法,將其傳遞至選擇條件和所需列。該方法結合 insert()和update()的元素,除非列列表定義了您希望獲取的數據,而不是希望插入的數據。 查詢的結果將在 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_SUBTITLE
};
// Filter results WHERE "title" = 'My Title'
String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
String[] selectionArgs = { "My Title" };
// How you want the results sorted in the resulting Cursor
String sortOrder =
FeedEntry.COLUMN_NAME_SUBTITLE + " 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
);
要查看游標中的某一行,請使用 Cursor 移動方法之一,您必須在開始讀取值之前始終調用這些方法。 一般情況下,您應通過調用 moveToFirst()開始,其將“讀取位置”置于結果中的第一個條目中。 對于每一行,您可以通過調用 Cursor獲取方法之一讀取列的值,比如 getString() 或 getLong()。對于每種獲取方法,您必須傳遞所需列的索引位置,您可以通過調用 getColumnIndex()或 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_TITLE + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { "MyTitle" };
// Issue SQL statement.
db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);
更新數據庫
當您需要修改數據庫值的子集時,請使用 update()方法。更新表可將 insert()的內容值語法與 delete()的 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 title
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
String[] selectionArgs = { "MyTitle" };
int count = db.update(
FeedReaderDbHelper.FeedEntry.TABLE_NAME,
values,
selection,
selectionArgs);