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的最快、性能最强悍的数据库框架,因为他不涉及反射,靠的是代码辅助生成。

那么,我们从几个简单常见的角度和案例出发看看两者的表现如何,将会涉及到:

  1. 性能表现情况
  2. 初步使用情况
  3. 应对需求变化
  4. 待续...(精力有限,等有机缘在弄噢)

一. LiteOrm 和 greenDAO 的性能表现

下面是一组直观的测试数据,分为循环操作和批量操作两种场景:

greenDAO vs LiteOrm 循环测试
greenDAO vs LiteOrm 批量测试

测试相关过程:

  1. 运行 Test Demo,点击 LiteOrm 测试按钮,通过日志观察执行完毕。

  2. 命令行卸载 Test Demo,重新运行,点击 GreenDAO 测试按钮,通过日志观察执行完毕。

  3. 每次点击按钮,所有操作会连续测试 10 次,取 10 次消耗时间的均值,安静等待结果就好了。

测试相关信息:

  1. 测试机为 Nexus5,取 10 次消耗时间的均值。

  2. 为了更直观清晰的观察数据,将循环操作和批量操作分开统计,否则因为两者数据差异过大,柱状图无法看清小数据的数值。

  3. 循环单个操作比较耗时,每次操作 1000 条数据。

  4. 批量操作因为整体是事务的,效率非常高,每次操作 100000 条数据。

测试相关结论:

  1. [循环插入]、[循环更新] 以及 [批量更新] 时,LiteOrm性能略强于greenDAO。

  2. [批量插入]、[查询操作] 时,LiteOrm性能略逊于greenDAO。

  3. 除了 [批量查询] 以外,其他性能优劣势差距不明显,[批量查询]耗时差异主要来源于 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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容