Android数据库ORM框架:GreenDAO使用简介

Android最常用的数据库是SQLite,通常使用SQLite进行CRUD操作需要记住一些常用的SQL语句,这不利于提高开发效率,而且很容易出错。ORM(Object-Relational Mapping)框架的引入使得对数据库的操作也能利用OOP(Object-oriented programming)思想实现。ORM通过将数据库中的一张张表映射成Java的对象,并提供统一的API提供CRUD操作,使得操作数据库变得简单,开发者可以将精力放在处理重要的逻辑,而不是记住SQL语句上。

对象关系映射(ORM)

有几个常用的ORM开源框架,它们的特点比较如下:

Android ORM框架比较

其中由GreenRobot公司开源的GreenDAO尤为出色,该开源库体积小,而且由于完全没有使用反射,所以速度很快,本文将介绍GreenDAO的使用。

特点

1. 鲁棒性强, 2011年开始使用
2. 很小, <100k
3. 速度快
4. 安全易用的API
5. 强大的join语句
6. 灵活的属性类型

使用步骤

1. 建立一个java工程,给每张表定义一个对应的Entity,运行工程,生成一系列的输出文件
2. 将这些输出文件拷贝到Android工程里,并在gradle里做相应的配置
3. 可以在Android里通过OOP的方式访问数据库了

首先打开Eclipse,建立一个java工程,引入两个库文件freemarker.jar和greendao-generator-1.3.1.jar,可以开始定义Entity了。

在greenDAO里,一个Entity对应SQLite的一张表,可以给Entity设置Property,Relation和Index,分别代表数据表的属性,关系和索引(一对一,一对多)。

Entity

这里的Schema类包含了数据库层面的信息,如数据库版本和名称:

Schema schema = new Schema(1, "me.chunyu.support");

利用Schema,可以给每张表建立一个对应的Entity。Entity包含了Property,Relation和Index。

Property

对Property的设置如下:

public static void addGroupChatRecord(Schema schema) {        
    Entity groupChatRecord = schema.addEntity("GroupChatRecord");     
    groupChatRecord.addStringProperty("messageMD5ID").primaryKey(); // conversation id + message id md5值        
    groupChatRecord.addBooleanProperty("hasRead"); // 是否已读        
    groupChatRecord.addDateProperty("messageTime");        
}

这个Entity命名为GroupChatRecord,记录的是好友群的数据,通过addStringProperty,addBooleanProperty,addDateProperty分别添加类型为String,Boolean和Date的属性,对应数据表里相应的字段,并通过primaryKey()指定messageMD5ID为主键。

Relation

addToOne定义一对一的关系:

Property avatarIdProperty = user.addLongProperty("avatarId").getProperty();
user.addToOne(picture, avatarIdProperty);

上面的代码中,user和picture都是Entity,通过addToOne指定user的头像属性对应picture表里的一条记录。

addToMany定义一对多的关系:

Property customerId = order.addLongProperty("customerId").notNull().getProperty();
ToMany customerToOrders = customer.addToMany(order, customerId);

上面的代码中,通过addToMany指定一个用户(customer)可以对应多个订单(order)。这样,通过customer.getOrders()就可以获取一个用户的所有订单:

List orders = customer.getOrders();
Index

通过给数据表建立索引可以提高检索效率,Index代表数据表的索引,通过addProperty添加索引字段,可以建立独立索引或联合索引。Entity通过addIndex方法添加索引:

Index index = new Index();
index.addProperty(property1);
index.addProperty(property2);
entity.addIndex(index);

定义完Entity后,调用DAOGenerator类的generateAll方法生成DAO文件,参数分布是代表数据库的schema和文件输出路径:

// 生成DAO文件到指定路径
new DaoGenerator().generateAll(schema, "../");

生成的公共文件,包括DaoMaster.java/DaoSession.java,另外每个Entity对应生成两个文件EntityName.java/EntityNameDao.java,文件内容如下:
1. EntityName.java: Entity的各个属性,及get/set方法
2. EntityNameDao.java:Entity的CRUD方法
3. DaoSession.java:提供获取各个EntityNameDao的方法
4. DaoMaster.java:生成DaoSession;进行数据库生成,升级

使用:
首先初始化DaoMaster,然后通过DaoMaster获得DaoSession,样例代码:

public class GreenDaoUtils {    
    private static DaoSession daoSession;    
    private static byte[] _lock = {};    

    /**获取Dao Session**/    
    public static DaoSession getDaoSession(Context context, String dbName) {        
        if (daoSession != null) {            
            return daoSession;        
        }        

        synchronized (_lock) {            
            if (daoSession != null) {                
                return daoSession;            
            }           
            SQLiteDatabase db;            
            DaoMaster daoMaster;            
            
            DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(context, dbName, null);
            db = helper.getWritableDatabase();        
            daoMaster = new DaoMaster(db);        
            daoSession = daoMaster.newSession();      
  
            return daoSession;    
        }
    }
}

代码中getWritableDatabase用于新建或返回数据库,如果第一次执行该代码,新建数据库,以后执行返回已建好的数据库。同时,如果数据库版本升级了,会执行helper的onUpgrade函数,执行升级数据库的操作。

样例代码中helper类型是DaoMaster.DevOpenHelper,该类的onUpgrade函数会删除所有表,再新建所有表,只适用于开发阶段。开发者需要实现自己的OpenHelper,重写onUpgrade函数,指明如何进行数据库的升级。下面是DaoMaster.DevOpenHelper的实现:

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);    
    }
}
使用DAO API进行CRUD操作:

Create:

entityNameDao.insert(newEntity)

Read:

List joes = userDao.queryBuilder()

                    .where(Properties.FirstName.eq("Joe"))

                    .orderAsc(Properties.LastName)

                    .list();

QueryBuilder qb = userDao.queryBuilder();

qb.where(Properties.FirstName.eq("Joe"),

    qb.or(Properties.YearOfBirth.gt(1970),

        qb.and(Properties.YearOfBirth.eq(1970), 
            Properties.MonthOfBirth.ge(10))));

List youngJoes = qb.list();

Update:

entityNameDao.update(newEntity);

Delete:

entityNameDao.deleteByKey(id)
// OR
entityNameDao.delete(note);
执行raw sql语句

除了使用greenDao提供的接口进行增删改查操作,greenDao还提供了执行Sql语句的功能,示例如下:

Query query = userDao.queryBuilder()
    .where(new StringCondition(“_ID IN ” +“(SELECT USER_ID FROM USER_MESSAGE WHERE READ_FLAG = 0)”)
    .build();


Query query = userDao
    .queryRawCreate(  ", GROUP G WHERE G.NAME=? AND T.GROUP_ID=G._ID", "admin");

同时,如果感觉没有得到期望的结果,可以通过下面代码打开greenDao调试功能,输出生成的sql语句进行debug:

QueryBuilder.LOG_SQL = true;
QueryBuilder.LOG_VALUES = true;

参考:
https://github.com/greenrobot/greenDAO
http://greenrobot.org/greendao/documentation/

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

推荐阅读更多精彩内容