Android數(shù)據(jù)庫存儲

前面說了Android文件的存放區(qū)域,這篇文章將說說數(shù)據(jù)的存放方式之一SQL數(shù)據(jù)庫,這個對于重復或結構化數(shù)據(jù)(比如聯(lián)系人信息)而言是理想之選,使用到的SQL相關API所在的包為android.database.sqlite

Define a Schema and Contract

SQL數(shù)據(jù)庫的主要原則之一是Schema:聲明了數(shù)據(jù)庫是如何組織的. 對于創(chuàng)建類似于Contract類等Companion類很有幫助.

一個Contract類是一個用于存放定義URI名字,表名,列名的常量的容器,通過這個類你可以在同一個包下的其他類中使用這些常量.Android API中的一些Contract類如ContactsContract,CalendarContract,TvContract等等.

組織Contract類的一種良好方法是將對于整個數(shù)據(jù)庫而言是全局性的定義放入該類頂層,然后為每個表表創(chuàng)建內部類。

  • 注意: 通過實現(xiàn)BaseColumns接口,你的類就會帶有有個兩個常量_ID和_COUNT,其中_ID對于一些類是有用的,雖然這不是必須的,但是這樣做對于數(shù)據(jù)庫與Android framework的和諧工作是有幫助的.

如下片段,一個Contract類中定義表明和列名:

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";
        ...
    }
}

Create a Database Using a SQL Helper

定義了數(shù)據(jù)庫的結構后,就可以來創(chuàng)建和維護數(shù)據(jù)了,下面是創(chuàng)建和刪除Table的語句:

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ù)據(jù)庫也是存放在internal storage中,如前文所講,這是安全區(qū)域.

對于Android中操作數(shù)據(jù)庫,有一個類提供了很多有用的API,這個就是SQLiteOpenHelper,里面提供了許多數(shù)據(jù)庫操作方法的回調,你可以override它們然后實現(xiàn)自己的邏輯操作. 同時該類還提供了getWritableDatabase()getReadableDatabase(),通過這兩個方法可以獲取到SQLiteDatabase實例對象,這樣就可以對數(shù)據(jù)庫進行操作.

  • 注意: 數(shù)據(jù)庫操作可能是long-running的,所以要保證對數(shù)據(jù)庫的操作放到異步來進行,比如用AsyncTask或IntentService來操做getWritableDatabase()或getReadableDatabase().

由于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);
    }
}

使用時只需實例化一個FeedReaderDbHelper的對象即可:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

Put Information into a Database

使用ContentValues類和insert()方法類插入數(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ù)是當?shù)诙€參數(shù)ContantValues的對象中無數(shù)據(jù)時在相應的列插入NULL的列名.(若第三個參數(shù)為null則不會插入空數(shù)據(jù)).

Read Information from a Database

調用query()方法可以查詢數(shù)據(jù),可以傳一些參數(shù)來篩選,返回的是一個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對象的讀取,通常先調用moveToFirst()方法,來把指針移到結果的第一個entry,也就是整行的數(shù)據(jù),可以通過Cursor的getString(),getLong()等方法來獲取具體值,這些方法需要列的索引,而列的索引可以用 getColumnIndex()或getColumnIndexOrThrow()方法來獲取,如下示例:

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

Delete Information from a Database

刪除數(shù)據(jù)需要指定某行,我們這個數(shù)據(jù)庫的API為創(chuàng)建選擇條件提供了一套機制來防止SQL注入. 這套機制把整個選擇分成了選擇語句和選擇參數(shù),其中選擇語句定義了要查找的列,同時也允許你進行組合測試. 而選擇參數(shù)則是根據(jù)綁定到選擇語句來測試的值,因為這個結果的處理與常規(guī)的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);

Update a Database

數(shù)據(jù)常規(guī)操作增刪改查中的改,使用update()方法,如下示例:

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中使用數(shù)據(jù)庫,只需要自定義一個SQLiteOpenHelper的實現(xiàn)類,最好再寫個Contract類將要列名表名等寫成全局靜態(tài)常量,然后在SQLiteOpenHelper中進行相應的操作,除了上面的一些方法,可以直接調用SQLiteDatabase的execSQL相關方法來直接執(zhí)行原生sql語句.
但是對于稍微復雜點的數(shù)據(jù),比如要存放多個類的數(shù)據(jù),那要寫的就比較多,而現(xiàn)在有一些工具和方案可以幫我們簡化數(shù)據(jù)庫的操作,SQLite封裝的工具如:

  1. OrmLite
  2. greenDAO
  3. SugarORM
  4. ActiveAndroid
  5. LitePal
  6. realm

你可以選擇你覺得好用的作為你的解決方案,還是不錯的.

Reference

  1. SQLiteDatabase
  2. A Comparison of Android ORMs
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市士八,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌桥狡,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妓局,死亡現(xiàn)場離奇詭異总放,居然都是意外死亡,警方通過查閱死者的電腦和手機好爬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門局雄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人存炮,你說我怎么就攤上這事炬搭。” “怎么了穆桂?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵宫盔,是天一觀的道長。 經(jīng)常有香客問我享完,道長灼芭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任般又,我火速辦了婚禮彼绷,結果婚禮上巍佑,老公的妹妹穿的比我還像新娘。我一直安慰自己寄悯,他們只是感情好萤衰,可當我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著猜旬,像睡著了一般脆栋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上洒擦,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天椿争,我揣著相機與錄音,去河邊找鬼秘遏。 笑死丘薛,一個胖子當著我的面吹牛,可吹牛的內容都是我干的邦危。 我是一名探鬼主播,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼舍扰,長吁一口氣:“原來是場噩夢啊……” “哼倦蚪!你這毒婦竟也來了?” 一聲冷哼從身側響起边苹,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤陵且,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后个束,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體慕购,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年茬底,在試婚紗的時候發(fā)現(xiàn)自己被綠了沪悲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡阱表,死狀恐怖殿如,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情最爬,我是刑警寧澤涉馁,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站爱致,受9級特大地震影響烤送,放射性物質發(fā)生泄漏。R本人自食惡果不足惜糠悯,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一帮坚、第九天 我趴在偏房一處隱蔽的房頂上張望妻往。 院中可真熱鬧,春花似錦叶沛、人聲如沸蒲讯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽判帮。三九已至,卻和暖如春溉箕,著一層夾襖步出監(jiān)牢的瞬間晦墙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工肴茄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留晌畅,地道東北人。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓寡痰,卻偏偏與公主長得像抗楔,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拦坠,可洞房花燭夜當晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,192評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理连躏,服務發(fā)現(xiàn),斷路器贞滨,智...
    卡卡羅2017閱讀 134,672評論 18 139
  • 任何一個應用程序入热,其實說白了就是在不停地和數(shù)據(jù)打交道,我們聊QQ晓铆、看新聞勺良、刷微博,所關心的都是里面的數(shù)據(jù)骄噪,...
    AndYMJ閱讀 1,663評論 2 5
  • 從傍晚開始坐在我斜對面尚困,從六點到十一點,一個人在酒吧玩五小時魔方的姑娘我還是第一次遇見腰池。 我心想這個女人雖然漂亮尾组,...
    此何人哉閱讀 699評論 0 6
  • 風 莫停 攜我情 向南遠行 且行且聆聽 伊人風里歌聲 一夜颯沓看流星 晨曦迷蒙應有露凝 西子湖畔有誰醉又醒 韶華何...
    那個詩人閱讀 157評論 1 1