配置:java
1. 引入Jar包或源码android
2. 配置litepal.xml数据库
在项目的assets目录下面新建一个litepal.xml文件,并将如下代码拷贝进去:编程
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="demo" ></dbname> <version value="1" ></version> <list> </list> </litepal>
配置文件至关简单,<dbname>用于设定数据库的名字,<version>用于设定数据库的版本号,<list>用于设定全部的映射模型,咱们稍后就会用到数组
3. 配置LitePalApplication数据结构
因为操做数据库时须要用到Context,而咱们显然不但愿在每一个接口中都去传一遍这个参数,那样操做数据库就显得太繁琐了。所以,LitePal使用了一个方法来简化掉Context这个参数,只须要在AndroidManifest.xml中配置一下LitePalApplication,全部的数据库操做就都不用再传Context了,以下所示:app
<manifest> <application android:name="org.litepal.LitePalApplication" ... > ... </application> </manifest>
固然,有些程序可能会有本身的Application,并在这里配置过了。好比说有一个MyApplication,以下所示:框架
<manifest> <application android:name="com.example.MyApplication" ... > ... </application> </manifest>
这时只须要修改一下MyApplication的继承结构,让它不要直接继承Application类,而是继承LitePalApplication类,就可使用一切都能正常工做了,代码以下所示:编程语言
public class MyApplication extends LitePalApplication { ... }
可是,有些程序可能会遇到一些更加极端的状况,好比说MyApplication须要继承另一个AnotherApplication,而且这个AnotherApplication仍是在jar包当中的,不能修改它的代码。这种状况应该算是比较少见了,可是若是你遇到了的话也不用急,仍然是有解释方案的。你能够把LitePal的源码下载下来,而后把src目录下的全部代码直接拷贝到你项目的src目录下面,接着打开LitePalApplication类,将它的继承结构改为继承自AnotherApplication,再让MyApplication继承自LitePalApplication,这样全部的Application就均可以在一块儿正常工做了。
全部的配置工做所有完成.ide
建表
LitePal中是如何建表的吧。根据对象关系映射模式的理念,每一张表都应该对应一个模型(Model),也就是说,若是咱们想要建一张news表,就应该有一个对应的News模型类。新建一个News类,而后,表中的每一列其实就是对应了模型类中的一个字段,好比news表中有id、title、content、publishdate、commentcount这几个列,那么在News类中就也应该有这几个字段,代码以下所示:以下所示:
package com.loaderman.litepaldemo; import java.util.Date; public class News { private int id; private String title; private String content; private Date publishDate; private int commentCount; }
其中id这个字段可写可不写,由于即便不写这个字段,LitePal也会在表中自动生成一个id列,毕竟每张表都必定要有主键的嘛。
这里我要特别说明一下,LitePal的映射规则是很是轻量级的,不像一些其它的数据库框架,须要为每一个模型类单独配置一个映射关系的XML,LitePal的全部映射都是自动完成的。根据LitePal的数据类型支持,能够进行对象关系映射的数据类型一共有8种,int、short、long、float、double、boolean、String和Date。只要是声明成这8种数据类型的字段都会被自动映射到数据库表中,并不须要进行任何额外的配置。
如今模型类已经建好了,咱们还差最后一步,就是将它配置到映射列表当中。编辑assets目录下的litepal.xml文件,在<list>标签中加入News模型类的声明:
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="demo" ></dbname> <version value="1" ></version> <list> <mapping class="com.loaderman.litepaldemo.News"></mapping> </list> </litepal>
只要你对数据库有任何的操做,news表就会被自动建立出来。好比说LitePal提供了一个便捷的方法来获取到SQLiteDatabase的实例,以下所示:
SQLiteDatabase db = Connector.getDatabase();
调用一下上述代码,news表就应该已经建立成功
升级表
添加表或者修改字段
须要建立一张comment表。得先建立一个Comment类了,以下所示:
package com.loaderman.litepaldemo; public class Comment { private int id; private String content; }
Comment类中有id和content这两个字段,也就意味着comment表中会有id和content这两列。
接着修改litepal.xml中的配置,在映射列表中新增Cooment类,并将版本号加1,以下所示:
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="demo" ></dbname> <version value="2" ></version> <list> <mapping class="com.loaderman.litepaldemo.News"></mapping> <mapping class="com.loaderman.litepaldemo.Comment"></mapping> </list> </litepal>
升级的操做就已经完成了,如今咱们只须要操做一下数据库,comment表就会自动生成了.
若是须要在comment表中添加一个publishdate列,只须要,Comment类中添加这样一个字段,而后须要在litepal.xml中对版本号加1就好了
这样当咱们下一次操做数据库的时候,publishdate列就应该会自动添加到comment表中。调用Connector.getDatabase()方法,而后从新查询comment表结构
不是说SQLite不支持删除列的命令吗?那LitePal又是怎样作到的呢?其实LitePal并无删除任何一列,它只是先将comment表重命名成一个临时表,而后根据最新的Comment类的结构生成一个新的comment表,再把临时表中除了publishdate以外的数据复制到新的表中,最后把临时表删掉。所以,看上去的效果好像是作到了删除列的功能。
这也是使用框架的好处,若是没有框架的帮助,咱们显然不会为了删除一个列而大废周章地去写这么多的代码,而使用框架的话,具体的实现逻辑咱们已经不用再关心,只须要控制好模型类的数据结构就能够了。
另外,若是你想删除某一张表的话,操做也很简单,在litepal.xml中的映射列表中将相应的类删除,表天然也就不存在了。其它的一些升级操做也都是相似的,相信你已经能触类旁通,这里就再也不赘述了。
表关联
表与表之间的关联关系一共有三种类型,一对1、多对1、和多对多,
表示两个表中的数据必须是一一对应的关系。这种场景其实并非很常见,咱们仍是经过例子来直观地体会一下,
如今已经建立好了news这张表,里面主要记录了新闻的标题和内容,那么除了标题和内容以外,有些新闻还可能带有一些导语和摘要,咱们把这两个字段放在一张introduction表中,做为新闻的简介。那么很显然,news表和introduction表就是一对一的关系了,由于一条新闻只能对应一个简介,一个简介也只能属于一条新闻。它们之间的对应关系大概以下图描述的同样:
能够看到,News1对应了Introduction2,News2对应了Introduction3,News3对应了Introduction1,但无论怎么样,它们都是一对一的关系。
因为数据库并不像面向对象的语言同样支持相互引用,若是想让两张表之间创建一对一的关系,通常就只能经过外键的方式来实现了。所以,一对一关系的表结构就能够这样设计:
请注意,introduction表中有一个news_id列,这是一个外键列,里面应该存放一个具体的新闻id,这样一条introduction就能对应一条news,也就实现一对一的关系了,以下图所示:
由此咱们就可以看出,id为1的introduction对应着id为2的news,id为2的introduction对应着id为3的news,id为3的introduction对应着id为1的news。须要注意的是,一对一的关系并无强制要求外键必须加在哪一张表上,你能够在introduction表中加一个news_id做为外键,也能够在news表中加一个introduction_id做为外键,无论使用哪种,均可以表示出它们是一对一的关联关系。
表示一张表中的数据能够对应另外一张表中的多条数据。这种场景比起一对一关系就要常见太多了,在咱们平时的开发工做中多对一关系真的是比比皆是。好比说如今咱们的数据库中有一个news表,还有一个comment表,它们两个之间就是典型的多对一关系,一条新闻能够有不少条评论,可是一条评论只能是属于一条新闻的。它们的关系以下图所示:
而这种多对一的关系在编程语言中是很是容易体现出来的,好比Java中就有专门集合类,如List、Set等,使用它们的话就能轻松简单地在对象之间创建多对一的关系,咱们稍后就会看到。那么,这里的难点仍然是在数据库表中如何创建这样的多对一关系。如今说难点其实已经不难了,由于前面咱们已经学会了一对一关系的创建方法,而多对一也是相似的。没错,数据库表中多对一的关系仍然是经过外键来创建的,只不过一对一的时候外键加在哪一张表上均可以,但多对一的时候关键必需要加在多方的表中。所以,多对一关系的表结构就能够这样设计:
在comment表中有一个news_id列,这是一个外键列,里面应该存放一个具体的新闻id,而且容许多条comment都存放同一个新闻id,这样一条评论就只能对应一条新闻,但一条新闻却能够有多条评论,也就实现多对一的关系了,以下图所示:
由此咱们就能够看出,id为一、二、3的三条评论是属于第一条新闻的,而id为四、5的两条评论是属于第二条新闻的。
表示两张关联表中的数据均可以对应另外一张表中的多条数据。这种场景也不算是很常见,但比一对一关系要稍微更加经常使用一些。举个例子,咱们都知道新闻网站是会将新闻进行种类划分的,这样用户就能够选择本身喜欢的那一类新闻进行浏览,好比说网易新闻中就会有头条、科技、娱乐、手机等等种类。每一个种类下面固然都会有许多条新闻,而一条新闻也多是属于多个种类的,好比iPhone6发布的新闻既能够属于手机种类,也能够属于科技种类,甚至还能够上头条。所以,新闻和种类之间就是一种多对多的关系,以下图所示:
能够看到,News1是属于Category1的,而News2和News3都是既属于Category1也属于Category2,如此复杂的关联关系该如何表示呢?在面向对象的编程语言中一切都是那么的简单,只须要在News类中使用集合类声明拥有多个Category,而后在Category类中也使用集合类声明拥有多个News就能够了,咱们稍后就会看到。而难点仍然是留在了数据库上,两张表之间如何创建多对多的关联关系呢,仍是用外键吗?确定不行了,多对多的状况只能是借助中间表来完成了。也就是说,咱们须要多创建一张表,这张表没什么其它做用,就是为了存放news表和category表之间的关联关系的,以下图所示:
注意这里咱们创建一张名为category_news的中间表,中间表的命名并无什么强制性的约束,但一个良好的命名规范可让你一眼就明白这张表是用来作什么的。中间表里面只有两列,并且也只须要有两列,分别是news表的外键和category表的外键,在这里存放新闻和种类相应的id,就可让它们之间创建关联关系了,以下图所示:
由此咱们就能够看出,第一条新闻是属于第一个种类的,而第二和第三条新闻,则既属于第一个种类,也属于第二个种类。反过来也能够这样看,第一个种类下面有第1、第2、第三这三条新闻,而第二个种类下面只有第2、第三这两条新闻。无论怎么看,多对多的关系都是成立的。
总结:即一对一关联的实现方式是用外键,多对一关联的实现方式也是用外键,多对多关联的实现方式是用中间表。
还须要有Introduction和Category这两个类,新建Introduction类,代码以下所示:
package com.loaderman.litepaldemo; public class Category { private int id; private String name; }
package com.loaderman.litepaldemo; public class Introduction { private int id; private String guide; private String digest; }
如今四个类都已经建好了,但目前它们都仍是各自独立的,互相之间没有任何联系,那么咱们如今就开始用极为简单易懂的方式来给它们创建关联吧。首先,News和Introduction是一对一的关系,那就能够在News类中添加以下引用:
private Introduction introduction;
就是这么简单,在News类中能够获得一个对应的Introduction的实例,那么它们之间就是一对一关系了。
接着Comment和News是多对一的关系,所以News中应该包含多个Comment,而Comment中应该只有一个News,因此就能够这样写:
private List<Comment> commentList = new ArrayList<Comment>();
先使用一个泛型为Comment的List集合来表示News中包含多个Comment,而后修改Comment类的代码,以下所示:
private News news;
在Comment类中声明了一个News的实例,这样就清楚地表示出了News中能够包含多个Comment,而Comment中只能有一个News,也就是多对一的关系了。
最后News和Category是多对多的关系,相信聪明的你必定已经知道该怎么写了。News中能够包含多个Category,因此仍然应该使用List集合来表示
private List<Category> categoryList = new ArrayList<Category>();
而Category中也能够包含多个News,所以Category类也应该使用相同的写法,以下所示:
private List<News> newsList = new ArrayList<News>();
关联关系都声明好了以后,咱们只须要将全部的实体类都添加到映射列表当中,并将数据库版本号加1就能够了。修改litepal.xml的代码,以下所示
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="demo" ></dbname> <version value="4" ></version> <list> <mapping class="com.loaderman.litepaldemo.News"></mapping> <mapping class="com.loaderman.litepaldemo.Comment"></mapping> <mapping class="com.loaderman.litepaldemo.Category"></mapping> <mapping class="com.loaderman.litepaldemo.Introduction"></mapping> </list> </litepal>
基本上到这里就能够轻松地说结束了,如今只须要任意操做一下数据库,表之间的关联关系就将会自动创建,好比说调用一下Connector.getDatabase()方法。
查看表结构发现:
introduction表的结构,多了一个news_id列,说明introduction表和news表之间的一对一关系
comment表的结构comment表中也有一个news_id的列,那么comment表和news表之间的多对一关系也已经创建好了
category_news这张中间表一共只有两列,一列是news_id,一列是category_id,分别对应着两张表的外键,这样news表和category表的多对多关系也创建好了
在项目里已经建好了News、Comment、Introduction、Category这几个实体类,经过这些实体类,LitePal就能够把相应的表自动建立出来。如今来观察这几个实体类,咱们发现这几个类都是没有继承结构的。没错,由于LitePal进行表管理操做时不须要这些实体类有任何的继承结构,当时为了简单起见就没有写。可是进行CRUD操做时就不行了,LitePal要求全部的实体类都要继承自DataSupport这个类,所以这里咱们就要把继承结构给加上才行。修改News类的代码,以下所示:
能够看到,这里只是让News类继承自了DataSupport,其它什么都没有改变。另外几个Comment、Introduction、Category类也使用一样的改法
继承了DataSupport类以后,这些实体类就拥有了进行CRUD操做的能力,那么好比想要存储一条数据到news表当中,就能够这样写
News news = new News(); news.title="这是一条新闻标题"; news.content="这是一条新闻内容"; news.publishDate=new Date(); news.save();
除此以外,save()方法仍是有返回值的,咱们能够根据返回值来判断存储是否成功
save()方法返回的是一个布尔值,用于表示存储成功仍是失败,但同时也说明这个方法是不会抛出异常的。有些朋友但愿若是存储失败的话就抛出异常,而不是返回一个false,那就可使用saveThrows()方法来代替
使用saveThrows()方法来存储数据,一旦存储失败就会抛出一个DataSupportException异常,咱们能够经过对这个异常进行捕获来处理存储失败的状况。
LitePal在存储数据的时候默默帮咱们作了不少的事情,好比多个实体类之间有关联关系的话,咱们不须要考虑在存储数据的时候怎么去创建数据与数据之间的关联,由于LitePal一切都帮咱们作好了。
仍是经过一个例子来看一下吧,Comment和News之间是多对一的关系,一条News中是能够包含多条评论的,所以咱们就能够这样写:
Comment comment1 = new Comment(); comment1.setContent("好评!"); comment1.setPublishDate(new Date()); comment1.save(); Comment comment2 = new Comment(); comment2.setContent("赞一个"); comment2.setPublishDate(new Date()); comment2.save(); News news = new News(); news.getCommentList().add(comment1); news.getCommentList().add(comment2); news.setTitle("第二条新闻标题"); news.setContent("第二条新闻内容"); news.setPublishDate(new Date()); news.setCommentCount(news.getCommentList().size()); news.save();
能够看到,这里先是存储了一条comment1数据,而后存储一条comment2数据,接着在存储News以前先把刚才的两个Comment对象添加到了News的commentList列表当中,这样就表示这两条Comment是属于这个News对象的,最后再把News存储到数据库中,这样它们之间的关联关系就会自动创建了。
LitePal对集合数据的存储还专门提供了一个方法,好比说咱们有一个News集合,那么应该怎样去存储这个集合中的每条News呢?传统状况下能够这样写:经过一个循环来遍历出这个集合中的每个News对象,而后逐个调用save()方法。这样的写法固然是能够的,可是效率会比较低,由于调用save()方法的时候除了会执行存储操做以外,还会去分析News类的关联关系,那么每次循环都去从新分析一遍关联关系显然是比较耗时的。所以,LitePal提供了一个saveAll()方法,专门用于存储集合数据的,用法以下所示:
List<News> newsList; ... DataSupport.saveAll(newsList);
saveAll()方法接收一个Collection集合参数,只要把待存储的集合数据传入便可。这个方法能够完成和上面一段代码彻底同样的功能,但效率却会高得多,并且写法也更加简单。
LitePal的修改和删除操做
public static int update(Class<?> modelClass, ContentValues values, long id)
ContentValues values = new ContentValues(); values.put("title", "今日iPhone6发布"); DataSupport.update(News.class, values, 2);
public static int updateAll(Class<?> modelClass, ContentValues values, String... conditions)
updateAll()方法表示修改多行记录,其中第一个参数仍然是Class,第二个参数仍是ContentValues对象,第三个参数是一个conditions数组,用于指定修改哪些行的约束条件,返回值表示这次修改影响了多少行数据。好比说咱们想把news表中标题为“今日iPhone6发布”的全部新闻的标题改为“今日iPhone6 Plus发布”,就能够这样写:
ContentValues values = new ContentValues(); values.put("title", "今日iPhone6 Plus发布"); DataSupport.updateAll(News.class, values, "title = ?", "今日iPhone6发布");
重点咱们看一下最后的这个conditions数组,因为它的类型是一个String数组,咱们能够在这里填入任意多个String参数,其中最前面一个String参数用于指定约束条件,后面全部的String参数用于填充约束条件中的占位符(即?号),好比约束条件中有一个占位符,那么后面就应该填写一个参数,若是有两个占位符,后面就应该填写两个参数,以此类推。
好比说咱们想把news表中标题为“今日iPhone6发布”且评论数量大于0的全部新闻的标题改为“今日iPhone6 Plus发布”,就能够这样写:
ContentValues values = new ContentValues(); values.put("title", "今日iPhone6 Plus发布"); DataSupport.updateAll(News.class, values, "title = ? and commentcount > ?", "今日iPhone6发布", "0");
能够看出,经过占位符的方式来实现条件约束明显要比原生的API更加简单易用。
那么若是咱们想把news表中全部新闻的标题都改为“今日iPhone6发布”,该怎么写呢?其实这就更简单了,只须要把最后的约束条件去掉就好了,以下所示:
ContentValues values = new ContentValues(); values.put("title", "今日iPhone6 Plus发布"); DataSupport.updateAll(News.class, values);
固然有些朋友可能会以为这样用起来仍是有点复杂,由于这个ContentValues对象很烦人,每次建立它的时候都要写不少繁琐的代码。不要紧,LitePal也充分考虑了这种状况,提供了一种不须要ContentValues就能修改数据的方法,下面咱们尝试使用这种新方法来完成上述一样的功能。
好比把news表中id为2的记录的标题改为“今日iPhone6发布”,就能够这样写:
News updateNews = new News(); updateNews.setTitle("今日iPhone6发布"); updateNews.update(2);
此次咱们并无用ContentValues,而是new出了一个News对象,把要修改的数据直接set进去,最后调用一下update()方法并传入id就能够了。不只不用建立ContentValues对象,连表名都不用指定了,由于News对象默认就是修改的news表。
这是其中一种用法,那么若是咱们想把news表中标题为“今日iPhone6发布”且评论数量大于0的全部新闻的标题改为“今日iPhone6 Plus发布”,就能够这样写:
News updateNews = new News(); updateNews.setTitle("今日iPhone6发布"); updateNews.updateAll("title = ? and commentcount > ?", "今日iPhone6发布", "0");
可是这种用法有一点须要注意,就是若是咱们想把某一条数据修改为默认值,好比说将评论数修改为0,只是调用updateNews.setCommentCount(0)这样是不能修改为功的,由于即便不调用这行代码,commentCount的值也默认是0。因此若是想要将某一列的数据修改为默认值的话,还须要借助setToDefault()方法。用法也很简单,在setToDefault()方法中传入要修改的字段名就能够了(类中的字段名),好比说咱们想要把news表中全部新闻的评论数清零,就能够这样写:
News updateNews = new News(); updateNews.setToDefault("commentCount"); updateNews.updateAll();
使用LitePal删除数据
public static int delete(Class<?> modelClass, long id)
delete()方法接收两个参数,第一个参数是Class,传入咱们要删除的那个类的Class就好,第二个参数是一个指定的id,表示咱们要删除哪一行数据。
那么好比说咱们想删除news表中id为2的记录,就能够这样写:
DataSupport.delete(News.class, 2);
须要注意的是,这不只仅会将news表中id为2的记录删除,同时还会将其它表中以news id为2的这条记录做为外键的数据一块儿删除掉,由于外键既然不存在了,那么这么数据也就没有保留的意义了。
DataSupport中也提供了一个经过where语句来批量删除数据的方法,先看一下方法定义:
public static int deleteAll(Class<?> modelClass, String... conditions)
看起来很眼熟吧?很是简单,deleteAll()方法接收两个参数,第一个参数是Class,传入咱们要删除的那个类的Class就好,第二个参数是一个conditions数组,用于指定删除哪些行的约束条件,返回值表示这次删除了多少行数据,用法和updateAll()方法是基本相同的。
那么好比说咱们想把news表中标题为“今日iPhone6发布”且评论数等于0的全部新闻都删除掉,就能够这样写:
DataSupport.deleteAll(News.class, "title = ? and commentcount = ?", "今日iPhone6发布", "0");
而若是咱们想把news表中全部的数据所有删除掉,就能够这样写:
DataSupport.deleteAll(News.class);
在不指定约束条件的状况下,deleteAll()方法就会删除表中全部的数据了
除了DataSupport类中提供的静态删除方法以外,还有一个删除方法是做用于对象上的,即任何一个继承自DataSupport类的实例均可以经过调用delete()这个实例方法来删除数据。但前提是这个对象必定是要持久化以后的,一个非持久化的对象若是调用了delete()方法则不会产生任何效果
好比说下面这种写法:
News news = new News(); news.delete();
这里new出了一个News对象,这个对象明显是没有持久化的,那么此时调用delete()方法则不会删除任何数据。但若是咱们以前将这个对象持久化过了,那么再调用delete()方法就会把这个对象对应的数据删除掉了,好比:
News news = new News(); news.setTitle("这是一条新闻标题"); news.setContent("这是一条新闻内容"); news.save(); ... news.delete();
一个对象若是save过了以后,那就是持久化的了。除了调用save()方法以外,经过DataSupport中提供的查询方法从数据库中查出来的对象也是通过持久化的,查询的功能咱们会在下篇博客中讲解。
另外还有一个简单的办法能够帮助咱们判断一个对象是不是持久化以后的,DataSupport类中提供了一个isSaved()方法,这个方法返回true就表示该对象是通过持久化的,返回false则表示该对象未通过持久化。那么删除一个对象对应的数据也就能够这样写了:
News news; ... if (news.isSaved()) { news.delete(); }
查询
查询news表中id为1的这条记录,使用LitePal就能够这样写
News news = DataSupport.find(News.class, 1);
想要获取news表中的第一条数据,只须要这样写:
News firstNews = DataSupport.findFirst(News.class);
想要获取News表中的最后一条数据:
News lastNews = DataSupport.findLast(News.class);
news表中id为一、三、五、7的数据都查出来,这时候用findAll()。这个方法的用法和find()方法是很是相似的,只不过它能够指定多个id,而且返回值也再也不是一个泛型类对象,而是一个泛型类集合,以下所示:
List<News> newsList = DataSupport.findAll(News.class, 1, 3, 5, 7);
能够看到,首先咱们是调用的findAll()方法,而后这个方法的第一个参数仍然是指定的泛型类,可是后面的参数就很随意了,你能够传入任意个id进去,findAll()方法会把全部传入的id所对应的数据所有查出来,而后一块儿返回到List<News>这个泛型集合当中。
虽然说这个语法设计算是至关人性化,可是在有些场景或许不太适用,由于可能要你要查询的多个id已经封装到一个数组里了。那么不要紧,findAll()方法也是接收数组参数的,因此说一样的功能你也能够这样写:
long[] ids = newlong[] { 1, 3, 5, 7 }; List<News> newsList = DataSupport.findAll(News.class, ids);
查询全部数据的写法更简单,只须要这样写:
List<News> allNews = DataSupport.findAll(News.class);
连缀查询:
这种查询很灵活,能够根据咱们实际的查询需求来动态配置查询参数。 那这里举个简单的例子,好比咱们想查询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 orderby publishdate desc;
而后呢,也许你并不但愿将全部条件匹配的结果一次性所有查询出来,由于这样数据量可能会有点太大了,而是但愿只查询出前10条数据,那么使用连缀一样能够轻松解决这个问题,代码以下所示:
List<News> newsList = DataSupport.select("title", "content") .where("commentcount > ?", "0") .order("publishdate desc").limit(10).find(News.class);
那么上面的一段代码,查询出的结果和以下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) .find(News.class);
能够看到,这里咱们又添加了一个offset()方法,用于指定查询结果的偏移量,这里指定成10,就表示偏移十个位置,那么原来是查询前10条新闻的,偏移了十个位置以后,就变成了查询第11到第20条新闻了,若是偏移量是20,那就表示查询第21到第30条新闻,以此类推。所以,limit()方法和offset()方法共同对应了一条SQL语句中的limit部分。
那么上面的一段代码,查询出的结果和以下SQL语句是相同的:
select title,content from users where commentcount > 0 orderby publishdate desc limit 10,10;
激进查询
不过,上述咱们的全部用法中,都只能是查询到指定表中的数据而已,关联表中数据是没法查到的,由于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的聚合函数
LitePal中一共提供了count()、sum()、average()、max()和min()这五种聚合函数,基本上已经将SQL语句当中最经常使用的几种聚合函数都覆盖了,那么下面咱们就来对这五种聚合函数的用法
count()方法主要是用于统计行数的,刚才演示了如何经过SQL语句来统计news表中一共有多少行,那么下面咱们来看一下如何经过LitePal来实现一样的功能,代码以下所示:
int result = DataSupport.count(News.class);
你没有看错!就是这样一行代码就能够了。调用DataSupport类当中的count()方法,count()方法接收一个Class参数,用于指定去统计哪张表当中的数据,而后返回值是一个整型数据,也就是统计出的结果了。
除此以外,LitePal中全部的聚合函数都是支持连缀的,也就是说咱们能够在统计的时候加入条件语句。好比说想要统计一共有多少条新闻是零评论的,就能够这样写:
int result = DataSupport.where("commentcount = ?", "0").count(News.class);
这个用法和咱们在上一篇文章当中学到的连缀查询是比较像的,在DataSupport类中首先指定一个where语句用于条件约束,而后连缀一个count()方法,这样统计出的就是知足条件语句的结果了。连缀不只适用于count()方法,也一样适用于下面咱们将要介绍的全部方法,但因为用法都是相同的,后面就再也不重复介绍了。
看完了count()方法应该是以为很是简单吧,剩下的几个聚合函数也是一样简单的,咱们继续来学习一下。
sum()方法主要是用于对结果进行求合的,好比说咱们想要统计news表中评论的总数量,就能够这样写:
int result = DataSupport.sum(News.class, "commentcount", int.class);
sum()方法的参数要稍微多一点,咱们来一一看下。第一个参数很简单,仍是传入的Class,用于指定去统计哪张表当中的数据。第二个参数是列名,表示咱们但愿对哪个列中的数据进行求合。第三个参数用于指定结果的类型,这里咱们指定成int型,所以返回结果也是int型。
须要注意的是,sum()方法只能对具备运算能力的列进行求合,好比说整型列或者浮点型列,若是你传入一个字符串类型的列去求合,确定是得不到任何结果的,这时只会返回一个0做为结果。
average()方法主要是用于统计平均数的,好比说咱们想要统计news表中平均每条新闻有多少评论,就能够这样写:
double result = DataSupport.average(News.class, "commentcount");
其中average()方法接收两个参数,第一个参数不用说,仍然是Class。第二个参数用于指定列名的,表示咱们想要统计哪一列的平均数。须要注意的是,这里返回值的类型是double型,由于平均数基本上都是会带有小数的,用double类型能够最大程序保留小数位的精度。
一样地,average()方法也只能对具备运算能力的列进行求平均值,若是你传入了一个字符串类型的列,也是没法获得任何结果的,这时一样只会返回一个0做为结果。
max()方法主要用于求出某个列中最大的数值,好比咱们想要知道news表中全部新闻里面最高的评论数是多少,就能够这样写:
int result = DataSupport.max(News.class, "commentcount", int.class);
能够看到,max()方法接收三个参数,第一个参数一样仍是Class,用于指定去统计哪张表当中的数据。第二个参数是列名,表示咱们但愿统计哪一个列中的最大值。第三个参数用于指定结果的类型,根据实际状况来选择传入哪一种类型就好了。
那么不用多说,max()方法也只能对具备运算能力的列进行求最大值的,但愿你在使用的时候可以谨记这一点。
min()方法主要用于求出某个列中最小的数值,好比咱们想要知道news表中全部新闻里面最少的评论数是多少,就能够这样写:
int result = DataSupport.min(News.class, "commentcount", int.class);
min()方法和max()方法的用法基本上是如出一辙的,参数也是彻底相同,只是方法名变了一下。它们一个是求出某一列中的最大值,一个是求出某一列中的最小值,仅此而已。
News news; ... if (news.isSaved()) { news.delete(); }