GreenDao源码学习

网上GreenDao相关的资料不是特别多,除了官方文档几乎没有特别好的资料。自己整理了一份,以备不时之需。

从源码上来分,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个实现,分别来构造属性条件和字符串条件



阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yuyuanhuang/article/details/42032977
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭