相信我,当你用上 LitePal 之后,你将再也不想去碰 SQLiteDatabase 了。
LitePal是一款开源的 Android 数据库框架,它采用了对象关系映射(ORM)的模式,并将我们平时开发最常用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成各种建表和增删改查的操作。LitePal 的项目主页上也有详细的使用文档,地址是https://github.com/LitePalFramework/LitePal
OK,如果看不懂,没关系,总之它很好用就是了
首先,新建一个项目 LitePalTest ,然后开启 LitePal 之旅,车速有点慢哈,耐心看下去。
如果你看过郭霖大神的《第一行代码》第二版,可能就没必要看下去了,因为内容基本一致。
1. 配置 LitePal
首先,修改项目的 app/build.gradle 文件,在 dependencies 闭包中申明LitePal库的引用
compile 'org.litepal.android:core:1.5.1'
开发者肯定会对框架的更新的,所以下载最新版的话请前往项目主页查看https://github.com/LitePalFramework/LitePal
接下来需要配置 litepal.xml 文件。右击 app/src/main 目录 ——>New——>Directory,创建一个 assets 目录
然后在 assets 目录下新建一个 litepal.xml 文件,接着编辑该文件,如下所示
最后还需要修改一下 AndroidManifest.xml 中的代码
加上我选中的哪一行,只有加上这个才能让 LitePal 的所有功能正常运行,关于 application 的作用,还请看官自行 Google 。
现在 Litepal 的配置工作已经做完了,让我们开始正式使用它吧!
2. 创建和升级数据库
先分析一下数据表,需要哪些数据,我们是一个书店应用,呢么它是不是应该存储书的数据,那么一本书就是一条数据,这条数据有书名、书的作者、书的价格、书的页数,还有数据的ID这些字段
LitePal 是采取的对象关系映射(ORM),我们的编程语言是面向对象的,而使用的数据库则是关系型数据库,那么将面向对象的语言和面向关系的数据库之间建立一种映射关系,这就是对象关系映射了,所以我们可以用面向对象的思维来操作数据库了。
所以根据刚才的分析结果,我们来定义一个 Book 类,一定要继承 DataSupport 类,代码如下所示:
package com.example.litepaltest;
import org.litepal.crud.DataSupport;
/**
* Created by jethro on 2017/6/8.
*/
public class Book extends DataSupport{
private int id ;
private String name; //书名
private double price; //价格
private int pages; //页数
private String author; //作者
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
这是一个典型的 Java Bean ,在 Book 类中我们定义了 id、name、price、pages、author 这几个字段,并且生成了相对应的 getter 和 setter 方法,不会生成请自行谷歌 “ Android studio 生成getter and setter ”
接着我们修改一下 litepal.xml 的代码,如下所示
这里使用<mapping>
标签来申明我们要配置的数据类,或者说数据表,注意这里一定要使用完整的类名,只写 Book 可不行哈,不管有多少数据表(书上是写的映射模型类,我觉得这个还是准确点,后面就叫映射模型类好了),都使用同样的方式配置在<list>
标签下即可。
OK,这样所有的工作都完成了,使用任何一次数据库的操作,BookStore.db 数据库都会被创建出来,然后 Book 数据表就在这个数据库里了。
如果你想再 Book 表中新添加一个 press(出版社) 列,直接修改 Book 类中的代码,添加一个
press 变量即可,如下所示:
记得添加对应的 get 和 set 方法,与此同时我们还想添加一张 category(类别) 表,那么只需要像刚才一样,新建一个 Category 类就可以了,代码如下所示
改完了所有我们想改的动西,只需要记得将版本号加1就行了,当然由于我们刚才又添加了一个模型类 Category,因此也需要将他添加到<list>
标签中,代码如下所示:
3. 操作数据库
对于数据库的操作,总结起来就是四个字:增、删、改、查
增:往数据库里增加数据,或者说添加数据
删:根据条件来删除数据库里的数据
改:根据条件来修改数据库里的数据
查:根据条件来查询数据库里的数据,这是一个比较考验开发者技术的操作
接着我们就对这四个操作一一讲解
3.1 增
现在我们来往Book表中添加数据,我们 MainActivity 现在有四个按钮,分别是添加数据、修改数据、删除数据、查询数据,先看添加数据的代码,代码如下:
这段代码很简单也很神奇,首先我们创建了一个 Book 的实例,然后调用 Book 类中的 set 方法对数据进行设置,最后再调用 book.save() 方法就能对完成数据添加操作了。那么这个 save() 是从哪儿来的呢?当然是从 DataSupport类中继承来的了,DataSupport 类还给我们提供了丰富的增删改查的方法。现在我们按一下添加数据按钮,再按查询数据就能看到我们刚才添加的数据了。查询按钮的代码最后会进行讲解
3.2 改
学习完了如何添加数据,接下来我们看看如何更新数据,更新数据要比添加数据要稍微复杂一点,因为它的 API 接口比较多,这里我们只介绍几种最常用的更新方式
首先,最简单的一种更新方式就是对已存储的对象重新设值,然后重新调用 save() 方法即可。那么这里我们就要了解一个概念,什么是已存储的对象?
对于LitePal来说,对象是否已储存是根据 model.isSaved() 方法的结果来判断的,返回 true 表示已储存,返回 false 表示未储存。那么接下来的问题是,什么情况下会返回 true ,什么情况下会返回 false 呢?
实际上只有两种情况下 isSaved() 方法才会返回 true ,一种情况是已经调用 save() 方法去添加数据了,此时 model 会被认为是已储存的对象。另一种情况是 model 对象是通过查询得来的对象,由于是从数据库中查到的对象,因此也会被认为是已储存的对象。
由于查询数据我们还学到,因此只能先通过第一种情况来进行验证,修改MainActivity中的代码,如下所示:
这里我们先是添加了一条书的数据,然后发现作者其实是霍金,不是老王,搞错了,于是又调用
setAuthor() 方法设置了一下作者,为了表示歉意,我们把价格也降低成了 14.26 元,最后又调用 save 方法保存了一下,此时 LitePal 会发现当前的 Book 对象是已储存的,于是就不会再向数据库去添加一条新的数据,而是会直接更新当前的数据。
此时我们按一下修改数据按钮,再按一下查询按钮
可以看到Book表中新增了一本书,但并不是一开始我们设置的作者老王,说明我们的更新操作是成功了。但是这样的更新方式限制性太大了,接下来我们学习另外一种非更加灵巧的更新方式。修改MainActivity的代码,如下所示:
可以看到这里我们先是 new 出了一个 Book 对象,然后直接调用 setPrice() 方法设置要更新的价格,最后调用 updateAll() 方法去执行更新操作。注意 updateAll() 可以指定一个约束条件,和 sql 的 where 参数类似(不懂?自己去学学 SQL 吧)但更加简洁,如果不指定条件的话,就表示更新所有数据,这里我们指定将书名是时间简史并且作者是霍金的书价格更新为9.9。现在重新运行一下,点击修改数据按钮,再点查询数据按钮,结果如图:
意料之中,《时间简史》的价格成功被更新为了9.9,怎么样?是不是很简单?
3.3 删
使用 LitePal 删除数据的方式主要有两种,第一种比较简单,就是直接调用已储存对象的 delete() 方法就可以了,对于已储存对象的概念,我们上一小节已经学习过了。也就是说,调用过
save() 方法的对象,或者是通过LitePal提供的查询 API 查出来的对象,都是可以直接使用 delete() 方法来删除数据的。这种方式比较简单,只是把第二次 save() 换成了 delete() 方法而已,我们就不进行代码演示了,下面来看另外一种删除数据的方式。
修改 MainActivity 的代码,如下所示
就一行代码,这里调用了 DataSupport.deleteAll() 方法来删除数据,其中deleteAll()方法的第一个参数用于指定删除那张表中的数据,Book.class 就意味着删除 Book 表中的数据,后面的参数用于指定约束条件,应该不难理解。那么这行代码的意思就是,删除 Book 表中的价格价格低于20元的书,目前有两本书,一本是 16.9 一本是 9.9,刚好可以看出效果。
现在重新运行一下程序,并点击删除数据按钮,然后查询数据按钮,结果如图所示
可以看到并没有查询结果,说明删除成功了,另外,deleteAll() 方法如果不指定约束条件,就以为着你要删除表中所有的数据,着一点和 updateAll() 方法是比较相似的。
3.4 查
终于又到了最复杂的查询数据部门了,不过着嘴复杂也只是相对于过去而言,因为使用 LitePal 来查询数据一点都不复杂。我一直都认为 LitePal在查询数据 API 方面的设计极为人性化,要是想获取表中的数据如果用 SQLite 的 query() 方法,冗长的参数列表让人看着头疼,即使多数参数都是用不到的,也不得不传入 null ,如下所示:
Cursor cursor = db.query("Book",null,null,null,null,null,null);
这样的代码恐怕是没人喜欢的,而使用LitePal则会享受到简化的操作和优雅的体验
上面的代码是查询 Book 表中的全部数据,那么如果使用 LitePal 来查询的话,代码会是怎么样的?非常简单,只需要这样写:
List<Book> books = DataSupport.findAll(Book.class);
怎么样?这样的代码是不是就简单易懂得多了?没有冗长得参数,只需要调用一下 findAll() 方法,然后通过 Book.class 参数指定查询 Book 表就可以了。findAll() 方法得返回值是一个 Book 类型得 List 集合,然后你就可以轻松得获取 Book 对象里的数据了。
下面通过一个完整的例子来实践一下吧,修改 MainActivity 中的代码,如下所示:
查询的那段代码刚刚已经解释过了,接下来就是遍历 List 集合中的 Book 对象,并将其中的书名、作者、价格数据打印出来。因为刚刚我们删除了所有的数据,那么现在点两下添加数据按钮,再点查询数据按钮,结果如下:
除了 findAll 方法之外,LitePal 还提供了很多其他非常有用的查询 API。比如我们想要查询 Book 表中的第一条数据就可以这样写:
Book firstBook = DataSupport.findFirst(Book.class);
查询 Book 表中的最后一条数据就可以这样写
Book firstBook = DataSupport.findLast(Book.class);
我们还可以通过连缀查询功能来定制更多的查询功能。
- select() 方法用于指定查询那几列的数据,对应了 SQL 当中的 select 关键字。比如只查 name 和 author 着两列的数据,就可以这样写:
List<Book> books = DataSupport.select("name", "author").find(Book.class);
- where() 方法用于指定约束条件,对应了 SQL 当中的 where 关键字。比如只查价格低于 10 块钱的书,就可以这么写:
List<Book> books = DataSupport.where("price < ?", "10").find(Book.class);
- order() 方法用于指定结果的排序方式,对应了 SQL 当中的 order by 关键字。比如将查询结果按照书价从高到低排序,就可以这样写:
List<Book> books = DataSupport.order("price desc").find(Book.class);
//其中 desc 表示降序排列,asc 或者不屑表示升序排列。
- limit() 方法用于指定查询结果的数量,比如只查询表中的前 3 条数据,就可以这么写:
List<Book> books = DataSupport.limit(3).find(Book.class);
- offset() 方法用于指定查询结果的偏移量,比如查询表中的第 2 ~ 4 条数据,就可以这么写:
List<Book> books = DataSupport.limit(3).offset(1).find(Book.class);
当然,你还可以对这5个方法进行任意的连缀组合,来完成一个比较复杂的查询操作:
List<Book> books = DataSupport.select("name", "author", "price")
.where("price < ?", "10")
.order("price")
.limit(10)
.offset(10)
.find(Book.class);
这段代码就表示,查询 Book 表中的第 11 ~ 20 条满足与价格小于 10 块钱的条件的数据,并且只要 name、author、price 这三列数据。并将查询结果按照价格升序排列。
怎么样?是不是感觉 LitePal 的查询功能非常强大?并且代码简洁,逻辑清晰?关于 LitePal 的查询 API 就讲到这里了,这些 API 已经足够我们应用绝大多数厂场景的查询需求了。当然如果你实在有一些特殊的需求,上述的 API 已经满足不了你的时候,LitePal 任然支持原生的 SQL 来进行查询:
Cursor c = DataSupport.findBySQL("select * from Book where pages > ? and price < ?", "400", "20");
这里的 Cursor 对象里的数据如何取出还请百度之。
4. 结束语
非常感谢看官能看到这里,相信你已经能完成一些简单的数据储存了吧?如果对于条件约束这些不理解,还是建议去自学一下 SQL