注:本文翻譯自Google官方的Android Developers Training文檔,譯者技術(shù)一般,由于喜愛安卓而產(chǎn)生了翻譯的念頭,純屬個人興趣愛好。
原文鏈接: http://developer.android.com/training/basics/data-storage/databases.html
在數(shù)據(jù)庫中保存數(shù)據(jù),對于重復(fù)性的或者結(jié)構(gòu)化的數(shù)據(jù)來說是很理想的,比如:聯(lián)系人信息。這節(jié)課我們假定你對SQL數(shù)據(jù)庫有一個大致的了解,并且?guī)椭阍贏ndroid上開始使用SQLite數(shù)據(jù)庫。你在Android的數(shù)據(jù)庫上需要使用的APIs在 android.database.sqlite 包中。
?
一). 定義一個架構(gòu)(Schema)和契約(Contract)
SQL數(shù)據(jù)庫的主要核心之一是:一個數(shù)據(jù)庫是如何組織的正式聲明。架構(gòu)所對應(yīng)的就是你用來創(chuàng)建數(shù)據(jù)庫的SQL語句聲明。你會發(fā)現(xiàn)創(chuàng)建一個輔助類(
companion class
),即所謂的合同類(contract
?class
),它通過一個系統(tǒng)的和自文檔化的方式來顯示地指定你的架構(gòu)布局。
一個合同類是一個容器,它包含了那些定義了URI,表和列的名稱的常量。合同類允許你在同一個包內(nèi)所有類之間使用相同的名字。這樣可以使你想要改變列名時只需在一個地方修改,就能影響到所有使用到它的代碼。
一個良好的組織合同類的方式是:在類的根位置處,放置那些你的數(shù)據(jù)庫的全局定義。然后為每一個表創(chuàng)建一個內(nèi)部類,來枚舉它的列。
Note:
通過實現(xiàn)基列( BaseColumns )接口,你的內(nèi)部類可以繼承一個主鍵字段,叫做:“ _ID ”。一些Android類,比如光標適配器( cursor adaptors )會期望它能夠擁有這個。雖然這個不是必須的,但是它能夠幫助你的數(shù)據(jù)庫和Android更協(xié)同地工作。
例如,下例為一個簡單的表定義了表名和列名:
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助手創(chuàng)建一個數(shù)據(jù)庫
一旦你定義了你的數(shù)據(jù)庫,你應(yīng)該實現(xiàn)一些方法來創(chuàng)建和維護你的數(shù)據(jù)庫及表。下例是一些標準的創(chuàng)建和刪除數(shù)據(jù)庫的聲明:
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;
就好像在設(shè)備內(nèi)存( internal storage )中所存儲的文件一樣,Android會將你的數(shù)據(jù)庫存儲在一個和應(yīng)用有關(guān)聯(lián)的私有磁盤空間中。你的數(shù)據(jù)將是安全的,因為默認情況下這些數(shù)據(jù)不會被其他應(yīng)用訪問到。
一個有用的APIs集合在
SQLiteOpenHelper
類中。當(dāng)你使用這個類來獲取你的數(shù)據(jù)庫的引用時,系統(tǒng)會僅在需要時(不在應(yīng)用啟動時)執(zhí)行一些可能需要消耗較長時間的操作,如:創(chuàng)建,更新數(shù)據(jù)庫。所有你需要的是調(diào)用
getWritableDatabase()
或者
getReadableDatabase()
。
Note:
因為它們可能會消耗比較長的時間,所以請確保你在一個后臺線程調(diào)用 getWritableDatabase() 或者 getReadableDatabase() ,比如: AsyncTask 或者 IntentService 。
要使用
SQLiteOpenHelper
,創(chuàng)建一個子類,覆寫
onCreate()
,
onUpgrade()
和
onOpen()
回調(diào)函數(shù)。你可能還需要實現(xiàn)
onDowngrade()
,但這不是必須的。
例如,下面是一個
SQLiteOpenHelper
的實現(xiàn),它使用了之前所列舉的一些命令:
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); } }
要訪問你的數(shù)據(jù)庫,實例化你的 SQLiteOpenHelper 子類:
FeedReaderDbHelper mDbHelper =
new
FeedReaderDbHelper(getContext());
?
三). 將信息添加到數(shù)據(jù)庫中
可以將一個 ContentValues 對象傳入 insert() 方法,來將數(shù)據(jù)添加到數(shù)據(jù)庫中:
// 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);
insert() 方法的第一個參數(shù)就是表名。第二個參數(shù)提供了一個列名,當(dāng) ContentValues 為空時,框架會插入一個NULL。(如果你將這個參數(shù)設(shè)置為“ null ”,那么當(dāng)輸入時空時,框架不會添加一個新列)
?
四). 從數(shù)據(jù)庫中讀取數(shù)據(jù)
為了從數(shù)據(jù)庫中讀取數(shù)據(jù),使用 query() 方法,傳遞給它你的選擇標準和期望查找的列。這個方法結(jié)合了 insert() 和 update() ,除了定義了你希望獲取數(shù)據(jù) (不是你希望插入的數(shù)據(jù)) 的列清單。查詢的結(jié)果將會以一個 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 );
為了查詢 cursor中的一行,使用 Cursor 中的一個移動方法,這個方法你必須在開始讀數(shù)據(jù)時一直調(diào)用。一般地,你應(yīng)該從調(diào)用 moveToFirst() 開始,這樣將讀取位置放置到結(jié)果的第一個記錄。對于每一行,你可以你可以讀取某一列的值,通過調(diào)用 Cursor 的一個get方法,比如: getString() 或者 getLong() 。對于每個get方法,你必須傳入你期望的列的索引號,你可以通過調(diào)用 getColumnIndex() 或者 getColumnIndexOrThrow() 來得到索引號。
cursor.moveToFirst(); long itemId = cursor.getLong( cursor.getColumnIndexOrThrow(FeedEntry._ID) );
?
五). 從數(shù)據(jù)庫中刪除數(shù)據(jù)
為了從數(shù)據(jù)庫中刪除一行數(shù)據(jù),你需要提供一個選擇標準來指定一行。數(shù)據(jù)庫的API提供了一個創(chuàng)建選擇標準的機制來防止SQL注入攻擊。這個機制將選擇語句分為了選擇命令段(selection clause)和選擇參數(shù)。命令段指定了要尋找的列,并且允許你可以結(jié)合一些列測試。參數(shù)是要測試的值,它是和命令段相對應(yīng)的。因為這個結(jié)果和通常的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);
?
六). 更新一個數(shù)據(jù)庫
當(dāng)你需要修改一個數(shù)據(jù)庫值的子集,可以使用 update() 方法。
更新表結(jié)合了 insert() 的內(nèi)容語法和 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 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);
【Android Developers Training】 26. 在SQL數(shù)據(jù)庫中保存數(shù)據(jù)
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
