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

