Android數據庫框架:greenDAO vs LiteOrm
本文主要對比基于 Android SQLiteDatabase 引擎實現的數據庫框架:
greenDAO,官網鏈接 http://greenrobot.org/greendao 。
LiteOrm,官網鏈接 http://litesuits.com 。
寫本文機緣起于微信群里有人談到Android數據庫框架,隨之一個有贊的朋友 @段揚揚 給了一份自己的測試數據,大概是這樣:
Android 數據庫框架性能測試
由這個圖可以看到 Realm( https://realm.io/cn ) 基本上是性能最好的,它確實是一個牛逼的項目,它不是基于 SQLite 而是基于一個自己的持久化引擎,具有 DB 文件跨平臺共享的優點,也存在一些不大不小的問題, 點擊這里查看 (若鏈接失效請google關鍵詞“為什么我不再使用Realm”),綜合而言,還是可以一戰,有興趣可以自己嘗試下。
里面沒有 LiteOrm(和 Ormlite 不是一回事)的測試數據,大概是知名度小,我就隨后要了一份他的測試代碼和原始數據(特別感謝這位同學),不過這份數據不全了,而且需要同一部測試機,所以暫沒法做框架間全量對比了。
所以,本文主要針對 greenDAO 和 LiteOrm,因為據說 greenDAO 是基于Android SQLite的最快、性能最強悍的數據庫框架,因為他不涉及反射,靠的是代碼輔助生成。
那么,我們從幾個簡單常見的角度和案例出發看看兩者的表現如何,將會涉及到:
- 性能表現情況
- 初步使用情況
- 應對需求變化
- 待續...(精力有限,等有機緣在弄噢)
一. LiteOrm 和 greenDAO 的性能表現
下面是一組直觀的測試數據,分為循環操作和批量操作兩種場景:
greenDAO vs LiteOrm 循環測試
greenDAO vs LiteOrm 批量測試
測試相關過程:
-
運行 Test Demo,點擊 LiteOrm 測試按鈕,通過日志觀察執行完畢。
-
命令行卸載 Test Demo,重新運行,點擊 GreenDAO 測試按鈕,通過日志觀察執行完畢。
-
每次點擊按鈕,所有操作會連續測試 10 次,取 10 次消耗時間的均值,安靜等待結果就好了。
測試相關信息:
-
測試機為 Nexus5,取 10 次消耗時間的均值。
-
為了更直觀清晰的觀察數據,將循環操作和批量操作分開統計,否則因為兩者數據差異過大,柱狀圖無法看清小數據的數值。
-
循環單個操作比較耗時,每次操作 1000 條數據。
-
批量操作因為整體是事務的,效率非常高,每次操作 100000 條數據。
測試相關結論:
-
[循環插入]、[循環更新] 以及 [批量更新] 時,LiteOrm性能略強于greenDAO。
-
[批量插入]、[查詢操作] 時,LiteOrm性能略遜于greenDAO。
-
除了 [批量查詢] 以外,其他性能優劣勢差距不明顯,[批量查詢]耗時差異主要來源于 LiteOrm 采用反射創建實例并賦值屬性,而 greenDAO 使用 new 操作直接創建對象并直接賦值屬性。
二. LiteOrm 和 greenDAO 的用法對比
我們以Note對象為例,展示操作過程,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...
}
實例化它:
Note note = new Note(null, "title", "comment", new Date());
1. greenDAO 增改查刪
1.1 New Module:即新建一個子項目模塊,選擇 Java Libray,它是一個java 項目,用來生成 greenDAO 所需要的輔助代碼。
1.2 寫DAO生成器:即子模塊里寫一個 DAOGenerator 類生成 DAO、Master、Session 等對象:
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"); // 默認表名為類名
note.setTableName("CustomNote"); // 自定義表名
note.addIdProperty().primaryKey().autoincrement(); //設置自增的主鍵
note.addStringProperty("text").notNull(); // 非空字段
note.addStringProperty("comment");
note.addDateProperty("date");
}
}
1.3 開始工作:實例化 DAO 對象后執行各種操作:
// 實例化 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();
// 執行插入
noteDao.insert(note);
// 執行更新
noteDao.update(note);
// 執行查詢
noteDao.queryBuilder().where(NoteDao.Properties.Id.eq(1)).list();
// 執行刪除
noteDao.delete(note);
2. LiteOrm 增改查刪
2.1 開始工作:實例化 LiteOrm 對象后執行插入操作:
// 實例化 LiteOrm
LiteOrm liteOrm = LiteOrm.newSingleInstance(this, "liteorm-notes");
// 執行插入
liteOrm.insert(note);
// 執行更新
liteOrm.update(note);
// 執行查詢
liteOrm.queryById(1, Note.class);
// 執行刪除
liteOrm.delete(note);
2.2 不要沉迷,沒有第二步,操作已經完了。。。
簡單解釋下:上面例子默認類名為表名,字段名為列名,id(或者_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 面對需求或模型更改
舉個簡單例子,Note對象增加了一系列新字段,假設新增一個[title] 的屬性:
public class Note {
private Long id;
private String title; // 新增這個 title 字段
private String text;
private String comment;
private java.util.Date date;
// 其他省略
}
greenDAO 面對需求or對象模型更改
第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");
}
}
如果不做上面操作,那么升級后,是會發生下面這個異常,因為之前建的表不存在[title]這個列呀!
android.database.sqlite.SQLiteException: no such table: CustomNote (code 1): , while compiling: INSERT INTO "CustomNote" ("_id","TEXT","COMMENT","TITLE","DATE") VALUES (?,?,?,?,?)
第2步修改 DAOGenerator 重新生成代碼
因為 greenDAO 操作的模型是由其代碼生成工具產生的,需要在 DAOGenerator 里添加一個字段,讓其重新生成一次模型。
Schema schema = new Schema(2, "lite.dbtest.greendao"); // 升級數據庫版本
Entity note = schema.addEntity("Note");
note.setTableName("CustomNote");
note.addIdProperty().primaryKey().autoincrement();
note.addStringProperty("title"); // 這里新增一個字段
note.addStringProperty("text").notNull();
note.addStringProperty("comment");
note.addDateProperty("date");
new DaoGenerator().generateAll(schema, "./app/src/main/java");
運行,greenDAO 通過 Schema 設置了數據庫版本,為我們生成了系列新的Note、Note DAO、Master、Session等類。
至此,然后基本完成 [添加一個屬性字段] 的升級改造。
LiteOrm 面對需求or對象模型更改
好害怕,會不會更麻煩。。。but。。。
事實是 1 步也不需要走,什么都不用改,因為模型里面我們已經新增了一個字段:
public class Note {
private Long id;
private String title; // 新增這個 title 字段
// ... 其他省略
這已經足夠了,這受益于 LiteOrm 的自動探測技術,它會智能的判斷某個對象是不是發生了改變,從而同步到數據庫,這一切,開發者是無感知的。
限于時間和個人精力問題,這篇分析并不全面,如果有誤還請不吝指正。不論哪款 ORM 或 數據庫框架,都各有利弊,至于該選用哪一款,可自行斟酌,開發者最好自己親身體驗下,畢竟絕知此事需躬行,只聽或者看別人的言論和結果,無異于直接吃別人嚼過的東西,沒有味道不重要,變了味會影響個人判斷。
測試代碼已經上傳Github:
https://github.com/litesuits/for-test/tree/master/DataBaseTest
最后,附一份<LiteOrm 和 系統原生SQLiteDatabase API 測試數據>
lite-vs-system.png
來自:http://www.jianshu.com/p/330bbd3b0e68