Android开发 | 数据持久化(下)

第三方数据库框架LitePal的配置及使用

第三方数据库框架LitePal的配置及使用

上一节总结了Android系统中数据持久化主要的几种方案——文件存储SharedPreferences存储,以及SQLite数据库存储。这几种方案针对不同的场景拥有各自的优势,对于复杂庞大的数据存储,SQLite提供了丰富完善的数据库接口,能完整地应对该情形,但其代码的复杂性却为开发带来了不少阻力。

本文继续就数据库存储方案进行拓展,引入对第三方库——LitePal的介绍,并总结它与SQLite的异同。

一、第三方库的引入

之前我们并未使用过第三方库,一直是基于Android SDK内嵌的库androidX进行学习,本文我们开始尝试使用第三方库来满足项目需求。尽管Android的内置库覆盖广、灵活度高,但是当我们想要做一个完整且独立的自定义组件(模块)以满足项目需求时,我们会发现需要造的轮子太多,大大拖慢了开发进度,并且Android内嵌的库并不能满足所有需求(通常Android内部的数据存储结构还得和外部接口提供的数据相适配),所以我们就不妨利用一些第三方被广泛验证了稳定性的库,这样可以大大提高开发效率。

LitePal是一个第三方的开源的Android数据库框架,本文将使用LitePal来建立上文中SQLite的表,并进行基本的增删改查和SQL语句的执行。

1.1 在项目中引入第三方库

编辑app/build.gradle,在dependencies闭包中追加:

1
implementation 'org.litepal.guolindev:core:3.2.3'

在编辑本文时笔者才发现,这个框架是郭霖老师开发的(guolindev)。

3.2.3为框架版本号,笔者在导入过程中发现,Android Studio已经摒弃了JPack源,因此如果只按上述方法导入,编译器将报错,这里还需在项目的settings.gradle中的dependencyResolutionManagement闭包的仓库中追加:

1
2
jcenter()
maven { url 'https://jitpack.io' }

最后重新sync同步gradle即可导入LitePal框架。

1.2 配置LitePal数据库

app/内新建assets资源文件夹,添加litepal.xml文件,配置如下:

1
2
3
4
5
6
7
<?xml version="1.0" encoding="utf-8" ?>
<litepal>
    <dbname value="BookStore"/>
    <version value="1"/>
    <list>
    </list>
</litepal>

其中,dbname标签用于指定数据库名称,version标签用于指定数据库版本,list标签用于列出该数据库中的表。

此外还需在清单文件的application标签中声明:

1
android:name="org.litepal.LitePalApplication

到此,LitePal的配置部分已完成。

二、新建数据库和更新数据库

SQLite数据库框架采用的是关系数据模型(关系数据模式),而LitePal采用的是对象关系映射模式(ORM)。这是一种在面向对象的语言和面向关系的数据库直接建立起的一种映射关系,故称对象关系映射模式

对象关系映射模式下的数据库就允许我们直接使用面向对象的思维去建立和操纵数据库,而不需要使用SQL语句。

2.1 新建数据库

下面通过创建一个Java Bean,再于litepal.xml中添加表声明,便能创建一个对应的表:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.cosyspark.litepal;

public class Book {
    private int id;
    private String name;
    private String author;
    private int pages;
    private double price;
    private String press;

    public int getId() {
        return id;
    }

    public void setId(int id){
        this.id = id;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getPress() {
        return press;
    }

    public void setPress(String press) {
        this.press = press;
    }
}

litepal.xml

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8" ?>
<litepal>
    <dbname value="BookStore"/>
    <version value="1"/>
    <list>
        <mapping class="com.cosyspark.litepal.Book"/>
    </list>
</litepal>

通过在list标签中添加mapping标签来声明要匹配的映射模型类。

最后通过ConnectorgetDatabase()方法来执行一次数据库操作,表便创建成功。

/androiddev-charpter6-datapersistence-b/database.webp
Database

通过sqlite3查看.db文件,可见创建表被成功创建。

2.2 更新数据库

向数据库中的book表添加press出版社这一属性,并在数据库中添加表Category。在之前使用SQLite更新数据库时,必须先将之前的表drop掉,但这就不免造成了数据丢失的问题。而在LitePal中,只需在litepal.xml中将版本号+1即可直接完成数据库的更新操作。

此时Book类需添加:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
      ···
      
public class Book{
      
      ···
      
    private String press;
      
      ···
      ···
      
    public String getPress() {
        return press;
    }

    public void setPress(String press) {
        this.press = press;
    }
}

litepal.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8" ?>
<litepal>
    <dbname value="testme"/>
    <version value="2"/>
    <list>
        <mapping class="com.cosyspark.litepal.Book"/>
        <mapping class="com.cosyspark.litepal.Category"/>
    </list>
</litepal>

三、表的增删改查

3.1 表中插入数据

之前使用SQLite插入数据时,先要构造一个ContenValues对象,在通过put()方法将数据放到该对象中,最后再调用SQLiteDatabaseinsert()方法向表中插入数据。

LitePal则更为简洁,只需先实例化一个Book对象,再利用setter设置成员变量的值,最后save()保存操作即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Button btn_addData = findViewById(R.id.btn_add_data);
btn_addData.setOnClickListener(v -> {
    Book book = new Book();
    book.setName("The Da Vinci Code");
    book.setAuthor("Dan Brown");
    book.setPages(454);
    book.setPrice(16.96);
    book.setPress("Unknown");
    book.save();
    Log.d("Litepal", "data added");
});

3.2 表中更新数据

对于LitePal而言,更新表的方式有多种。其中一种是通过对已经存储的对象进行操作后再save(),对于对象是否已经被存储,是通过isSaved()方法进行判断的。而isSaved()方法返回true的情形只有两种:执行过save()方法的对象,和通过查询API在数据库中查询到的对象。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Button btn_updateData = findViewById(R.id.btn_update_data);
btn_updateData.setOnClickListener(v -> {
    Book book = new Book();
    book.setName("The Lost Symbol");
    book.setAuthor("Dan Brown");
    book.setPages(510);
    book.setPrice(19.95);
    book.setPress("Unknown");
    book.save();
    book.setPrice(20.95);
    book.save();
    Log.d("Litepal", "data updated");
});

另一种途径是通过updateAll()方法指定条件约束来进行表数据的更新:

1
2
3
4
5
6
Button btn_updateData = findViewById(R.id.btn_update_data);
btn_updateData.setOnClickListener(v -> {
      Book book = new Book();
      book.setPrice(20.95);
      book.updateAll("name = ?","The Lost Symbol");
});

LitePal中更新数据至默认值时,不是通过属性.set()方法,而是通过setToDefault(<属性名>)

3.3 删除数据

通过LitePal.deleteAll()方法来删除表中数据,方法参数列表分别表示:待删除数据的表名,约束条件。如果参数列表的不指定约束条件,则会删除表中所有数据。

1
2
3
4
5
Button btn_deleteData = findViewById(R.id.btn_delete_data);
btn_deleteData.setOnClickListener(v -> {
    LitePal.deleteAll(Book.class, "price < ?", "18");
    Log.d("Litepal", "data deleted");
});

3.4 查询数据

在使用SQLitequery()方法时,必须指定所有参数,例如:

1
Cursor cursor = db.query("Book",null,null,null,null,null,null);

即使要查询的数据用不到很多约束条件,但在调用query()方法是还是得至少传入null。 但是在LitePal中就简便多了:

1
List<Book> books =  ListPal.findAll(Book.class):

只需调用findAll()方法并传入待查询的表名,即可返回所有的表项的对象数组。

除此之外,还有其他find方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
//查询表中第一条数据
findFirst(Book.class) 

//查询表中最后一条数据
findLast(Book.class) 

/* 定制查询:*/

// 查询书名和作者两列数据
List<Book> books = ListPal.select("name", "author").find(Book.class);

// 查询页数大于400页的书籍
List<Book> books = ListPal.where("pages > ?","400").find(Book.class);

// 以价格降序排列查询全部书籍
List<Book> books = ListPal.order("price desc").find(Book.class);

// 指定查询数量,e.g. 查询表中前行个数据
List<Book> books = ListPal.limit(3).find(Book.class);

// 指定查询结果的偏移量,e.g. 查询第2,3,4条数据
List<Book> books = ListPal.limit(3).offset(1).find(Book.class);

// 任意连缀组合,查询Book表中400页以上的第11~20条数据的书名、作者、页数三列数据
List<Book> books = ListPal.select("name", "author", "pages")
                          .where("pages > ?", "400")
                          .order("pages")
                          .limit(10)
                          .offset(10)
                          .find(Book.class);


/* 直接使用SQL语句查询:*/
Cursor cursor = LitePal.findBySQL("select * from Book where price > ?","200");
// findBySQL返回的Cursor对象,利用上节的方法进行解析。

四、本文内容的DEMO

给作者倒杯卡布奇诺 ~
Albresky 支付宝支付宝
Albresky 微信微信