本文主要對(duì)比基于 Android SQLiteDatabase 引擎實(shí)現(xiàn)的數(shù)據(jù)庫框架:
greenDAO,官網(wǎng)鏈接 http://greenrobot.org/greendao 挫剑。
LiteOrm裸删,官網(wǎng)鏈接 http://litesuits.com 。
寫本文機(jī)緣起于微信群里有人談到Android數(shù)據(jù)庫框架,隨之一個(gè)有贊的朋友 @段揚(yáng)揚(yáng) 給了一份自己的測試數(shù)據(jù)贴铜,大概是這樣:
由這個(gè)圖可以看到 Realm(https://realm.io/cn) 基本上是性能最好的,它確實(shí)是一個(gè)牛逼的項(xiàng)目瀑晒,它不是基于 SQLite 而是基于一個(gè)自己的持久化引擎绍坝,具有 DB 文件跨平臺(tái)共享的優(yōu)點(diǎn),也存在一些不大不小的問題苔悦,點(diǎn)擊這里查看(若鏈接失效請(qǐng)google關(guān)鍵詞“為什么我不再使用Realm”)轩褐,綜合而言,還是可以一戰(zhàn)玖详,有興趣可以自己嘗試下把介。
里面沒有 LiteOrm(和 Ormlite 不是一回事)的測試數(shù)據(jù)勤讽,大概是知名度小,我就隨后要了一份他的測試代碼和原始數(shù)據(jù)(特別感謝這位同學(xué))拗踢,不過這份數(shù)據(jù)不全了地技,而且需要同一部測試機(jī),所以暫沒法做框架間全量對(duì)比了秒拔。
所以莫矗,本文主要針對(duì) greenDAO 和 LiteOrm,因?yàn)閾?jù)說 greenDAO 是基于Android SQLite的最快砂缩、性能最強(qiáng)悍的數(shù)據(jù)庫框架作谚,因?yàn)樗簧婕胺瓷洌康氖谴a輔助生成庵芭。
那么妹懒,我們從幾個(gè)簡單常見的角度和案例出發(fā)看看兩者的表現(xiàn)如何,將會(huì)涉及到:
- 性能表現(xiàn)情況
- 初步使用情況
- 應(yīng)對(duì)需求變化
- 待續(xù)...(精力有限双吆,等有機(jī)緣在弄噢)
一. LiteOrm 和 greenDAO 的性能表現(xiàn)
下面是一組直觀的測試數(shù)據(jù)眨唬,分為循環(huán)操作和批量操作兩種場景:
測試相關(guān)過程:
運(yùn)行 Test Demo,點(diǎn)擊 LiteOrm 測試按鈕好乐,通過日志觀察執(zhí)行完畢匾竿。
命令行卸載 Test Demo,重新運(yùn)行蔚万,點(diǎn)擊 GreenDAO 測試按鈕岭妖,通過日志觀察執(zhí)行完畢。
每次點(diǎn)擊按鈕反璃,所有操作會(huì)連續(xù)測試 10 次昵慌,取 10 次消耗時(shí)間的均值,安靜等待結(jié)果就好了淮蜈。
測試相關(guān)信息:
測試機(jī)為 Nexus5斋攀,取 10 次消耗時(shí)間的均值。
為了更直觀清晰的觀察數(shù)據(jù)梧田,將循環(huán)操作和批量操作分開統(tǒng)計(jì)淳蔼,否則因?yàn)閮烧邤?shù)據(jù)差異過大,柱狀圖無法看清小數(shù)據(jù)的數(shù)值柿扣。
循環(huán)單個(gè)操作比較耗時(shí)肖方,每次操作 1000 條數(shù)據(jù)。
批量操作因?yàn)檎w是事務(wù)的未状,效率非常高,每次操作 100000 條數(shù)據(jù)析桥。
測試相關(guān)結(jié)論:
[循環(huán)插入]司草、[循環(huán)更新] 以及 [批量更新] 時(shí)艰垂,LiteOrm性能略強(qiáng)于greenDAO。
[批量插入]埋虹、[查詢操作] 時(shí)猜憎,LiteOrm性能略遜于greenDAO。
除了 [批量查詢] 以外搔课,其他性能優(yōu)劣勢差距不明顯胰柑,[批量查詢]耗時(shí)差異主要來源于 LiteOrm 采用反射創(chuàng)建實(shí)例并賦值屬性,而 greenDAO 使用 new 操作直接創(chuàng)建對(duì)象并直接賦值屬性爬泥。
二. LiteOrm 和 greenDAO 的用法對(duì)比
我們以Note對(duì)象為例柬讨,展示操作過程,Note類如下:
public class Note {
private Long id;
private String text;
private String comment;
private java.util.Date date;
public Note() {}
public Note(Long id, String text, String comment, java.util.Date date) {
this.id = id;
this.text = text;
this.comment = comment;
this.date = date;
}
// getter and setter...
}
實(shí)例化它:
Note note = new Note(null, "title", "comment", new Date());
1. greenDAO 增改查刪
1.1 New Module:即新建一個(gè)子項(xiàng)目模塊袍啡,選擇 Java Libray踩官,它是一個(gè)java 項(xiàng)目,用來生成 greenDAO 所需要的輔助代碼境输。
1.2 寫DAO生成器:即子模塊里寫一個(gè) DAOGenerator 類生成 DAO蔗牡、Master、Session 等對(duì)象:
public class NoteDaoGenerator {
public static void main(String[] args) throws Exception {
Schema schema = new Schema(1, "lite.dbtest.greendao");
addNote(schema);
new DaoGenerator().generateAll(schema, "./app/src/main/java");
}
/** 指定 表名 和 列名嗅剖,以及主鍵 */
private static void addNote(Schema schema) {
Entity note = schema.addEntity("Note"); // 默認(rèn)表名為類名
note.setTableName("CustomNote"); // 自定義表名
note.addIdProperty().primaryKey().autoincrement(); //設(shè)置自增的主鍵
note.addStringProperty("text").notNull(); // 非空字段
note.addStringProperty("comment");
note.addDateProperty("date");
}
}
1.3 開始工作:實(shí)例化 DAO 對(duì)象后執(zhí)行各種操作:
// 實(shí)例化 DAO
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "greendao-notes", null);
SQLiteDatabase db = helper.getWritableDatabase();
DaoMaster daoMaster = new DaoMaster(db);
DaoSession daoSession = daoMaster.newSession();
NoteDao noteDao = daoSession.getNoteDao();
// 執(zhí)行插入
noteDao.insert(note);
// 執(zhí)行更新
noteDao.update(note);
// 執(zhí)行查詢
noteDao.queryBuilder().where(NoteDao.Properties.Id.eq(1)).list();
// 執(zhí)行刪除
noteDao.delete(note);
2. LiteOrm 增改查刪
2.1 開始工作:實(shí)例化 LiteOrm 對(duì)象后執(zhí)行插入操作:
// 實(shí)例化 LiteOrm
LiteOrm liteOrm = LiteOrm.newSingleInstance(this, "liteorm-notes");
// 執(zhí)行插入
liteOrm.insert(note);
// 執(zhí)行更新
liteOrm.update(note);
// 執(zhí)行查詢
liteOrm.queryById(1, Note.class);
// 執(zhí)行刪除
liteOrm.delete(note);
2.2 不要沉迷辩越,沒有第二步,操作已經(jīng)完了信粮。区匣。。
簡單解釋下:上面例子默認(rèn)類名為表名蒋院,字段名為列名亏钩,id(或者_(dá)id)屬性為主鍵,但若要自定義 表名欺旧、列名 和 主鍵姑丑,需要給Model加注解:
// table name is "lite-note"
@Table("lite-note")
public class Note {
@Column("_id")
@PrimaryKey(AssignType.AUTO_INCREMENT)
private Long id; // column name is "_id"
@NotNull
@Column("_text")
private String text;// column name is "_text"
private String comment;// column name is "comment"
private java.util.Date date;// column name is "date"
}
二. LiteOrm 和 greenDAO 面對(duì)需求或模型更改
舉個(gè)簡單例子,Note對(duì)象增加了一系列新字段辞友,假設(shè)新增一個(gè)[title] 的屬性:
public class Note {
private Long id;
private String title; // 新增這個(gè) title 字段
private String text;
private String comment;
private java.util.Date date;
// 其他省略
}
greenDAO 面對(duì)需求or對(duì)象模型更改
第1步 自定義 SQLiteOpenHelper
greenDAO 常見得做法是在自定義你使用的 SQLiteOpenHelper栅哀,比如下面方案。
方案a : 刪舊表称龙,建新表留拾。
/** WARNING: Drops all table on Upgrade! Use only during development. */
public static class DevOpenHelper extends OpenHelper {
public DevOpenHelper(Context context, String name, CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 方案1,刪舊表鲫尊,建新表痴柔。
dropAllTables(db, true);
onCreate(db);
}
}
方案b :為舊表添加新列。
/** WARNING: Drops all table on Upgrade! Use only during development. */
public static class DevOpenHelper extends OpenHelper {
public DevOpenHelper(Context context, String name, CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 方案2疫向,為舊表添加新列咳蔚。
db.execSQL("ALTER TABLE NOTE ADD COLUMN title");
}
}
如果不做上面操作豪嚎,那么升級(jí)后,是會(huì)發(fā)生下面這個(gè)異常谈火,因?yàn)橹敖ǖ谋聿淮嬖赱title]這個(gè)列呀侈询!
android.database.sqlite.SQLiteException: no such table: CustomNote (code 1): , while compiling: INSERT INTO "CustomNote" ("_id","TEXT","COMMENT","TITLE","DATE") VALUES (?,?,?,?,?)
第2步 修改 DAOGenerator 重新生成代碼
因?yàn)?greenDAO 操作的模型是由其代碼生成工具產(chǎn)生的,需要在 DAOGenerator 里添加一個(gè)字段糯耍,讓其重新生成一次模型扔字。
Schema schema = new Schema(2, "lite.dbtest.greendao"); // 升級(jí)數(shù)據(jù)庫版本
Entity note = schema.addEntity("Note");
note.setTableName("CustomNote");
note.addIdProperty().primaryKey().autoincrement();
note.addStringProperty("title"); // 這里新增一個(gè)字段
note.addStringProperty("text").notNull();
note.addStringProperty("comment");
note.addDateProperty("date");
new DaoGenerator().generateAll(schema, "./app/src/main/java");
運(yùn)行,greenDAO 通過 Schema 設(shè)置了數(shù)據(jù)庫版本温技,為我們生成了系列新的Note革为、Note DAO、Master荒揣、Session等類篷角。
至此,然后基本完成 [添加一個(gè)屬性字段] 的升級(jí)改造系任。
LiteOrm 面對(duì)需求or對(duì)象模型更改
好害怕恳蹲,會(huì)不會(huì)更麻煩。俩滥。嘉蕾。but。霜旧。错忱。
事實(shí)是 1 步也不需要走,什么都不用改挂据,因?yàn)槟P屠锩嫖覀円呀?jīng)新增了一個(gè)字段:
public class Note {
private Long id;
private String title; // 新增這個(gè) title 字段
// ... 其他省略
這已經(jīng)足夠了以清,這受益于 LiteOrm 的自動(dòng)探測技術(shù),它會(huì)智能的判斷某個(gè)對(duì)象是不是發(fā)生了改變崎逃,從而同步到數(shù)據(jù)庫掷倔,這一切,開發(fā)者是無感知的个绍。
限于時(shí)間和個(gè)人精力問題勒葱,這篇分析并不全面,如果有誤還請(qǐng)不吝指正巴柿。不論哪款 ORM 或 數(shù)據(jù)庫框架凛虽,都各有利弊,至于該選用哪一款广恢,可自行斟酌凯旋,開發(fā)者最好自己親身體驗(yàn)下,畢竟絕知此事需躬行,只聽或者看別人的言論和結(jié)果瓦阐,無異于直接吃別人嚼過的東西蜗侈,沒有味道不重要篷牌,變了味會(huì)影響個(gè)人判斷睡蟋。
測試代碼已經(jīng)上傳Github:
https://github.com/litesuits/for-test/tree/master/DataBaseTest
最后,附一份<LiteOrm 和 系統(tǒng)原生SQLiteDatabase API 測試數(shù)據(jù)>