通过了多篇文章的学习,咱们已经把 LitePal 中的绝大部份内容都掌握了。如今回想起来了,增删改查四种操做中的前三种咱们都已经学完了,不知道如今使用起数据库来,你有没有感受到格外的轻松和简单。git
可是呢,咱们都知道,在全部的数据库操做当中,查询操做确定是最复杂的,用法也是最多的,所以 LitePal 在查询方面提供的 API 也是比较丰富,并且 LitePal 在查询方面的 API 设计也是颇为艺术的。那么今天咱们就专门使用一篇博客来说解一下查询操做的用法,体验一下 LitePal 查询的艺术。尚未看过前面一篇文章的朋友建议先去参考 Android 数据库高手秘籍 (六)——LitePal 的修改和删除操做 。github
LitePal 的项目地址是:github.com/LitePalFram…sql
其实最传统的查询数据的方式固然是使用 SQL 语句了,Android 当中也提供了直接使用原生 SQL 语句来查询数据库表的方法,即 SQLiteDatabase 中的 rawQuery() 方法,方法定义以下:数据库
public Cursor rawQuery(String sql, String[] selectionArgs)
复制代码
其中,rawQuery() 方法接收两个参数,第一个参数接收的就是一个 SQL 字符串,第二个参数是用于替换 SQL 语句中占位符(?)的字符串数组。rawQuery() 方法返回一个 Cursor 对象,全部查询到的数据都是封闭在这个对象当中的,咱们只要一一取出就能够了。数组
固然这种用法其实并非很经常使用,由于相信大多数人都仍是不喜欢编写 SQL 语句的。因此,Android 专门提供了一种封装好的 API,使得咱们不用编写 SQL 语句也能查询出数据,即 SQLiteDatabase 中的 query() 方法。query() 提供了三个方法重载,其中参数最少的一个也有七个参数,咱们来看下方法定义:markdown
public Cursor query(String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having,
复制代码
其中第一参数是表名,表示咱们但愿从哪张表中查询数据。第二个参数用于指定去查询哪几列,若是不指定则默认查询全部列。第3、第四个参数用于去约束查询某一行或某几行的数据,不指定则默认是查询全部行的数据。第五个参数用于指定须要去 group by 的列,不指定则表示不对查询结果进行 group by 操做。第六个参数用于对 group by 以后的数据进行进一步的过滤,不指定则表示不进行过滤。第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。app
这个方法是 query() 方法最少的一个方法重载了,另外还有两个方法重载分别是八个和九个参数。虽然说这个方法在 Android 数据库表查询的时候很是经常使用,但重多的参数让咱们在理解这个方法的时候可能会很费力,另外使用起来的时候也会至关的不爽。好比说,咱们想查询 news 表中的全部数据,就应该要这样写:ide
SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor cursor = db.query("news", null, null, null, null, null, null);
复制代码
能够看到,将第一个表名参数指定成 news,而后后面的六个参数咱们都用不到,就所有指定成 null。函数
那若是是咱们想查询 news 表中全部评论数大于零的新闻该怎么写呢?代码以下所示:oop
SQLiteDatabase db = dbHelper.getWritableDatabase();
Cursor cursor = db.query("news", null, "commentcount>?", new String[]{"0"}, null, null, null);
复制代码
因为第三和第四个参数是用于指定约束条件的,因此咱们在第三个参数中指明了 commentcount>?,而后在第四个参数中经过一个 String 数组来替换占位符,这样查到的结果就是 news 表中全部评论数大于零的新闻了。那么其它的几个参数呢?仍然用不到,因此仍是只能传 null。
而后咱们能够看到,query() 方法的返回值是一个 Cursor 对象,全部查询到的数据都是封装在这个对象中的,因此咱们还须要将数据逐一从 Cursor 对象中取出,而后设置到 News 实体类当中,以下所示:
List<News> newsList = new ArrayList<News>();
if (cursor != null && cursor.moveToFirst()) {
int id = cursor.getInt(cursor.getColumnIndex("id"));
String title = cursor.getString(cursor.getColumnIndex("title"));
String content = cursor.getString(cursor.getColumnIndex("content"));
Date publishDate = new Date(cursor.getLong(cursor.getColumnIndex("publishdate")));
int commentCount = cursor.getInt(cursor.getColumnIndex("commentcount"));
news.setContent(content);
news.setPublishDate(publishDate);
news.setCommentCount(commentCount);
} while (cursor.moveToNext());
复制代码
这大概就是传统查询数据方式的用法了,整体来看,用法确实很是不友好,尤为是 query() 方法冗长的参数列表,即便咱们用不到那些参数,也必需要传入许多个 null。另外,查询到的数据还都只是封装到了一个 Cursor 对象中,咱们还须要将数据一一取出而后再 set 到实体类对象当中。麻烦吗?可能你以为不麻烦,由于你已经习惯了这种用法。可是习惯老是能够改变的,也许当你体验了 LitePal 中查询 API 给咱们带来的便利以后,就会有了新的见解了,那么下面咱们就一块儿来体验一下 LitePal 的查询艺术。
LitePal 在查询方面提供了很是丰富的 API,功能多种多样,基本上已经可以知足咱们平时全部的查询需求了。不只如此,LitePal 在查询 API 的设计方面也是很是用心,摒弃了原生 query() 方法中繁琐的参数列表,而是改用了一种更为灵巧的方式——连缀查询。除此以外,LitePal 查询的结果也再也不返回 Cursor 对象,而后再由开发者本身去逐个取出,而是直接返回封装好的对象。这些改变都使得查询数据变得更加简单,也更加合理,那么下面咱们就来完整地学习一下 LitePal 中查询数据的全部用法。
好比说如今咱们想实现一个最简单的功能,查询 news 表中 id 为 1 的这条记录,使用 LitePal 就能够这样写:
News news = DataSupport.find(News.class, 1);
复制代码
天呐!有没有以为过轻松了?仅仅一行代码,就能够把 news 表中 id 为 1 的记录查出来了,并且结果仍是自动封装到 News 对象里的,也不须要咱们手动再从 Cursor 中去解析。若是是用原生的 SQL 语句,或者 query() 方法来写,至少要 20 行左右的代码才能完成一样的功能!
那咱们先冷静一下,来分析分析这个 find() 方法。能够看到,它的参数列表也比较简单,只接收两个参数,第一个参数是一个泛型类,也就是说咱们在这里指定什么类,返回的对象就是什么类,因此这里传入 News.class,那么返回的对象也就是 News 了。第二个参数就更简单了,就是一个 id 值,若是想要查询 id 为 1 的记录就传 1,想查 id 为 2 的记录就传 2,以此类推。
原本一个还算颇为复杂的功能,经过 LitePal 以后就变得这么简单了!那么你可能已经火烧眉毛地想要学习更多 LitePal 中更多的查询用法了,别着急,咱们一个个来看。
你也许遇到过如下场景,在某些状况下,你须要取出表中的第一条数据,那么传统的作法是怎么样的呢?在 SQL 语句中指定一个 limit 值,而后获取返回结果的第一条记录。可是在 LitePal 中不用这么麻烦,好比咱们想要获取 news 表中的第一条数据,只须要这样写:
News firstNews = DataSupport.findFirst(News.class);
复制代码
OK,语义性很是强吧,让人一眼就看懂是什么意思了,只需调用 findFirst() 方法,而后传入 News 类,获得的就是 news 表中的第一条数据了。
那咱们举一翻三,若是是想要获取 News 表中的最后一条数据该怎么写呢?一样简单,以下所示:
News lastNews = DataSupport.findLast(News.class);
复制代码
由于获取表中第一条或者是最后一条数据的场景比较常见,因此 LitePal 特地提供了这两个方法来方便咱们的操做。
那么咱们看到这里,目前都只是查询单条数据的功能,若是想要查询多条数据该怎么办呢?好比说,咱们想把 news 表中 id 为 一、三、五、7 的数据都查出来,该怎么写呢?也许有的朋友会比较聪明,立马就想到能够一个个去查,就调用四次 find() 方法嘛,而后把 一、三、五、7 这四个 id 分别传进去不就能够了。没错,这样作彻底是能够的,并且效率也并不低,可是 LitePal 给咱们提供了一个更简便的方法——findAll()。这个方法的用法和 find() 方法是很是相似的,只不过它能够指定多个 id,而且返回值也再也不是一个泛型类对象,而是一个泛型类集合,以下所示:
List<News> newsList = DataSupport.findAll(News.class, 1, 3, 5, 7);
复制代码
能够看到,首先咱们是调用的 findAll() 方法,而后这个方法的第一个参数仍然是指定的泛型类,可是后面的参数就很随意了,你能够传入任意个 id 进去,findAll() 方法会把全部传入的 id 所对应的数据所有查出来,而后一块儿返回到 List 这个泛型集合当中。
虽然说这个语法设计算是至关人性化,可是在有些场景或许不太适用,由于可能要你要查询的多个 id 已经封装到一个数组里了。那么不要紧,findAll() 方法也是接收数组参数的,因此说一样的功能你也能够这样写:
long[] ids = new long[] { 1, 3, 5, 7 };
List<News> newsList = DataSupport.findAll(News.class, ids);
复制代码
看到这里,那有的朋友可能会奇怪了,说 findAll() 方法不该该是查询全部数据的意思吗?怎么老是查询几个 id 所对应数据呢?哈!这个问题问得好,由于 findAll() 方法也是能够查询全部数据的,并且查询全部数据的写法更简单,只须要这样写:
List<News> allNews = DataSupport.findAll(News.class);
复制代码
看到没有,咱们只须要把后面的参数都去掉,在不指定具体 id 的状况下,findAll() 方法查询出的就是 news 表中的全部数据了,是否是语义性很是强?
并且你们不要觉得刚才这些都只是 findAll() 的几个方法重载而已,实际上刚才咱们的这几种用法都是调用的同一个 findAll() 方法!一个方法却可以实现多种不一样的查询效果,而且语义性也很强,让人一看就能理解,这就是 LitePal 的查询艺术!
固然了,LitePal 给咱们提供的查询功能还远远不仅这些,好戏还在后头。相信你们如今也已经发现了,咱们目前的查询功能都是基于 id 来进行查询的,并不能随意地指定查询条件。那么怎样才能指定查询条件呢?让咱们回想一下传统状况应该怎么作,query() 方法中接收七个参数,其中第三和第四个参数就是用于指定查询条件的,而后其它几个参数都填 null 就能够了。可是呢,前面咱们已经痛批过了这种写法,由于冗长的参数列表太过繁琐,那么 LitePal 又是怎么解决这个问题的呢?咱们如今就来学习一下。
为了不冗长的参数列表,LitePal 采用了一种很是巧妙的解决方案,叫做连缀查询,这种查询很灵活,能够根据咱们实际的查询需求来动态配置查询参数。 那这里举个简单的例子,好比咱们想查询 news 表中全部评论数大于零的新闻,就能够这样写:
List<News> newsList = DataSupport.where("commentcount > ?", "0").find(News.class);
复制代码
能够看到,首先是调用了 DataSupport 的 where() 方法,在这里指定了查询条件。where() 方法接收任意个字符串参数,其中第一个参数用于进行条件约束,从第二个参数开始,都是用于替换第一个参数中的占位符的。那这个 where() 方法就对应了一条 SQL 语句中的 where 部分。
接着咱们在 where() 方法以后直接连缀了一个 find() 方法,而后在这里指定一个泛型类,表示用于查询哪张表。那么上面的一段代码,查询出的结果和以下 SQL 语句是相同的:
select * from users where commentcount > 0;
复制代码
可是这样会将 news 表中全部的列都查询出来,也许你并不须要那么多的数据,而是只要 title 和 content 这两列数据。那么也很简单,咱们只要再增长一个连缀就好了,以下所示:
List<News> newsList = DataSupport.select("title", "content")
.where("commentcount > ?", "0").find(News.class);
复制代码
能够看到,这里咱们新增了一个 select() 方法,这个方法接收任意个字符串参数,每一个参数要求对应一个列名,这样就只会把相应列的数据查询出来了,所以 select() 方法对应了一条 SQL 语句中的 select 部分。
那么上面的一段代码,查询出的结果和以下 SQL 语句是相同的:
select title,content from users where commentcount > 0;
复制代码
很好玩吧?不过这还不算完呢,咱们还能够继续连缀更多的东西。好比说,我但愿将查询出的新闻按照发布的时间倒序排列,即最新发布的新闻放在最前面,那就能够这样写:
List<News> newsList = DataSupport.select("title", "content")
.where("commentcount > ?", "0")
.order("publishdate desc").find(News.class);
复制代码
order() 方法中接收一个字符串参数,用于指定查询出的结果按照哪一列进行排序,asc 表示正序排序,desc 表示倒序排序,所以 order() 方法对应了一条 SQL 语句中的 order by 部分。
那么上面的一段代码,查询出的结果和以下 SQL 语句是相同的:
select title,content from users where commentcount > 0 order by publishdate desc;
复制代码
而后呢,也许你并不但愿将全部条件匹配的结果一次性所有查询出来,由于这样数据量可能会有点太大了,而是但愿只查询出前 10 条数据,那么使用连缀一样能够轻松解决这个问题,代码以下所示:
List<News> newsList = DataSupport.select("title", "content")
.where("commentcount > ?", "0")
.order("publishdate desc").limit(10).find(News.class);
复制代码
这里咱们又连缀了一个 limit() 方法,这个方法接收一个整型参数,用于指定查询前几条数据,这里指定成 10,意思就是查询全部匹配结果中的前 10 条数据。
那么上面的一段代码,查询出的结果和以下 SQL 语句是相同的:
select title,content from users where commentcount > 0 order by publishdate desc limit 10;
复制代码
刚才咱们查询到的是全部匹配条件的前 10 条新闻,那么如今我想对新闻进行分页展现,翻到第二页时,展现第 11 到第 20 条新闻,这又该怎么实现呢?不要紧,在 LitePal 的帮助下,这些功能都是十分简单的,只须要再连缀一个偏移量就能够了,以下所示:
List<News> newsList = DataSupport.select("title", "content")
.where("commentcount > ?", "0")
.order("publishdate desc").limit(10).offset(10)
复制代码
能够看到,这里咱们又添加了一个 offset() 方法,用于指定查询结果的偏移量,这里指定成 10,就表示偏移十个位置,那么原来是查询前 10 条新闻的,偏移了十个位置以后,就变成了查询第 11 到第 20 条新闻了,若是偏移量是 20,那就表示查询第 21 到第 30 条新闻,以此类推。所以,limit() 方法和 offset() 方法共同对应了一条 SQL 语句中的 limit 部分。
那么上面的一段代码,查询出的结果和以下 SQL 语句是相同的:
select title,content from users where commentcount > 0 order by publishdate desc limit 10,10;
复制代码
这大概就是 LitePal 中连缀查询的全部用法了。看出区别了吧?这种查询的好处就在于,咱们能够随意地组合各类查询参数,须要用到的时候就把它们连缀到一块儿,不须要用到的时候不用指定就能够了。对比一下 query() 方法中那冗长的参数列表,即便咱们用不到那些参数,也必需要传 null,是否是明显感受 LitePal 中的查询更加人性化?
不过,上述咱们的全部用法中,都只能是查询到指定表中的数据而已,关联表中数据是没法查到的,由于 LitePal 默认的模式就是懒查询,固然这也是推荐的查询方式。那么,若是你真的很是想要一次性将关联表中的数据也一块儿查询出来,固然也是能够的,LitePal 中也支持激进查询的方式,下面咱们就来一块儿看一下。
不知道你有没有发现,刚才咱们所学的每个类型的 find() 方法,都对应了一个带有 isEager 参数的方法重载,这个参数相信你们一看就明白是什么意思了,设置成 true 就表示激进查询,这样就会把关联表中的数据一块儿查询出来了。
好比说,咱们想要查询 news 表中 id 为 1 的新闻,而且把这条新闻所对应的评论也一块儿查询出来,就能够这样写:
News news = DataSupport.find(News.class, 1, true);
List<Comment> commentList = news.getCommentList();
复制代码
能够看到,这里并无什么复杂的用法,也就是在 find() 方法的最后多加了一个 true 参数,就表示使用激进查询了。这会将和 news 表关联的全部表中的数据也一块儿查出来,那么 comment 表和 news 表是多对一的关联,因此使用激进查询一条新闻的时候,那么该新闻所对应的评论也就一块儿被查询出来了。
激进查询的用法很是简单,就只有这么多,其它 find() 方法也都是一样的用法,就再也不重复介绍了。可是这种查询方式 LitePal 并不推荐,由于若是一旦关联表中的数据不少,查询速度可能就会很是慢。并且激进查询只能查询出指定表的关联表数据,可是无法继续迭代查询关联表的关联表数据。所以,这里我建议你们仍是使用默认的懒加载更加合适,至于如何查询出关联表中的数据,其实只须要在模型类中作一点小修改就能够了。修改 News 类中的代码,以下所示:
public class News extends DataSupport{
public List<Comment> getComments() {
return DataSupport.where("news_id = ?", String.valueOf(id)).find(Comment.class);
复制代码
能够看到,咱们在 News 类中添加了一个 getComments() 方法,而这个方法的内部就是使用了一句连缀查询,查出了当前这条新闻对应的全部评论。改为这种写法以后,咱们就能够将关联表数据的查询延迟,当咱们须要去获取新闻所对应的评论时,再去调用 News 的 getComments() 方法,这时才会去查询关联数据。这种写法会比激进查询更加高效也更加合理。
相信你已经体会到,LitePal 在查询方面提供的 API 已经至关丰富了。可是,也许你总会遇到一些千奇百怪的需求,可能使用 LitePal 提供的查询 API 没法完成这些需求。没有关系,由于即便使用了 LitePal,你仍然可使用原生的查询方式 (SQL 语句) 来去查询数据。DataSuppport 类中还提供了一个 findBySQL()方法,使用这个方法就能经过原生的 SQL 语句方式来查询数据了,以下所示:
Cursor cursor = DataSupport.findBySQL("select * from news where commentcount>?", "0");
复制代码
findBySQL() 方法接收任意个字符串参数,其中第一个参数就是 SQL 语句,后面的参数都是用于替换 SQL 语句中的占位符的,用法很是简单。另外,findBySQL() 方法返回的是一个 Cursor 对象,这和原生 SQL 语句的用法返回的结果也是相同的。
好了,这样咱们就把 LitePal 中提供的查询数据的方法所有都学完了,那么今天的文章就到这里,下一篇文章当中会开始讲解聚合函数的用法,感兴趣的朋友请继续阅读。