greenDAO系列5--查询

greenDAO系列5--查询

查询就是返回符合一定条件的实体。可使用原始的SQL语句执行查询;或是更好的方式,使用greenDAO中QueryBuilder的API。查询支持懒加载,当操作较大的结果集时,便可节约内存、提高性能。


QueryBuilder

QueryBuilder类可构建自定义查询,而无需接触SQL语句。编写SQL语句不是每个人都喜欢并且它容易导致在运行时才发现的错误。而QueryBuilder简单易用,将你从编写SQL语句中拯救出来。同时它不易在查询代码中出现bug,因为在编译时会进行语法检测。编译时的检查包括对属性的引用,这样greenDAO就可以在底层通过代码生成的方式组成SQL语句。

举例:查询所有名为“Joe”的用户,且按姓排序。

List joes = userDao.queryBuilder().where(Properties.FirstName.eq("Joe")).orderAsc(Properties.LastName).list();

嵌套条件的例子:获取名为“Joe”出生在1970年10月份或1970年之后的用户。假如用户的生日分成年月日三个字段,这样便可换一种方式来表示上面的查询:名字是“Joe” AND (生日年份大于1970 OR(生日年份是1970 AND 月份大于等于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();


Query和LazyList

Query类代表着一个查询,可被执行多次。但使用QueryBuilder的方法之一(如list())获取一个结果时,QueryBuilder内部就是使用Query类。如果要多次执行相同的请求,可调用QueryBulder的build()方法创建一个查询而非执行它。

greenDAO同时支持获取单个结果(0或者1个结果)和结果列表。若期待获取一个结果可以调用Query(或QueryBulder)的unique(),这将返回一个结果或匹配不到返回null。若不允许返回nul,可调用uniqueOrThrow(),它将保证返回一个非null实体(否则会抛出一个DaoException)。

若期待返回多个结果,可以调用下面的list...方法:

list() 所有实体载入内存,以ArrayList形式返回,使用最简单。 listLazy() 实体按需加载到内存。当列表中的其中一个元素第一次被访问,它会被加载并缓存备将来使用。使用完必须关闭。 listLazyUncached() 一个“虚拟”的实体列表:任何访问列表中的元素都会从数据库中读取。使用完必须关闭。 listIterator 可迭代访问结果集,按需加载数据,数据不缓存。使用完必须关闭。

方法listLazy,listLazyUncached和 listIterator需使用greenDAO的LazyList类。LazyList持有一个数据库游标,可按需加载数据。这也是为什么必须确保关闭懒加载列表和迭代器(通常使用try/finally包裹)。listLazy()的懒加载列表和listIterator()懒加载迭代器,在所有元素被访为或遍历后自动关闭游标。但是,还是必须调用close()方法,防止list的执行过早结束。


多次执行查询

通过QueryBuilder创建一个Query,Query对象可以在一次查询结束后重新使用。相比创建新的Query对象,这样更加高效。如果查询参数没有改变,可再次调用list/unique方法。如果参数改变,则必须调用setParameter方法修改相应的参数。参数的索引地址从0开始。而索引为参数添加到QueryBuilder的顺序。

下面的例子使用一个查询对象,获取以“Joe”为名,出生于1970年的用户:

Query query = userDao.queryBuilder().where(Properties.FirstName.eq("Joe"), Properties.YearOfBirth.eq(1970)).build();List joesOf1970 = query.list();

使用该查询对象,可搜索“Marias”生于1977年:

query.setParameter(0, "Maria");query.setParameter(1, 1977);List mariasOf1977 = query.list();


多线程执行查询

多线程中使用查询,必须调用Query的forCurrentThread()获取当前线程的查询实例。从greenDAO1.3开始,Query对象实例绑定到创建它的线程。这样便可安全的设置Query对象的参数,不受其他线程影响。如果其他线程试图设置参数或执行查询,将抛出异常,这样便无需使用同步语句。实际上我们应该避免加锁,因为在并发处理使用了相同的查询对象,容易导致死锁。为完全避免潜在的死锁,greenDAO1.3引入方法forCurentThread(),返回一个本地线程的Query对象,可以被当前线程安全使用。每次forCurrentThread()方法被调用,所有参数将被设置成该查询被其builder创建时的参数。


原始查询

有两种方法执行原始SQL语句,获取实体结果集。最好的方式是使用QueryBuilder和WhereCondition.StringCondition。可传入任何SQL片段到WHERE字句。如下:

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

当QueryBuilder的功能未能符合需求时,可使用queryRaw或queryRawCreate方法。你可传入一个原始语句,它会被添加到SELECT和实体字段后。用这种方式可以编写任何WHERE和ORDER BY字句去查询需要的实体集。实体表可以使用别名“T”来引用。

下面的例子使用联合查询检索所在组为“admin”的所有用。

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

注意:可用生成的常量表示表名和字段名。这是推荐的做法,避免编写错误。在一个实体的DAO中,TABLENAME表示数据库的表名,其内部类Properties则有所有属性的常量。


删除实体

//TODO


检测查询

如果查询结果没有返回你期待的,有两个静态标识可开启QueryBuilder的SQL和参数的日志输出:

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

这样会输出SQL命令和调用相关build方法时传入的参数。

推荐阅读