Android ORM 框架之 greenDAO

Android ORM 框架之 greenDAO

我相信,在平时的开发过程中,大家一定会或多或少地接触到 SQLite。然而在使用它时,我们往往需要做许多额外的工作,像编写 SQL 语句与解析查询结果等。所以,适用于 Android 的ORM 框架也就孕育而生,现在市面上主流的框架有 OrmLite、SugarORM、Active Android、Realm 与 GreenDAO。

greenDAO是一种Android数据库ORM(object/relational mapping)框架,与OrmLite、ActiveOrm、LitePal等数据库相比,单位时间内可以插入、更新和查询更多的数据,而且提供了大量的灵活通用接口。

 

Android ORM 框架之 greenDAO 使用心得

 

 

 

简单的讲,greenDAO 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案。(greenDAO is a light & fast ORM solution that maps objects to SQLite databases.) 
而关于 ORM (Object Relation Mapping – 对象关系映射)的概念,可参见 Wikipedia

GREENDAO 设计的主要目标

  • 一个精简的库

  • 性能最大化

  • 内存开销最小化

  • 易于使用的 APIs

  • 对 Android 进行高度优化

GREENDAO 设计的主要特点

  • greenDAO 性能远远高于同类的 ORMLite,具体测试结果可见官网

  • greenDAO 支持 protocol buffer(protobuf) 协议数据的直接存储,如果你通过 protobuf 协议与服务器交互,将不需要任何的映射。

  • 与 ORMLite 等使用注解方式的 ORM 框架不同,greenDAO 使用「Code generation」的方式,这也是其性能能大幅提升的原因。

给提供一篇关于greenDAO的文章,里面详细的介绍Android studio搭建greenDao:http://www.open-open.com/lib/view/open1438065400878.html

 

 

 

 

 

如何开始

 

greenDAO需要提前生成Entity、DAO等文件,因此需要先建立一个java工程用于生成上述文件。具体可以参照 GitHub源码

1、 在Android Studio中选择File -> New -> New Module -> Java Library建立greenDAO Generate工程

2、 在新建的Java工程中新建一个Java类,该Java类用于生成项目所需的Entity、DAO等文件,以下是该类的模板代码:


public static void main(String[] args) throws Exception { Schema schema = new Schema(1000, "de.greenrobot.daoexample"); addNote(schema); new DaoGenerator().generateAll(schema, "./DaoExample/src/main/java"); } private static void addNote(Schema schema) { Entity note = schema.addEntity("Note"); note.addIdProperty().primaryKey().autoincrement(); note.addStringProperty("text").notNull(); note.addStringProperty("comment"); note.addDateProperty("date"); }

在main方法中,


Schema schema = new Schema(3, "de.greenrobot.daoexample");

该方法第一个参数用来更新数据库版本号,第二个参数为要生成的DAO类所在包路径。

然后进行建表和设置要生成DAO文件的目标工程的项目路径。


addNote(schema); new DaoGenerator().generateAll(schema, "./DaoExample/src/main/java");

最后生成的文件会在目录./DaoExample/src/main/java/de/greenrobot/daoexample下看到。

创建一个实体类Entity就是对应一张表,默认表名就是类名,也可以自定义表名


Entity note = schema.addEntity("Note"); // 默认表名为类名 note.setTableName("CustomNote"); // 自定义表名

greenDAO会自动根据实体类属性创建表字段,并赋予默认值。例如在数据库方面的表名和列名都来源于实体类名和属性名。默认的数据库名称是大写使用下划线分隔单词,而不是在Java中使用的驼峰式大小写风格。例如,一个名为“CREATIONDATE”属性将成为一个数据库列“CREATION_DATE”。

可以设置一个自增长ID列为主键,也可以设置其他各种类型的属性:


note.addIdProperty().primaryKey().autoincrement(); // 自增长ID为主键 note.addStringProperty("text").notNull(); // text列不能为空

3、 最后还需要在Java工程下的build.gradle文件中引入greendao-generator


compile 'de.greenrobot:greendao-generator:2.1.0'

4、 执行Java工程,就可以生成项目所需的各种Entity、DAO等文件

5、 Android工程还需要引入greendao


compile 'de.greenrobot:greendao:2.1.0'

数据库常用操作

在正式开始进行增删改查操作前还需要简单的初始化,代码如下:


DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "notes-db", null); db = helper.getWritableDatabase(); daoMaster = new DaoMaster(db); daoSession = daoMaster.newSession(); noteDao = daoSession.getNoteDao();

其中notes-db为数据库名称,DevOpenHelper文件继承SQLiteOpenHelper,数据库的创建和升级就是在其中完成,GreenDAO已经默认实现了,代码如下:


public static abstract class OpenHelper extends SQLiteOpenHelper { public OpenHelper(Context context, String name, CursorFactory factory) { super(context, name, factory, SCHEMA_VERSION); } @Override public void onCreate(SQLiteDatabase db) { Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION); createAllTables(db, false); } } /** 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) { Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables"); dropAllTables(db, true); onCreate(db); } }

可以看到在onCreate方法中调用了createAllTable方法,顾名思义就是创建所有的数据表,而在onUpgrade方法中先是删除所有的数据表,然后再调用onCreate方法,也可以在onUpgrade方法中实现自定义的数据库升级操作。

根据DevOpenHelper可以得到SQLiteDatabase对象,SQLiteDatabase是Android原生的数据库操作类,该类提供了大量操作数据库的方法,如果想在项目中调用Android原生的sql语句就可以用该类实现。

此外greenDAO还提供了两个类,一个是DaoMaster,一个是DaoSession,关于这两个类与各DAO文件的关系见下图:

Android ORM 框架之 greenDAO

greenDAO类关系图

DaoMaster包含了DevOpenHelper,上述createAllTables(db, false)和dropAllTables(db, true)也是DaoMaster提供的,DaoMaster可以新建DaoSession,DaoSession管理所有的DAO文件,并提供相应的getter方法。

XyzDao文件提供了大量的数据库操作方法,也是最常用的一个类,下面分别介绍数据库增删改查操作。

插入数据


Note note = new Note(null, "title", "comment", new Date()); noteDao.insert(note);

greenDAO不仅提供了插入单条数据的方法,还提供了批量插入的方法,代码如下:


noteDao.insertInTx((Note[])noteList.toArray(new Note[noteList.size()]));

如果数据库中已有要插入的数据,那么上面的插入方法就会失败,可以调用insertOrReplace和insertOrReplaceInTx方法。跟踪源码可以发现,insert最终执行的sql语句是”INSERT INTO …”,而insertOrReplace执行的sql语句是”INSERT OR REPLACE INTO …”。

删除数据


noteDao.delete(note); noteDao.deleteByKey(note.getId()); noteDao.deleteByKeyInTx(deleteList); noteDao.deleteAll();

delete方法需要传入Entity对象,deleteByKey方法需要传入主键,此处主键就是Note中的id属性,也即Java Generate工程中的


note.addIdProperty().primaryKey().autoincrement();

deleteByKeyInTx批量删除,参数为List,其包含需要删除的Entity的id,deleteAll表示删除所有数据。

修改数据

更新单条数据:


note.setText("update_title"); note.setComment("update_comment"); noteDao.update(note);

批量更新数据:


noteDao.updateInTx(noteList);

查询数据


List<Note> list1 = noteDao.queryRaw("where _id = ?", new String[]{
   "20"}); List<Note> list2 = noteDao.queryBuilder() .where(NoteDao.Properties.Id.ge(10)) .limit(10000) .offset(0) .orderAsc(NoteDao.Properties.Date) .list(); QueryBuilder<Note> qb = noteDao.queryBuilder(); qb.where(qb.and(NoteDao.Properties.Id.between(10, 15), NoteDao.Properties.Comment.eq("comment"))).list();

queryRaw基本上就是对Android原生的查询方法的简单封装,跟踪queryRaw查看其具体的实现,代码如下:


public List<T> queryRaw(String where, String... selectionArg) { Cursor cursor = db.rawQuery(statements.getSelectAll() + where, selectionArg); return loadAllAndCloseCursor(cursor); }

可以看到queryRaw就是将在原生的queryRaw的查询语句前加了”SELECT * FROM Note “,因此只需要再传入具体的查询条件即可。

queryBuilder方法采用build链式结构可以灵活地添加各种查询相关的约束,where包含具体的查询条件,limit表示查询数据的条目数量,offset表示查询数据的起始位置,orderAsc表示根据某一列进行排序,最后list得到查询结果。

greenDAO还提供了多重条件查询。db.and表示查询条件取”与”,db.or表示查询条件取”或”。

关联查询

关联查询属于greenDAO比较高级的用法,目前greenDAO支持一对一、一对多,不支持多对多。

一对一

在greenDAO generator中建模时,必须使一个属性作为外键,使用这个属性,你可以用Entity.addToOne方法增加to-one关系。 addToOne方法的参数是另一个实体,和本实体的外键属性。


/** * Adds a to-one relationship to the given target entity using the given given foreign key property (which belongs * to this entity). */ public ToOne addToOne(Entity target, Property fkProperty) { if (protobuf) { throw new IllegalStateException("Protobuf entities do not support realtions, currently"); } Property[] fkProperties = {fkProperty}; ToOne toOne = new ToOne(schema, this, target, fkProperties, true); toOneRelations.add(toOne); return toOne; } /** Convenience for {
    @link #addToOne(Entity, Property)} with a subsequent call to {
    @link ToOne#setName(String)}. */ public ToOne addToOne(Entity target, Property fkProperty, String name) { ToOne toOne = addToOne(target, fkProperty); toOne.setName(name); return toOne; }

例如:user有一个photo属性,user和photo都是普通实体


Entity customer = schema.addEntity("Customer"); customer.addIdProperty(); customer.addStringProperty("name").notNull(); Entity photo = schema.addEntity("Photo"); Property photoIdProperty = customer.addLongProperty("photoId").getProperty(); customer.addToOne(photo, photoIdProperty, "photo");

这样就是customer有一个photo属性,并且可以直接操作Photo对象,customer类具有Photo属性的getPhoto/setPhoto方法。to-one关系中的getter方法在第一次加载目标实体的时候是懒汉式加载,之后的访问将返回先前已解析的对象。

注意外键属性(“photoId”)和实体对象的属性(“Photo”)绑在一起。如果你改变了photoId,下一次调用getPhoto()的时候就会用更新之后的id重新解析Photo实体。同样,如果设置了一个新的Photo实体,photoId属性也会被更新。

一对多

在greenDAO中建立to-many模型的方法和数据库中的操作类似,首先需要在目标实体中增加一个属性,用于关联To-many关系中的资源实体,然后使用这个属性,添加到资源实体的To-many关系。

例如:客户/订单的例子,客户可以有多个订单,所以我们用To-Many关系模型,在数据库中,在订单表中创建customerID列,来创建1:N关系。这样的话,就可以使用客户的id查询客户的所有的订单。


Entity customer = schema.addEntity("Customer"); customer.addIdProperty(); customer.addStringProperty("name").notNull(); Entity order = schema.addEntity("Order"); order.setTableName("ORDERS"); // "ORDER" is a reserved keyword order.addIdProperty(); Property orderDate = order.addDateProperty("date").getProperty(); Property customerId = order.addLongProperty("customerId").notNull().getProperty(); order.addToOne(customer, customerId); ToMany customerToOrders = customer.addToMany(order, customerId); customerToOrders.setName("orders"); customerToOrders.orderAsc(orderDate);

这样,我们可以在客户类中简单的调用生成的getOrders()方法获取订单,同样,也可以在订单类中调用生成的getCustomer方法获取客户信息。

源码分析

greenDAO有别于其他通过反射机制实现的ORM框架,greenDAO需要一个Java工程事先生成需要的文件,而在每一个DAO文件中都已经自动组装好创建和删除数据表的sql语句。代码如下:


/** Creates the underlying database table. */ public static void createTable(SQLiteDatabase db, boolean ifNotExists) { String constraint = ifNotExists? "IF NOT EXISTS ": ""; db.execSQL("CREATE TABLE " + constraint + "\\"NOTE\\" (" + // "\\"_id\\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id "\\"TEXT\\" TEXT NOT NULL ," + // 1: text "\\"COMMENT\\" TEXT," + // 2: comment "\\"DATE\\" INTEGER);"); // 3: date } /** Drops the underlying database table. */ public static void dropTable(SQLiteDatabase db, boolean ifExists) { String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\\"NOTE\\""; db.execSQL(sql); }

根据之前介绍的用法知道,数据库需要提前做一些初始化


DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "notes-db", null); db = helper.getWritableDatabase(); daoMaster = new DaoMaster(db); daoSession = daoMaster.newSession(); noteDao = daoSession.getNoteDao();

此处的DevOpenHelper类的构造方法就包含了创建所有数据表的操作,代码在上面数据库常用用法处已经展示过,此处不再重复。

greenDAO的增删改查方法有一些是在Android原生的操作方法上进行了封装,比如说上面的查询方法queryRaw就是对原生的queryRaw进行简单的封装,对于上面链式查询的最终执行也是调用了Android原生的查询操作。


public List<T> list() { checkThread(); Cursor cursor = dao.getDatabase().rawQuery(sql, parameters); return daoAccess.loadAllAndCloseCursor(cursor); }

同时还有一些方法是基于SQLiteStatement实现的,SQLiteStatement相比原生的execSQL方法还要快一些,并且最终执行时也开启了事务,性能又提升了很多。下面是插入数据的最终实现方法:


private long executeInsert(T entity, SQLiteStatement stmt) { long rowId; if (db.isDbLockedByCurrentThread()) { synchronized (stmt) { bindValues(stmt, entity); rowId = stmt.executeInsert(); } } else { // Do TX to acquire a connection before locking the stmt to avoid deadlocks db.beginTransaction(); try { synchronized (stmt) { bindValues(stmt, entity); rowId = stmt.executeInsert(); } db.setTransactionSuccessful(); } finally { db.endTransaction(); } } updateKeyAfterInsertAndAttach(entity, rowId, true); return rowId; }

可以看到先执行bindValues方法,该方法是一个抽象方法,需要业务方在DAO文件中实现,跟踪至NoteDao文件查看该方法代码如下:


@Override protected void bindValues(SQLiteStatement stmt, Note entity) { stmt.clearBindings(); Long id = entity.getId(); if (id != null) { stmt.bindLong(1, id); // 1为索引值,id为入库的值 } stmt.bindString(2, entity.getText()); String comment = entity.getComment(); if (comment != null) { stmt.bindString(3, comment); } java.util.Date date = entity.getDate(); if (date != null) { stmt.bindLong(4, date.getTime()); } }

这样就将SQLiteStatement需要的数据都进行了封装,然后执行stmt.executeInsert()方法即可完成数据库的插入操作。纵观整个数据插入流程,greenDAO借助SQLiteStatement完成了数据的插入,避免了其他框架利用反射拼装sql语句而造成的执行效率低下的问题。

书到用时方恨少,纸上得来终觉浅。希望对你有所帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/115071.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • WebService 实例应用

    WebService 实例应用两个工程分别部署在两台电脑上:webservice_client客户端  webservice_server:服务器端先说服务器导入jar包改写xml文件:cxfcom.rainspnsor.webservice.CXFNonSpringServiceImpl0cxf/services/*然后创建类:1util中:

    2022年7月21日
    12
  • Yii 多語言

    Yii 多語言

    2021年8月20日
    46
  • 数独解法Java实现「建议收藏」

    数独解法Java实现「建议收藏」数独问题描述标准的数独游戏是在一个9X9的棋盘上填写1–9这9个数字,规则是这样的:棋盘分成上图所示的9个区域(不同颜色做背景标出,每个区域是3X3的子棋盘),在每个子棋盘中填充1–9且不允许重复,下面简称块重复每一行不许有重复值,下面简称行重复每一列不许有重复值,下面简称列重复如上红色框出的子区域中的亮黄色格子

    2025年5月24日
    0
  • oracle优化书籍推荐

    经常听到有做应用的朋友抱怨数据库的性能问题,比如非常低的并发,令人崩溃的响应时间,长时间的锁等待,锁升级,甚至是死锁,等等。本文针对应用开发人员经常接触的性能问题,推荐几本书,请大家关注。 一、《 oracle9i/10g 编程艺术》内容简介 本书是一本关于Oracle9jaz&10g数据库体系结构的权威图书,涵盖了所有最重要的Ora

    2022年4月6日
    119
  • 360天擎卸载方法[通俗易懂]

    360天擎卸载方法[通俗易懂]具体方法如下:1、先找到360天擎软件的安装文件夹,通常是:C:\ProgramFiles(x86)\360\360Safe\EntClient\conf2、先下载我们使有记事本打开目录下的EntBase.dat文件[base]persistent_connetion=closeshow_tip=1net_env=1communication_interval=900[api_frequency]checkupdate=180get_client_tasks=180getconf=

    2022年9月25日
    0
  • SpringBoot的认识,SpringBoot与Spring关系[通俗易懂]

    SpringBoot的认识,SpringBoot与Spring关系[通俗易懂]一、概念1、SpringSpring是一个开源容器框架,可以接管web层,业务层,dao层,持久层的组件,并且可以配置各种bean,和维护bean与bean之间的关系。其核心就是控制反转(IOC),和面向切面(AOP),简单的说就是一个分层的轻量级开源框架。2、SpringMVCSpringMVC属于SpringFrameWork的后续产品,已经融合在SpringWebFlow里面。SpringMVC是一种web层mvc框架,用于替代servlet(处理|响应请求,获取表单参数,表单校验等。S

    2022年5月27日
    33

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号