| page.title=在 SQL 資料庫中儲存資料 |
| page.tags=資料儲存空間 |
| helpoutsWidget=true |
| |
| trainingnavtop=true |
| |
| @jd:body |
| |
| |
| <div id="tb-wrapper"> |
| <div id="tb"> |
| |
| <h2>本課程示範</h2> |
| <ol> |
| <li><a href="#DefineContract">定義結構描述與合約</a></li> |
| <li><a href="#DbHelper">使用 SQL Helper 建立資料庫</a></li> |
| <li><a href="#WriteDbRow">將資訊置入資料庫中</a></li> |
| <li><a href="#ReadDbRow">讀取資料庫中的資訊</a></li> |
| <li><a href="#DeleteDbRow">刪除資料庫中的資訊</a></li> |
| <li><a href="#UpdateDbRow">更新資料庫</a></li> |
| </ol> |
| |
| <h2>您也應該閱讀</h2> |
| <ul> |
| <li><a href="{@docRoot}guide/topics/data/data-storage.html#db">使用資料庫</a></li> |
| </ul> |
| |
| <!-- |
| <h2>Try it out</h2> |
| |
| <div class="download-box"> |
| <a href="{@docRoot}shareables/training/Sample.zip" class="button">Download the sample</a> |
| <p class="filename">Sample.zip</p> |
| </div> |
| --> |
| |
| </div> |
| </div> |
| |
| |
| <p>對於重複資料或結構化資料 (例如連絡資訊),將其儲存至資料庫是理想的選擇。 |
| 本課程假設您已大體熟悉 SQL 資料庫,並協助您開始在 Android 上使用 SQLite 資料庫。 |
| |
| {@link android.database.sqlite} 套件中提供在 Android 上使用資料庫所需的 API。 |
| </p> |
| |
| |
| <h2 id="DefineContract">定義結構描述與合約</h2> |
| |
| <p>SQL 資料庫的其中一項主要準則是結構描述,即針對資料庫組織方式的正式宣告。 |
| 在您建立資料庫所用的 SQL 陳述式中,會反映結構描述。 |
| 您可能會發現,建立伴隨類別 (也稱為<em>合約</em>類別) 會非常有益,該類別會以系統化的自我記錄方式,明確指定結構描述的配置。 |
| |
| </p> |
| |
| <p>合約類別是常數 (可為 URI、表格與欄定義名稱) 的容器。 |
| 藉由合約類別,您可在同一套件內的所有其他類別中使用相同的常數。 |
| 您可藉此在一個位置變更欄名稱,然後將其傳播到全部程式碼中。 |
| </p> |
| |
| <p>組織合約類別的良好方式,是將適用於整個資料庫的全域定義置於類別的根層級, |
| 然後,針對列舉欄的每個表格建立內部類別。 |
| </p> |
| |
| <p class="note"><strong>注意:</strong>透過實作 {@link |
| android.provider.BaseColumns} 介面,您的內部類別可繼承主索引鍵欄位 (稱為 {@code _ID}),某些 Android 類別 (諸如 cursor adaptor) 希望其具備該欄位。 |
| |
| 此操作並非必需的操作,但是可協助您的資料庫與 Android 架構協調運作。 |
| </p> |
| |
| <p>例如,以下程式碼片段會定義單一表格的表格名稱與欄名稱: |
| </p> |
| |
| |
| <pre> |
| 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"; |
| ... |
| } |
| } |
| </pre> |
| |
| |
| |
| <h2 id="DbHelper">使用 SQL Helper 建立資料庫</h2> |
| |
| <p>定義資料庫的外觀之後,您應實作多種方法以建立並維護資料庫與表格。 |
| 以下所示是可建立及刪除表格的某些典型陳述式: |
| </P> |
| |
| <pre> |
| 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; |
| </pre> |
| |
| <p>如同在裝置的<a href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">內部儲存空間</a>儲存檔案一樣,Android 會將您的資料庫儲存在與應用程式關聯的私用磁碟空間內。 |
| |
| 依預設,其他應用程式無法存取此區域,因此您的資料安全無虞。 |
| </p> |
| |
| <p>{@link |
| android.database.sqlite.SQLiteOpenHelper} 類別中提供一組有用的 API。若使用此類別取得資料庫的參考,只有在需要執行且<em>並非處於應用程式啟動期間</em>時,系統才會執行資料庫的建立與更新操作 (執行時間可能很長)。 |
| |
| |
| |
| 您只需呼叫 {@link android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} 或 |
| {@link android.database.sqlite.SQLiteOpenHelper#getReadableDatabase} 即可。 |
| </p> |
| |
| <p class="note"><strong>注意:</strong>由於這些操作的時間可能很長,因此請確保您在背景執行緒 (例如 {@link android.os.AsyncTask} 或 {@link android.app.IntentService}) 中呼叫 {@link |
| android.database.sqlite.SQLiteOpenHelper#getWritableDatabase} 或 {@link |
| android.database.sqlite.SQLiteOpenHelper#getReadableDatabase}。 |
| |
| </p> |
| |
| <p>若要使用 {@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()},但這並非必需的操作。 |
| |
| </p> |
| |
| <p>例如,以下展示了 {@link |
| android.database.sqlite.SQLiteOpenHelper} (使用上述某些命令) 的實作:</p> |
| |
| <pre> |
| 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); |
| } |
| } |
| </pre> |
| |
| <p>若要存取您的資料庫,請啟動 {@link |
| android.database.sqlite.SQLiteOpenHelper} 的子類別:</p> |
| |
| <pre> |
| FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext()); |
| </pre> |
| |
| |
| |
| |
| <h2 id="WriteDbRow">將資訊置入資料庫中</h2> |
| |
| <p>透過將 {@link android.content.ContentValues} 物件傳遞至 {@link android.database.sqlite.SQLiteDatabase#insert insert()} 方法,可將資料插入至資料庫: |
| </p> |
| |
| <pre> |
| // 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); |
| </pre> |
| |
| <p>{@link android.database.sqlite.SQLiteDatabase#insert insert()} 的第一個引數即為表格名稱。 |
| 第二個引數將提供欄的名稱, |
| 在 {@link android.content.ContentValues} 為空時,架構可在該欄中插入 NULL (若您將其設為 {@code "null"},則在無值時,架構不會插入列)。 |
| |
| </p> |
| |
| |
| |
| |
| <h2 id="ReadDbRow">讀取資料庫中的資訊</h2> |
| |
| <p>若要從資料庫進行讀取,請使用 {@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} 物件中,為您傳回查詢結果。 |
| </p> |
| |
| <pre> |
| SQLiteDatabase db = mDbHelper.getReadableDatabase(); |
| |
| // Define a <em>projection</em> 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 |
| ); |
| </pre> |
| |
| <p>若要查看游標指示的列,請使用其中一種 {@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()} 取得該位置。例如: |
| |
| |
| </p> |
| |
| <pre> |
| cursor.moveToFirst(); |
| long itemId = cursor.getLong( |
| cursor.getColumnIndexOrThrow(FeedEntry._ID) |
| ); |
| </pre> |
| |
| |
| |
| |
| <h2 id="DeleteDbRow">刪除資料庫中的資訊</h2> |
| |
| <p>若要刪除表格中的列,您需要提供識別這些列的選取條件。 |
| 資料庫 API 可提供建立選取條件的機制 (能防止 SQL 插入)。 |
| 該機制會將選取規格分為選取子句與選取引數。 |
| 子句可定義要查看的欄,您也可以藉此合併欄測試。 |
| |
| 引數是要測試的值,繫結在子句中。由於對結果的處理方式不同於規則 SQL 陳述式,因此結果不會遭受 SQL 插入。 |
| |
| </p> |
| |
| <pre> |
| // 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); |
| </pre> |
| |
| |
| |
| <h2 id="UpdateDbRow">更新資料庫</h2> |
| |
| <p>若您需要修改資料庫值的子集,請使用 {@link |
| android.database.sqlite.SQLiteDatabase#update update()} 方法。</p> |
| |
| <p>更新表格會合併 {@link |
| android.database.sqlite.SQLiteDatabase#insert insert()} 的內容值語法與 {@link android.database.sqlite.SQLiteDatabase#delete delete()} 的 {@code where} 語法。 |
| </p> |
| |
| <pre> |
| 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); |
| </pre> |
| |