GreenDao源码学习
2015-02-27 13:44
288 查看
网上GreenDao相关的资料不是特别多,除了官方文档几乎没有特别好的资料。自己整理了一份,以备不时之需。
从源码上来分,GreenDao大体可以分成两个项目,DaoCore和DaoGenerator。
使用GreenDao后整个项目的大概的一个关系是这样的:
DaoGenerator部分:
FreeMarker
DaoGenerator使用FreeMarker根据指定的schema生成代码。
为了能够更好的介绍DaoGenerator的代码,有必要先简单介绍一下FreeMarker的基本功能,
一个基本的FreeMarker工程一般有这三个步骤组成。
1.声明一个configration,用来处理生成代码的逻辑
指定模版文件地址的方式有三种,
分别是:基于类路径、文件系统以及Servlet Context
2.生成ftl文件,用来作为生成代码的模版
ftl模版如下:
这是一个简单的ftl模版的样子,其中${X}表示要用替换对象X进行替换。
3.通过config读取指定的ftl,生成template,进而结合模版,替换对象,输出流,生成最终的文件
这里的替换对象可以是Map的,也可以是一个普通的bean。
DaoGenerator:
下面切入正题开始看DaoGenerator的代码吧
包路径de.greenrobot.daogenerator 下面有如下几个类:
ContentProvider,
DaoGenerator,
DaoUtil,
Entity,
Index,
Property,
PropertyOrderList,
PropertyType,
Query,
QueryParam,
Schema,
ToMany,
ToOne,
使用顺序:
Schema作为一次完整的活动,它指定了生成代码的包名,其内部一般会有多个Entity,一个Entity对应数据库一张表,Entity下的Property,每个Property对应数据库中一个列。DaoGenerator作为一个入口,通过他的generateAll()方法来触发代码生成。
一般使用的顺序是这样的,先new一个schema,指定好生成代码包路径,然后通过Schema.addEntity(“表名”)得到一个Entity,然后再通过Entity.addXXProperty(“列名”)将指定的property添加到Entity(XX表示列的类型,比如int,String,等等)。
关系指定好之后通过DaoGenerator.generateAll(schema,srcOutputPath),触发根据schema中的关系和参数在数据库中建表,并在指定位置生成相应的实体bean和DAOs。
顺着使用时候的思路,我们来看一下源码里有什么值得我们借鉴的地方吧:
1.在Property的构造部分,由于property参数较多,这里他采用了Builder模式[Gamma95,p97],不直接生成想要的对象,而是让使用者利用必要的参数调用构造器(或者工厂模式),得到一个builder对象,然后使用者在builder对象上调用类似setter的方法,来设置每个可选的相关参数。 最后使用者调用build方法生成不可变的对象。这个builder是他构建的类的静态成员类,下面是他的实现:
使用起来是这个样子的:
注意这里类似setter的方法(columnName,columnType,等等)返回的都是builder本身,这样就可以把调用链起来。
DaoCore部分:
我们先用一张类图来说明一下DaoCore部分里面的数据调用关系:
;
(上边的这幅图和下面的分析主要引用和参考自Github上一个叫做“android-open-project-analysis”的项目中对Greendao的DaoCore部分的分析,感谢他们。)
1.包de.greeenrobot.dao,主要的类
一、DbUtils类 vacuum函数:清除数据库的空闲空间,减少数据库大小 executeSqlScript函数:执行assert目录文件中的sql语句,可以指定是否开启事务执行 readAsset函数:读取assert文件返回byte数组 logTableDump函数:将数据库中指定表的内容输出到debug日志
二、DaoLog类 将系统Log类封装,将tag变量隐藏而已,tag值为greenDao.倒数第二个函数应该是作者写错了,如:Log.w应该是Log.epublic static int e(String msg) { return Log.w(TAG, msg); }
三、AbstractDao类 所有生成代码xxDao的基类,使用泛型,AbstractDao<T, K>其中T是数据库实体(entity)类型,K是主键类型.使用模板模式,来完成crud方法,是抽象类,定义了许多abstract方法,子类来定义它的行为.
通过DaoConfig和AbstractDaoSession来构造,提供许多crud方法,支持在批处理中执行: T load(K key):通过主键值来查找实体结果 T loadByRowId(long rowId):通过行号来查询结果 List loadAll():加载所有结果 executeInsertInTx:批量插入 ....
四、AbstractDaoMaster类 通过SQLiteDatabase和版本号构造,持有SQLiteDatabase对象,保持一个泛型的容器Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> ,做Dao和DaoConfig的缓存,另外加2个抽象方法,给子类来实现
五、AbstractDaoSession类 所有DaoSession的基类,包含一个Map<Class<?>, AbstractDao<?, ?>> entityToDao对象,缓存所有实体类和实体Dao对应关系。因为可以获取Dao,可以做一些crud操作,可以生成异步的session来完成crud操作
六、Property类 对应存入类中的一个属性,对应数据库中一列,用来生成where子句使用。在实体Dao中有引用。
2.de.greenrobot.dao.async包,完成数据库的异步操作,主要的类
一、AsyncOperation类 表示一个将在子线程完成的操作,包含操作的类型、是否成功、操作的Dao、SQLiteDatabase、操作开始和结束时间、是否和其他操作合并、操作唯一号码、操作的实体对象(数组、单个元素或迭代器)
二、AsyncOperationExecutor类 执行数据库操作的类,是runnable对象,还能回调主线程,内部有AsyncOperation类型的阻塞队列,完成数据库操作加入队列、执行操作、结果回调、合并数据库操作等工作。
三、AsyncOperationListener类 数据库异步操作完成后的回调接口
四、AsyncSession类 对外提供的类,可以初始化它,并调用它的异步方法来完成数据库操作的异步执行功能。对它设置回调listen,完成操作后,将整个AsyncOperation回调给界面
3.de.greenrobot.dao.identityscope包 这个包主要是定义一个k-v的缓存组件,定义一些操作缓存的接口,key支持long和object类型,value是entity类型,使用软引用.
一、IdentityScope类 接口类,使用泛型加接口的形式用来派生key为long和key为object的子类,也是实体对象的主键,提供根据主键来查询、删除、插入entity.
二、IdentityScopeLong类 内部有一个锁的成员变量,map使用自定义优化过的LongHashMap,支持带锁操作map和不带锁操作map
三、IdentityScopeObject类 和IdentityScopeLong类似,只是内部使用hashMap,key是object类型
4.de.greenrobot.dao.internal包 里面包含一些内部使用的工具类,
一、DaoConfig类 被AbstractDaoMaster引用。由SQLiteDatabase和AbstractDao的子类来构造,通过这两个参数,反射获取Dao类的属性、主键、主键类型、表名。通过构造方法和Cloneable接口提供创建新对象的方法。
二、FastCursor类 给AbstractDao类的子类使用,快速读取cursor数据。 它实现Cursor接口,重载读取当前游标指向数据的方法(getDouble、getFloat等),提供移动游标函数(), 如move(int offset)、moveToPosition(int position)等,来快速访问Cursor数据。
三、LongHashMap类 给IdentityScopeLong类使用,是自定义的map,针对long类型对map进行优化,例如get、put方法,通过位移运算符来很快完成插入、查找结果
四、SqlUtils类 内部使用,在给AbstractDao类的子类使用,用来帮助创建crud操作的sql语句
五、TableStatements类 greenDao内部使用,给DaoConfig引用,用来给指定的tab创建SQLiteStatement。由DaoConfig传入tab相关的信息来构造,如表名、所有列名、主键名。
5.de.greenrobot.dao.query包 这整个包的作用是使用面向对象的方式来提供可复用的查询对象,用于内部测试
一、AbstractQuery类 一些可以复用的查询基类,返回实体entity,包含泛型T,是entity的类型,包含一些查询必要的参数信息,用于实现子类的查询
二、AbstractQueryData类 辅助类,使用模板方法,来构造AbstractQuery的子类,其中的泛型T是实体类型(entity),Q是需要构造的AbstractQuery子类
三、CountQuery类 AbstractQuery的子类,提供查找数据库符合条件entity的个数
四、DeleteQuery类 AbstractQuery的子类,提供删除符合条件的entity,其中executeDeleteWithoutDetachingEntities方法不会清除identity scope中的缓存
五、Query类 AbstractQuery的子类,提供查找符合条件的entity,以多种形式返回查找结果,如List、带缓存的LazyList
六、WhereCondition类 用来在查询中构造where子句,使用Dao类中的Property对象构造查询条件
七、QueryBuilder类 构造自定义的查询对象,而不使用sql语句,查询对象内部会生成sql语句,并且在编译时进行语法检查。可以通过AbstractDao.queryBuilder或者AbstractDaoSession.queryBuilder来获取
八、WhereCondition类 内部的接口来构造查询中的where条件,使用DAO类中的Property对象来构造新的查询条件.代码结构是一个接口,加2个实现,分别来构造属性条件和字符串条件
从源码上来分,GreenDao大体可以分成两个项目,DaoCore和DaoGenerator。
使用GreenDao后整个项目的大概的一个关系是这样的:
DaoGenerator部分:
FreeMarker
DaoGenerator使用FreeMarker根据指定的schema生成代码。
为了能够更好的介绍DaoGenerator的代码,有必要先简单介绍一下FreeMarker的基本功能,
一个基本的FreeMarker工程一般有这三个步骤组成。
1.声明一个configration,用来处理生成代码的逻辑
Configuration config =newConfiguration(); config.setClassForTemplateLoading(this.getClass(),"/"); config.setObjectWrapper(newDefaultObjectWrapper());
指定模版文件地址的方式有三种,
Configuration config =newConfiguration(); config.setClassForTemplateLoading(this.getClass(),"/"); config.setObjectWrapper(newDefaultObjectWrapper());
分别是:基于类路径、文件系统以及Servlet Context
2.生成ftl文件,用来作为生成代码的模版
Template temp = config.getTemplate("test.ftl”);
ftl模版如下:
<html> <head> <title>Welcome!</title> </head> <body> <h1>Welcome ${user}!</h1> <p>Our latest product: <a href="${latestProduct.url}">${latestProduct.name}</a>! </body> </html>
这是一个简单的ftl模版的样子,其中${X}表示要用替换对象X进行替换。
3.通过config读取指定的ftl,生成template,进而结合模版,替换对象,输出流,生成最终的文件
Map root = this.getRoot(); Writer out = new OutputStreamWriter(System.out); temp.process(root, out); out.flush();
这里的替换对象可以是Map的,也可以是一个普通的bean。
DaoGenerator:
下面切入正题开始看DaoGenerator的代码吧
包路径de.greenrobot.daogenerator 下面有如下几个类:
ContentProvider,
DaoGenerator,
DaoUtil,
Entity,
Index,
Property,
PropertyOrderList,
PropertyType,
Query,
QueryParam,
Schema,
ToMany,
ToOne,
使用顺序:
Schema作为一次完整的活动,它指定了生成代码的包名,其内部一般会有多个Entity,一个Entity对应数据库一张表,Entity下的Property,每个Property对应数据库中一个列。DaoGenerator作为一个入口,通过他的generateAll()方法来触发代码生成。
一般使用的顺序是这样的,先new一个schema,指定好生成代码包路径,然后通过Schema.addEntity(“表名”)得到一个Entity,然后再通过Entity.addXXProperty(“列名”)将指定的property添加到Entity(XX表示列的类型,比如int,String,等等)。
关系指定好之后通过DaoGenerator.generateAll(schema,srcOutputPath),触发根据schema中的关系和参数在数据库中建表,并在指定位置生成相应的实体bean和DAOs。
顺着使用时候的思路,我们来看一下源码里有什么值得我们借鉴的地方吧:
1.在Property的构造部分,由于property参数较多,这里他采用了Builder模式[Gamma95,p97],不直接生成想要的对象,而是让使用者利用必要的参数调用构造器(或者工厂模式),得到一个builder对象,然后使用者在builder对象上调用类似setter的方法,来设置每个可选的相关参数。 最后使用者调用build方法生成不可变的对象。这个builder是他构建的类的静态成员类,下面是他的实现:
public class Property { public static class PropertyBuilder { private final Property property; public PropertyBuilder(Schema schema, Entity entity, PropertyType propertyType, String propertyName) { property = new Property(schema, entity, propertyType, propertyName); } public PropertyBuilder columnName(String columnName) { property.columnName = columnName; return this; } public PropertyBuilder columnType(String columnType) { property.columnType = columnType; return this; } . . . . . public Property getProperty() { return property; } } . . }
使用起来是这个样子的:
public PropertyBuilder addProperty(PropertyType propertyType, String propertyName) { if (!propertyNames.add(propertyName)) { throw new RuntimeException("Property already defined: " + propertyName); } PropertyBuilder builder = new Property.PropertyBuilder(schema, this, propertyType, propertyName); properties.add(builder.getProperty()); return builder; } public PropertyBuilder addIdProperty() { PropertyBuilder builder = addLongProperty("id"); builder.columnName("_id").primaryKey(); return builder; }
注意这里类似setter的方法(columnName,columnType,等等)返回的都是builder本身,这样就可以把调用链起来。
DaoCore部分:
我们先用一张类图来说明一下DaoCore部分里面的数据调用关系:
;
(上边的这幅图和下面的分析主要引用和参考自Github上一个叫做“android-open-project-analysis”的项目中对Greendao的DaoCore部分的分析,感谢他们。)
1.包de.greeenrobot.dao,主要的类
一、DbUtils类 vacuum函数:清除数据库的空闲空间,减少数据库大小 executeSqlScript函数:执行assert目录文件中的sql语句,可以指定是否开启事务执行 readAsset函数:读取assert文件返回byte数组 logTableDump函数:将数据库中指定表的内容输出到debug日志
二、DaoLog类 将系统Log类封装,将tag变量隐藏而已,tag值为greenDao.倒数第二个函数应该是作者写错了,如:Log.w应该是Log.epublic static int e(String msg) { return Log.w(TAG, msg); }
三、AbstractDao类 所有生成代码xxDao的基类,使用泛型,AbstractDao<T, K>其中T是数据库实体(entity)类型,K是主键类型.使用模板模式,来完成crud方法,是抽象类,定义了许多abstract方法,子类来定义它的行为.
通过DaoConfig和AbstractDaoSession来构造,提供许多crud方法,支持在批处理中执行: T load(K key):通过主键值来查找实体结果 T loadByRowId(long rowId):通过行号来查询结果 List loadAll():加载所有结果 executeInsertInTx:批量插入 ....
四、AbstractDaoMaster类 通过SQLiteDatabase和版本号构造,持有SQLiteDatabase对象,保持一个泛型的容器Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> ,做Dao和DaoConfig的缓存,另外加2个抽象方法,给子类来实现
五、AbstractDaoSession类 所有DaoSession的基类,包含一个Map<Class<?>, AbstractDao<?, ?>> entityToDao对象,缓存所有实体类和实体Dao对应关系。因为可以获取Dao,可以做一些crud操作,可以生成异步的session来完成crud操作
六、Property类 对应存入类中的一个属性,对应数据库中一列,用来生成where子句使用。在实体Dao中有引用。
2.de.greenrobot.dao.async包,完成数据库的异步操作,主要的类
一、AsyncOperation类 表示一个将在子线程完成的操作,包含操作的类型、是否成功、操作的Dao、SQLiteDatabase、操作开始和结束时间、是否和其他操作合并、操作唯一号码、操作的实体对象(数组、单个元素或迭代器)
二、AsyncOperationExecutor类 执行数据库操作的类,是runnable对象,还能回调主线程,内部有AsyncOperation类型的阻塞队列,完成数据库操作加入队列、执行操作、结果回调、合并数据库操作等工作。
三、AsyncOperationListener类 数据库异步操作完成后的回调接口
四、AsyncSession类 对外提供的类,可以初始化它,并调用它的异步方法来完成数据库操作的异步执行功能。对它设置回调listen,完成操作后,将整个AsyncOperation回调给界面
3.de.greenrobot.dao.identityscope包 这个包主要是定义一个k-v的缓存组件,定义一些操作缓存的接口,key支持long和object类型,value是entity类型,使用软引用.
一、IdentityScope类 接口类,使用泛型加接口的形式用来派生key为long和key为object的子类,也是实体对象的主键,提供根据主键来查询、删除、插入entity.
二、IdentityScopeLong类 内部有一个锁的成员变量,map使用自定义优化过的LongHashMap,支持带锁操作map和不带锁操作map
三、IdentityScopeObject类 和IdentityScopeLong类似,只是内部使用hashMap,key是object类型
4.de.greenrobot.dao.internal包 里面包含一些内部使用的工具类,
一、DaoConfig类 被AbstractDaoMaster引用。由SQLiteDatabase和AbstractDao的子类来构造,通过这两个参数,反射获取Dao类的属性、主键、主键类型、表名。通过构造方法和Cloneable接口提供创建新对象的方法。
二、FastCursor类 给AbstractDao类的子类使用,快速读取cursor数据。 它实现Cursor接口,重载读取当前游标指向数据的方法(getDouble、getFloat等),提供移动游标函数(), 如move(int offset)、moveToPosition(int position)等,来快速访问Cursor数据。
三、LongHashMap类 给IdentityScopeLong类使用,是自定义的map,针对long类型对map进行优化,例如get、put方法,通过位移运算符来很快完成插入、查找结果
四、SqlUtils类 内部使用,在给AbstractDao类的子类使用,用来帮助创建crud操作的sql语句
五、TableStatements类 greenDao内部使用,给DaoConfig引用,用来给指定的tab创建SQLiteStatement。由DaoConfig传入tab相关的信息来构造,如表名、所有列名、主键名。
5.de.greenrobot.dao.query包 这整个包的作用是使用面向对象的方式来提供可复用的查询对象,用于内部测试
一、AbstractQuery类 一些可以复用的查询基类,返回实体entity,包含泛型T,是entity的类型,包含一些查询必要的参数信息,用于实现子类的查询
二、AbstractQueryData类 辅助类,使用模板方法,来构造AbstractQuery的子类,其中的泛型T是实体类型(entity),Q是需要构造的AbstractQuery子类
三、CountQuery类 AbstractQuery的子类,提供查找数据库符合条件entity的个数
四、DeleteQuery类 AbstractQuery的子类,提供删除符合条件的entity,其中executeDeleteWithoutDetachingEntities方法不会清除identity scope中的缓存
五、Query类 AbstractQuery的子类,提供查找符合条件的entity,以多种形式返回查找结果,如List、带缓存的LazyList
六、WhereCondition类 用来在查询中构造where子句,使用Dao类中的Property对象构造查询条件
七、QueryBuilder类 构造自定义的查询对象,而不使用sql语句,查询对象内部会生成sql语句,并且在编译时进行语法检查。可以通过AbstractDao.queryBuilder或者AbstractDaoSession.queryBuilder来获取
八、WhereCondition类 内部的接口来构造查询中的where条件,使用DAO类中的Property对象来构造新的查询条件.代码结构是一个接口,加2个实现,分别来构造属性条件和字符串条件
相关文章推荐
- greenDaoMaster的学习研究
- greenDaoMaster的学习研究
- GreenDao 学习笔记 1
- GreenDao 3.0 学习笔记(一)
- Android 操作数据库的框架——greenDAO的学习
- Android数据库greenDAO框架用法和源码分析
- 强大的数据库ORM框架-GreenDao项目源码剖析篇
- Android ORM数据库之GreenDao使用教程及源码分析
- 拆轮子系列之理解GreenDao框架源码
- greenDaoMaster的学习研究
- GreenDAO 学习笔记-小白教程(三)数据库升级及注意事项
- Android ORM 框架之 greenDAO 学习
- GreenDao Id自增之后的学习
- GreenDAO 学习笔记-小白教程(二)使用单例类来管理DaoSession
- mybatis源码学习--spring+mybatis注解方式为什么mybatis的dao接口不需要实现类
- mybatis源码学习--spring+mybatis注解方式为什么mybatis的dao接口不需要实现类
- greenDaoMaster的学习研究
- Android ORM之GreenDao学习
- GreenDao源码详解第一篇(Dao、Mater等类生成原理)
- GreenDao源码分析及使用GreenDao实现静态数据缓存