Android数据库框架:greenDAO vs LiteOrm
LeoMeldrum
7年前
<p>本文主要对比基于 Android SQLiteDatabase 引擎实现的数据库框架:</p> <p>greenDAO,官网链接 <a href="/misc/goto?guid=4959749215443415805" rel="nofollow,noindex">http://greenrobot.org/greendao</a> 。</p> <p>LiteOrm,官网链接 <a href="/misc/goto?guid=4959749215530579126" rel="nofollow,noindex">http://litesuits.com</a> 。</p> <p>写本文机缘起于微信群里有人谈到Android数据库框架,随之一个有赞的朋友 @段扬扬 给了一份自己的测试数据,大概是这样:</p> <p><img src="https://simg.open-open.com/show/b9014f659db75b6fc6296b76635cc417.jpg"></p> <p>Android 数据库框架性能测试</p> <p>由这个图可以看到 Realm( <a href="/misc/goto?guid=4959749215624367045" rel="nofollow,noindex">https://realm.io/cn</a> ) 基本上是性能最好的,它确实是一个牛逼的项目,它不是基于 SQLite 而是基于一个自己的持久化引擎,具有 DB 文件跨平台共享的优点,也存在一些不大不小的问题, <a href="/misc/goto?guid=4958973331819391051" rel="nofollow,noindex">点击这里查看</a> (若链接失效请google关键词“为什么我不再使用Realm”),综合而言,还是可以一战,有兴趣可以自己尝试下。</p> <p>里面没有 LiteOrm(和 Ormlite 不是一回事)的测试数据,大概是知名度小,我就随后要了一份他的测试代码和原始数据(特别感谢这位同学),不过这份数据不全了,而且需要同一部测试机,所以暂没法做框架间全量对比了。</p> <p>所以,本文主要针对 greenDAO 和 LiteOrm,因为据说 greenDAO 是基于Android SQLite的最快、性能最强悍的数据库框架,因为他不涉及反射,靠的是代码辅助生成。</p> <p>那么,我们从几个简单常见的角度和案例出发看看两者的表现如何,将会涉及到:</p> <ol> <li>性能表现情况</li> <li>初步使用情况</li> <li>应对需求变化</li> <li>待续...(精力有限,等有机缘在弄噢)</li> </ol> <h3>一. LiteOrm 和 greenDAO 的性能表现</h3> <p>下面是一组直观的测试数据,分为循环操作和批量操作两种场景:</p> <p><img src="https://simg.open-open.com/show/67925c6da799a1a323ea4fbbd209ebd3.png"></p> <p>greenDAO vs LiteOrm 循环测试</p> <p><img src="https://simg.open-open.com/show/07e16a111a9f398c314f3a6120b44b0f.png"></p> <p>greenDAO vs LiteOrm 批量测试</p> <p>测试相关过程:</p> <ol> <li> <p>运行 Test Demo,点击 LiteOrm 测试按钮,通过日志观察执行完毕。</p> </li> <li> <p>命令行卸载 Test Demo,重新运行,点击 GreenDAO 测试按钮,通过日志观察执行完毕。</p> </li> <li> <p>每次点击按钮,所有操作会连续测试 10 次,取 10 次消耗时间的均值,安静等待结果就好了。</p> </li> </ol> <p>测试相关信息:</p> <ol> <li> <p>测试机为 Nexus5,取 10 次消耗时间的均值。</p> </li> <li> <p>为了更直观清晰的观察数据,将循环操作和批量操作分开统计,否则因为两者数据差异过大,柱状图无法看清小数据的数值。</p> </li> <li> <p>循环单个操作比较耗时,每次操作 1000 条数据。</p> </li> <li> <p>批量操作因为整体是事务的,效率非常高,每次操作 100000 条数据。</p> </li> </ol> <p>测试相关结论:</p> <ol> <li> <p>[循环插入]、[循环更新] 以及 [批量更新] 时,LiteOrm性能略强于greenDAO。</p> </li> <li> <p>[批量插入]、[查询操作] 时,LiteOrm性能略逊于greenDAO。</p> </li> <li> <p>除了 [批量查询] 以外,其他性能优劣势差距不明显,[批量查询]耗时差异主要来源于 LiteOrm 采用反射创建实例并赋值属性,而 greenDAO 使用 new 操作直接创建对象并直接赋值属性。</p> </li> </ol> <h3>二. LiteOrm 和 greenDAO 的用法对比</h3> <p>我们以Note对象为例,展示操作过程,Note类如下:</p> <pre> <code class="language-java">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... }</code></pre> <p>实例化它:</p> <pre> <code class="language-java">Note note = new Note(null, "title", "comment", new Date());</code></pre> <p>1. greenDAO 增改查删</p> <p>1.1 New Module:即新建一个子项目模块,选择 Java Libray,它是一个java 项目,用来生成 greenDAO 所需要的辅助代码。</p> <p>1.2 写DAO生成器:即子模块里写一个 DAOGenerator 类生成 DAO、Master、Session 等对象:</p> <pre> <code class="language-java">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"); } }</code></pre> <p>1.3 开始工作:实例化 DAO 对象后执行各种操作:</p> <pre> <code class="language-java">// 实例化 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);</code></pre> <p>2. LiteOrm 增改查删</p> <p>2.1 开始工作:实例化 LiteOrm 对象后执行插入操作:</p> <pre> <code class="language-java">// 实例化 LiteOrm LiteOrm liteOrm = LiteOrm.newSingleInstance(this, "liteorm-notes"); // 执行插入 liteOrm.insert(note); // 执行更新 liteOrm.update(note); // 执行查询 liteOrm.queryById(1, Note.class); // 执行删除 liteOrm.delete(note);</code></pre> <p>2.2 不要沉迷,没有第二步,操作已经完了。。。</p> <p>简单解释下:上面例子默认类名为表名,字段名为列名,id(或者_id)属性为主键,但若要自定义 表名、列名 和 主键,需要给Model加注解:</p> <pre> <code class="language-java">// 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" }</code></pre> <h3>二. LiteOrm 和 greenDAO 面对需求或模型更改</h3> <p>举个简单例子,Note对象增加了一系列新字段,假设新增一个[title] 的属性:</p> <pre> <code class="language-java">public class Note { private Long id; private String title; // 新增这个 title 字段 private String text; private String comment; private java.util.Date date; // 其他省略 }</code></pre> <p>greenDAO 面对需求or对象模型更改</p> <p>第1步自定义 SQLiteOpenHelper</p> <p>greenDAO 常见得做法是在自定义你使用的 SQLiteOpenHelper,比如下面方案。</p> <p>方案a : 删旧表,建新表。</p> <pre> <code class="language-java">/** 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); } }</code></pre> <p>方案b :为旧表添加新列。</p> <pre> <code class="language-java">/** 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"); } }</code></pre> <p>如果不做上面操作,那么升级后,是会发生下面这个异常,因为之前建的表不存在[title]这个列呀!</p> <pre> <code class="language-java">android.database.sqlite.SQLiteException: no such table: CustomNote (code 1): , while compiling: INSERT INTO "CustomNote" ("_id","TEXT","COMMENT","TITLE","DATE") VALUES (?,?,?,?,?)</code></pre> <p>第2步修改 DAOGenerator 重新生成代码</p> <p>因为 greenDAO 操作的模型是由其代码生成工具产生的,需要在 DAOGenerator 里添加一个字段,让其重新生成一次模型。</p> <pre> <code class="language-java">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");</code></pre> <p>运行,greenDAO 通过 Schema 设置了数据库版本,为我们生成了系列新的Note、Note DAO、Master、Session等类。</p> <p>至此,然后基本完成 [添加一个属性字段] 的升级改造。</p> <p>LiteOrm 面对需求or对象模型更改</p> <p>好害怕,会不会更麻烦。。。but。。。</p> <p>事实是 1 步也不需要走,什么都不用改,因为模型里面我们已经新增了一个字段:</p> <pre> <code class="language-java">public class Note { private Long id; private String title; // 新增这个 title 字段 // ... 其他省略</code></pre> <p>这已经足够了,这受益于 LiteOrm 的自动探测技术,它会智能的判断某个对象是不是发生了改变,从而同步到数据库,这一切,开发者是无感知的。</p> <p>限于时间和个人精力问题,这篇分析并不全面,如果有误还请不吝指正。不论哪款 ORM 或 数据库框架,都各有利弊,至于该选用哪一款,可自行斟酌,开发者最好自己亲身体验下,毕竟绝知此事需躬行,只听或者看别人的言论和结果,无异于直接吃别人嚼过的东西,没有味道不重要,变了味会影响个人判断。</p> <p>测试代码已经上传Github:</p> <p><a href="/misc/goto?guid=4959749215744123530" rel="nofollow,noindex">https://github.com/litesuits/for-test/tree/master/DataBaseTest</a></p> <p>最后,附一份<LiteOrm 和 系统原生SQLiteDatabase API 测试数据></p> <p><img src="https://simg.open-open.com/show/7a0ad49e6ae238275d9f359f4446528f.png"></p> <p>lite-vs-system.png</p> <p> </p> <p>来自:http://www.jianshu.com/p/330bbd3b0e68</p> <p> </p>