目录[-]
目前android开发刚学习了一个多月,最近开始研究三方开源框架的用法。了解android开发的人应该都会知道,android的数据库开发主要用到sqlite(如果这点你不清楚,那这篇文章就直接pass吧)。greenDAO应该算是当前最火的数据库开源框架了吧,它是一个移动开发的ORM(object / relational mapping)框架,至于ORM是什么,可以百度之,本人理解也不是很深,大概意思就是为懒人设计的能够将对象和关系以映射的方式表达出来。greenDAO就是如此:
greenDAO will do the word for you: it maps Java objects to datebase tables(often called ORM). 这样开发人员就可以吧精力集中在软件开发上,减轻了"wrting sql and parsing query results" 等等这些"quite tedious tasks".
总之,一句话,greenDAO就是实现Java对象和SQLite Datebase的一个媒介人,简化了SQLite的操作。
注:官方网站http://greendao-orm.com/
要使用肯定要先下载他的软件包了,官网上有它的连接,对于marven和gradle环境直接到serarch.maven.org上下载jar包就好了。
下载的jar导入到工程里面就可以了,通常都是/libs目录下。
(1)generator --- greenDAO-generator.jar
网上使用说明很少,搜到的都是对于我这个新人来说看的云里雾里,于是本人就干脆直接看官方文档,这篇文章也是边看边写的,可能会有些混乱。
在github上下载的实例源码以及上面图中我们都会发现,会有一个generator。比如包里面会有greendao和greendao-generator,示例工程会有DaoExample和DaoExampleGenerator两个工程。之前一直不明白,看了官方文档终于理解了。
“In order to use greenDao in your project, you need to create a second project, the "generator project". 这个额外的工程是一个普通的java工程,而非android工程。它的任务是生成你希望的java对象所对应的DAO接口对象。
(2)核心的class -- greenDAO.jar
DAO的core library(greenDAO.jar)中有以下几个核心类,也是后面比用到的,先来大概了解下他们的结构吧,不然直接看他们的使用会云里雾里。
DaoMaster:一看名字就知道它是Dao中的最大的官了。它保存了sqlitedatebase对象以及操作DAO classes(注意:不是对象)。其提供了一些创建和删除table的静态方法,其内部类OpenHelper和DevOpenHelper实现了SQLiteOpenHelper并创建数据库的框架。
DaoSession:会话层。操作具体的DAO对象(注意:是对象),比如各种getter方法。
XXXDao:实际生成的某某DAO类,通常对应具体的java类,比如NoteDao等。其有更多的权限和方法来操作数据库元素。
XXXEntity:持久的实体对象。通常代表了一个数据库row的标准java properties。
如下一个实例初始化代码:
1
2
3
4
5
|
helper =
new
DaoMaster.DevOpenHelper(
this
,
"notes-db"
,
null
);
db = helper.getWritableDatabase();
daoMaster =
new
DaoMaster(db);
daoSession = daoMaster.newSession();
noteDao = daoSession.getNoteDao();
|
即:先创建了一个SQLiteOpenHelper并创建连接到一个具体数据库;再根据具体的datebase创建一个master对象用于;最后通过master创建一个数据库的会话操作。
(1)创建Java工程
(2)导入greenDao-generator.jar和freemarker.jar两个包。
freemarker是一个用java写的模板引擎,它能够基于模板来生成文本输出。应该就是用来自动生成DAO文件的。eclipse下面就是在 properties --> Java build path --> libraries下面导入jar包。
(3)创建schema
这个不知道怎么业内怎么翻译的,暂时先翻译成”框架“吧。即搭建自己应用的数据库框架,包括table。
创建schema时需要制定数据库的版本号、默认的Java package等参数。
1
|
Schema schema =
new
Schema(
1
,
"de.greenrobot.daoexample"
);
|
默认Java package用来存放生成的entity、DAO文件、test代码。但也可以重新制定test文件的单独路径以及DAO文件的路径,代码:
1
2
|
schema.setDefaultJavaPackageTest(
"de.greenrobot.daoexample.test"
);
schema.setDefaultJavaPackageDao(
"de.greenrobot.daoexample.dao"
);
|
另外,还有两个flag,用来标示entity是否是activie以及是否使用keep sections。代码:
1
2
|
schema2.enableKeepSectionsByDefault();
schema2.enableActiveEntitiesByDefault();
|
Keep sections:因为entity class在每次generator执行时均会覆盖原来的程序,为了能够添加用户自定义代码到entity中,需要设置该参数。只需要把自己的代码添加到下面的KEEP[]块中间就可以了。
1
2
3
4
5
6
7
8
|
// KEEP INCLUDES - put your custom includes here
// KEEP INCLUDES END
...
// KEEP FIELDS - put your custom fields here
// KEEP FIELDS END
...
// KEEP METHODS - put your custom methods here
// KEEP METHODS END
|
(4)创建entity
schema中可以添加entity,简单的理解应该是entity对应一个具体的java class,entity可以添加property。Entity通常也是对应一个table。除了添加property之外,entity也可以添加to-one和to-many关系,即添加一对一的关系和一对多的关系。
下面就直接贴一个官方示例的代码吧:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
static
void
main(String[] args)
throws
Exception {
Schema schema =
new
Schema(
3
,
"de.greenrobot.daoexample"
);
addNote(schema);
new
DaoGenerator().generateAll(schema,
"../DaoExample/src-gen"
);
}
private
static
void
addNote(Schema schema) {
Entity note = schema.addEntity(
"Note"
);
note.addIdProperty();
note.addStringProperty(
"text"
).notNull();
note.addStringProperty(
"comment"
);
note.addDateProperty(
"date"
);
}
|
系统会自动以传入的参数作为表的名字,但也可以自定义表名称:note.setTableName("NoteTable");
(5)生成DAO文件
生成DAO文件就是使用main函数的最后一句话,其中后面的参数就是希望自动生成的代码对应的项目路径。
new DaoGenerator().generateAll(schema, "../DaoExample/src-gen");
注:设置的路径必须存在,否则会抛出异常。该路径是另外的工程下面的目录,当然也可以自己不这么做,只要这个目录存在就行。在schema创建的时候指定的package接口也是相对于该路径的。
执行generator工程,会发现生成了DaoMaster、DaoSession、NoteDao、Note共4个类文件,Note是一个纯Java类,内部各种getter和setter接口均自动创建实现了。NoteDao文件则是创建Note这个表的各个接口,包括sql语句等。很神奇很方便哈!!
完成了generator工作之后后面的东西就简单了:
(1)创建一个android工程
(2)插入一个Note对象:
1
2
3
4
5
6
7
8
|
DevOpenHelper helper =
new
DaoMaster.DevOpenHelper(
this
,
"notes-db"
,
null
);
db = helper.getWritableDatabase();
daoMaster =
new
DaoMaster(db);
daoSession = daoMaster.newSession();
noteDao = daoSession.getNoteDao();
Note note =
new
Note(
null
, noteText, comment,
new
Date());
noteDao.insert(note);
noteDao.deleteByKey(id);
|
1
|
真的是so easy!!
|
目录[-]
上一篇中我们讲述了如何创建generator工程以及如何在android工程中使用。那么在这篇中就重点讲述下使用篇,主要针对数据库查询操作。
1)你可以使用原生的SQl(raw sql)语句;
2)也可以使用推荐的方法:使用greenDAO提供的QueryBuilder的API。
3)查询还支持结果延迟加载(lazy-loading),主要为操作较大查询结果是节约内存并提高性能。
(1)QueryBuilder
使用过sql语句查询的人都会有一种感触(主要针对不是专职开发数据库并对sql不是很熟练的人),写起来复杂、不能再第一时间发现问题(只能在运行过程中验证sql的正确性)、查找bug麻烦等等。QueryBuilder的出现就是为了解决sql使用的问题,提高开发效率。
看一个略微复杂的例子,查询first name是Joe,并在1970年10月以及之后的所有人:
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();
不用我来解释了吧。
(2)Lazylist
greenDAO支持返回唯一查询结果(如果没有返回null) ---- 调用Query或QueryBuilder的unique()方法;
也支持返回list ---- 调用list()方法。
当不希望返回null作为结果时,则调用uniqueOrThrow()方法,当结果null时将直接抛出异常。
返回多个结果解释:
list(): 所有entity均会被加载到内存中。结果仅是一个简单的ArrayList。使用最简单。
listLazy(): 查询结果会根据需要加载到内存中。列表中的元素仅在accessed for the first time,它才会被加载并缓存。该方法必须主动关闭(Must be closed)。
listLazyUncached(): 虚拟的结果列表,任何对元素的方法实际都会到数据库中去加载数据。Must be closed
listIterator(): 根据需要迭代结果(lazily)。数据不缓存。Must be closed
在greenDAO实现中,后3中其实都使用的LazyList类。为了实现根据需要加载,其内部实现上是保存了数据库cursor的引用。这也是为何这3中方式must be closed,其就是为了释放内部cursor和迭代器(通常是在try-finally块中完成close)。
(3)Query
解释:Query类代表了一个可以被重复执行的查询。在QueryBuilder内部其实也是会定义一个Query并执行完成查询。
这将带来极大地方便,因为任何人都不希望在每次查询的时候总是写一遍query代码。同时Query还可以根据需要改变参数。如下实例代码:
使用Query对象查询名为Joe并出生在1970年的人:
Query query = userDao.queryBuilder().where(
Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970))
.build();
List joesOf1970 = query.list();
然后想再查询出生在1977年之后并叫Marias的人:
query.setParameter(0, "Maria");
query.setParameter(1, 1977);
List mariasOf1977 = query.list();
注意:参数的编号是创建query所设置的顺序。
(4)多线程查询
有时我们希望在多线程中执行查询。从greenDAO 1.3版本开始,实例化Query对象均会绑定到其自己的线程,这样我们就可以安全的设置Query参数,因为其他线程无法影响到。如果其他线程使用别的线程的query(比如设置参数、执行查询)将会抛出异常。因此,我们也不需要做同步工作,而且不要加锁,因为加入相关的事务用到了该Query对象将导致死锁。
为完全地避免可能产生的死锁,1.3版本引入了forCurrentThread()方法。该方法将返回本线程内的Query实例,每次调用该方法时,参数均会被重置为最初创建时的一样。
(5)原生sql查询
推荐的方法执行原生sql语句是通过QueryBuilder和WhereCondition.StringCondition。如下代码:
Query query = userDao.queryBuilder().where(
new StringCondition("_ID IN " + "(SELECT USER_ID FROM USER_MESSAGE WHERE READ_FLAG = 0)").build();
假如QueryBuilder没有提供你需要的特性,你也可以使用queryRaw() 和 queryRawCreate() 方法。具体就不再详细介绍了。
(6)删除操作(delete query)
删除操作会删除所有满足条件的实体。为实现批量删除(bulk delete),调用QueryBuilder的 buildDelete方法并执行DeleteQuery。该部分API还不稳定,可能会改变就不介绍了。
(7)问题定位(日志)
你可能会遇到查询结果并不是预期的那样,这时候你就可以设置两个静态flag参数来打日志定位问题:
QueryBuilder. LOG_SQL = true ;QueryBuilder. LOG_VALUES = true ;
这将会将产生的sql命令以及传递的参数以日志方式输出,由此方便程序员定位问题。