greenDao查询(Queries)
Queries帮助你返回entities。你可以使用Raw SQL(原生查询)构造查询,然而使用greenDao的QueryBuilder API才是更加优雅的方式。Queries支持lazy-loading的查询结果。
当处理一个较大的结果集时,lazy-loading(懒加载模式)可以节省内存提高性能。
QueryBuilder
你可以使用QueryBuilder
创建自定义查询,而不用做丑陋的SQL拼接。毕竟不是每一个人都擅长写SQL,并且还容易出错。QueryBuilder使用非常方便,它将你从SQL编写中解脱出来。妈妈再也不用担心写SQL时出现bug了,因为语法检查是在编译时进行的。
greenDao简单查询例子:查询所有first name为“Joe”的人,然后按照last name排序。
1 2 3 4 5 |
List joes = userDao.queryBuilder() .where(Properties.FirstName.eq("Joe")) .orderAsc(Properties.LastName) .list(); |
greenDao Nested conditions query(嵌套查询)例子: 有一个需求是查询first name为“Joe”并且出生在1970年10月或者之后的人。 假如我们有一个用户,其中生日被分为,year、month、day三个字段。于是我们可以写出一个嵌套查询。
1 2 3 4 5 6 |
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(); |
Query and LazyList
使用QueryBuilder的方法来获取结果,例如list()
,在QueryBuilder的内部是使用的Query类。如果你想得到Query的引用来多次执行,可以使用QueryBuilder.Build()
来创建查询。
greenDao支持唯一结果(0 or 1个结果)和结果集。如果希望获得唯一结果就调用unique()
,这样会获得唯一结果或者null。如果你不希望获得null,可以调用uniqueOrThrow()
这样可以确保返回一个非空的entity。
如果你期望返回多个entity作为查询结果,可以使用如下的方法:
方法 | 描述 |
---|---|
list() | entity都会加载到内存中,返回的结果通常就是ArrayList。 |
listLazy() | Entity按需加载到内存中,在第一次访问list中的element时,它就会呗加载并且缓存。 |
listLazyUncached() | 每次访问结果集的时候都是从数据库中加载,而不使用缓存。 |
listIterator() | 通过迭代器访问结果集,并且采用的时lazy-loading,结果不会缓存。 |
close
方法关闭lazy lists和iterators(通常在try/finally块里面)。当所有的element被访问了之后,listLazy()和listIterator()自动关闭cursor。如果list提前处理完成,调用close()就是你必须完成的工作。
查询复用(Executing Queries multiple times)
如果使用QueryBuilder创建query,查询对象(Query object)可以重用。可以避免重复创建查询对象性能更高。如果查询参数没有变化,可以再调用list或者unique方法。如果查询参数有变化,需要对每一个改变的parameter调用setParameter方法。可以通过下标index访问parameter。 下面的例子是用Query Object查询出生在1970年并且first name为“Joe”的人:
1 2 3 4 5 |
Query query = userDao.queryBuilder().where( Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970)) .build(); List joesOf1970 = query.list(); |
使用上面的查询对象,我们可以查询1977年之后出生的“Marias”
1 2 3 4 |
query.setParameter(0, "Maria"); query.setParameter(1, 1977); List mariasOf1977 = query.list(); |
多线程查询(Executing queries in multiple threads)
如果使用多线程查询,必须调用query的forCurrentThread()
方法,返回当前线程的查询实例。从greenDao1.3开始,查询实例与创建查询的现场绑定在一起。在查询对象上安全地调用setParameter,而其他线程不能干预。
如果线程A想调用已经被线程B绑定的query对象,或者执行查询,将会抛出一个exception。像这样,你不需要synchronized。实际上要避免使用锁,因为如果当前的transaction使用同一个查询对象时,将导致死锁。
为了避免这种潜在的死锁,greenDao1.3推出了forCurrentThread()方法。这将返回一个thread-local的查询实例,用于当前线程的安全调用。每次forCurrentThread()被调用,parameters就会被设置称为查询对象创建的初始状态。
原生查询(Raw queries)
如果QueryBuilder没有提供你需要的特性,那么Raw Queries就是最后的解决方案。这里有两种方法执行Raw SQL。推荐使用QueryBuilder,WhereConditon,StringCondition。通过这种方式可以传入任何的SQL片段作为Where条件。下面是一个假设的例子执行一个子查询(采用join是更好的解决方案):
1 2 3 4 |
Query query = userDao.queryBuilder().where( new StringCondition("_ID IN " + "(SELECT USER_ID FROM USER_MESSAGE WHERE READ_FLAG = 0)").build(); |
另外一种方式是采用queryRaw或者queryRawCreate方法。可以传入一个raw SQL,它被追加到SELECT和columns之后。这样,就可以使用Where和Order by来选择entities。表还可以使用别名“T”: 下面的例子是查询“admin”用户组下面的所有用户:
1 2 3 |
uery query = userDao.queryRawCreate( ", GROUP G WHERE G.NAME=? AND T.GROUP_ID=G._ID", "admin"); |
注意:为了避免录入错误,使用表名和列名时采用自动生成的约束,因为编译器会检查名称。在entity的Dao里,可以看到TABLENAME管理数据库表名,所有包含约束的的内部类属性管理表的列名称。
Delete Queries
要执行批量删除,需要创建一个QueryBuilder然后调用他的buildDelete方法,然后执行DeleteQuery。这部分的API将来会有变动。
打开调试日志,追踪问题(Troubleshooting queries)
如果你的查询没有返回期望的结果,这里有两个static标记用来打开SQL和parameter日志:
1 2 3 |
QueryBuilder.LOG_SQL = true; QueryBuilder.LOG_VALUES = true; |