Litepal——作为带我入行的第一本教学书籍《Android第一行代码》的作者郭霖老师所写出来的持久化框架,几乎算是我接触Android世界之后第一个遇到的框架,故将该框架列为一系列学习框架博客的首位。
根据Litepal的GitHub主页:Litepal,可以看到该框架的一些简介:
LitePal is an open source Android library that allows developers to use SQLite database extremely easy. You can finish most of the database operations without writing even a SQL statement, including create or upgrade tables, crud operations, aggregate functions, etc. The setup of LitePal is quite simple as well, you can integrate it into your project in less than 5 minutes.
事实上,正如这段简介所说,集成Litepal相当简单,不需要超过五分钟时间。使用Litepal,也适合对sql语言还不熟悉的开发者快速上手。
让我们继续浏览Litepal的GitHub主页,可以发掘Litepal的一些特性:
- Using object-relational mapping (ORM) pattern.
- Almost zero-configuration(only one configuration file with few properties).
- Maintains all tables automatically(e.g. create, alter or drop tables).
- Multi databases supported.
- Encapsulated APIs for avoiding writing SQL statements.
- Awesome fluent query API.
- Alternative choice to use SQL still, but easier and better APIs than the originals.
- More for you to explore.
用大白话来描述的话,可以列举如下:
总之,看到Litepal具有这么多良好的特性,读者是否心动了呢。理论的话不多说,我们现在就开始正式地使用Litepal进行数据库的相关操作
PS:如果有曾经学习过Java的ORM框架——Mybatis的读者,应该不会对Litepal的使用太陌生,因为它们都使用了xml文件进行相应的配置
现在Android框架的集成相比于IDE还为ADT的时代,要方便了许多。原因是现在的主流IDE是Android Studio,而AS默认使用了Gradle进行版本的配置管理,这让集成框架变得简单了许多。
在build.gradle下,添加以下语句,然后重新sync,即可将Litepal集成到你的项目中:
implementation 'org.litepal.android:java:3.0.0'
当然,目前Android的主流开发语言,除了Java之外,还有Kotlin,Litepal同样具有Kotlin版本的(这里的演示仅针对Java,Kotlin版本的异曲同工)依赖:
implementation 'org.litepal.android:kotlin:3.0.0'
可以根据个人需求进行配置。
集成了Litepal之后,要想正式使用它还需要进行一些配置
<litepal>
<dbname value="androidframelearn"/>
<version value="1"/>
<list>
list>
litepal>
android:name="org.litepal.LitePalApplication"
android:name="com.example.MyOwnApplication"
,然后再编写MyOwnApplication类,代码如下:public class MyOwnApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
LitePal.initialize(this);
}
...
}
两种方式亦可,Litepal的作者建议若使用第二种方式,需要尽快地调用LitePal.initialize(this);
所以将其放在onCreate()
方法是最好的。
刚才在介绍的时候已经说过,Litepal采取的是对象关系映射(ORM)的模式,那么什么是对象关系映射呢?简单点说,我们使用的编程语言是面向对象语言,而使用的数据库则是关系型数据库,那么将面向对象的语言和面向关系的数据库之间建立一种映射关系,这就是对象关系映射了。
不过你可千万不要小看对象关系映射模式,它赋予了我们一个强大的功能,就是可以用面向对象的思维来操作数据库,而不用再和SQL语句打交道了,不信的话我们现在就来体验一下。像往常使用SQLiteOpenHelper类,为了创建一张Book表需要先分析表中应该包含哪些列,然后再编写出一条建表语句,最后在自定义的SQLiteOpenHelper中去执行这条建表语句。但是使用LitePal,你就可以用面向对象的思维来实现同样的功能了,定义一个Book类,代码如下所示:
package com.androidframelearn.dao_litapal;
import org.litepal.crud.LitePalSupport;
public class Book extends LitePalSupport {
private int id;
private String author;
private double price;
private int pages;
private String name;
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 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 getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
这是一个典型的Java bean,在Book类中我们定义了 id、author,price, pages,name这几个字段,并生成了相应的getter和setter方法。相应你已经能猜到了,Book类就会对应数据库中的Book表,而类中的每一个字段分别对应了表中的每一个列,这就是对象关系映射直观的体验,现在你能够理解得更加清楚了吧。
接下来我们还需要将Book类添加到映射模型列表当中,修改litepal.xml中的代码,如下所示:
<litepal>
<dbname value="androidframelearn"/>
<version value="1"/>
<list>
<mapping class="com.androidframelearn.dao_litapal.Book"/>
list>
litepal>
这里使用标签来声明我们要配置的映射模型类,注意一定要使用完整的类名。不管有多少模型类需要映射,都使用同样的方式配置在标签下即可。
没错,这样就已经把所有工作都完成了,现在只要进行任意一次数据库的操作,BookStore.db数据库应该就会自动创建出来。为了更好地演示代码,我们将布局文件所需要的功能一次性编写好,activity_main.xml代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/btn_db_create"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="创建数据库"/>
<Button
android:id="@+id/btn_db_query"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="查询数据"/>
<Button
android:id="@+id/btn_db_insert"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="插入数据"/>
<Button
android:id="@+id/btn_db_update"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="更新数据"/>
<Button
android:id="@+id/btn_db_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="删除数据"/>
LinearLayout>
该布局效果如图所示:
接下来,修改MainActivity,除了给按钮注册点击事件,还需要编写不同的方法代表不同的逻辑,其中,创建数据库的方法代码如下:
private void createDBbyLitePal() {
btn_db_create.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG,"创建数据库成功");
LitePal.getDatabase();
}
});
}
仅仅通过点击按钮,调用LitePal.getDatabase();
这句api,就可以创建出数据库,让我们实际进入项目中尝试一下吧!点击该按钮,然后查看控制台,如图所示:
出现该句日记,说明数据库创建成功,接下来我们看看这个数据库是否按照我们所设置好的格式创建出来了,进入data/data/你的项目包名/databases,即可查看到该数据库已经放置到该目录下,如图所示:
事实上,若想对现有数据库进行升级,也是可以实现的。以前我们使用SQLiteOpenHelper来升级数据库的方式,虽说功能是实现了,但你有没有发现一个问题,,就是升级数据库的时候我们需要先把之前的表drop掉,然后再重新创建才行。这其实是一个非常严重的问题,因为这样会造成数据丢失,每当升级一次数据库,之前表中的数据就全没了。
而使用Litepal,就可以很好地避免这个问题。假设我们现在有一张新的表Category要加进去,同样编写它的实体类,代码如下:
package com.androidframelearn.dao_litapal;
public class Category {
private int id;
private String categoryName;
private int categoryCode;
public int getId(){
return id;
}
public void setId(int id){
this.id = id;
}
public String getCategoryName(){
return categoryName;
}
public void setCategoryName(String categoryName){
this.categoryName = categoryName;
}
public int getCategoryCode(){
return categoryCode;
}
public void setCategoryCode(int categoryCode){
this.categoryCode = categoryCode;
}
}
改完了所有我们想改的东西,只需要记得在litepal.xml将版本号加1就行了。当然由于这里还添加了一个新的模型类,因此也需要将它添加到映射模型列表中。修改litepal.xml中的代码,如下所示:
<litepal>
<dbname value="androidframelearn"/>
<version value="2"/>
<list>
<mapping class="com.androidframelearn.dao_litapal.Book"/>
<mapping class="com.androidframelearn.dao_litapal.Category"/>
list>
litepal>
重新运行一下程序,再次创建数据库,就可以完美地完成数据库的升级了。这里的调试可以使用sqlite工具,这里不再赘述。
在讲述本节时,首先回顾一下之前添加数据的方法,我们需要创建出一个Contentvalues对象,然后将所有要添加的数据put到这个Contentvalues对象当中,最后再调用SQLiteDatabase的insert() 方法将数据添加到数据库表当中,步骤相当繁琐。
而使用LitePal来添加数据,这些操作可以简单到让你惊叹!我们只需要创建出模型类的实例,再将所有要存储的数据设置好,最后调用一下save()方法就可以了。
同样地,修改MainActivity,增加插入数据的事件方法,代码如下:
private void insertDatabyLitePal() {
btn_db_insert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Book book = new Book();
book.setName("The Da Vinci Code");
book.setauthor("Dan Brown");
book.setPages(454);
book.setPrice(16.96);
book.save();
Log.i(TAG,"插入数据成功");
}
});
}
同样运行程序,查看控制台,如图所示:
当点击查询数据(下一节将介绍该逻辑)时,控制台打印刚刚插入的数据,如图所示:
使用Litepal同样可以很轻易地查询数据,当然了,由于篇幅限制,这里仅仅贴出最简单的查询方式,至于关联查询等稍复杂的查询方式,可以去GItHub上参考Litepal的官方文档进行相关调用即可。
同样地,修改MainActivity,增加查看数据的事件方法,代码如下:
private void queryDatabyLitePal() {
btn_db_query.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
List<Book> books = LitePal.findAll(Book.class);
for (Book book : books){
Log.i(TAG,"查询数据成功");
Log.d("MainActivity","书名是"+book.getName());
Log.d("MainActivity","书的作者是"+book.getAuthor());
Log.d("MainActivity","书的页数是"+book.getPages());
Log.d("MainActivity","书的价格是"+book.getPrice());
}
}
});
}
相关的运行结果上一小节以贴出,这里不再重复。
更新数据要比添加数据稍微复杂一点,因为它的API接口比较多,这里我们只介绍最常用的几种更新方式。
首先,最简单的一种更新方式就是对已存储的对象重新设值,然后重新调用save()方法即可。那么这里我们就要了解一个概念,什么是已存储的对象?
对于LitePal来说,对象是否已存储就是根据调用model.isSaved()
方法的结果来判断的, 返回true就表示已存储,返回false就表示未存储。那么接下来的问题就是,什么情况下会返回true,什么情况下会返回false呢?
实际上只有在两种情况下model.isSave()
方法才会返回true, 一种情况是已经调用过model. save()方法去添加数据了,此时model会被认为是已存储的对象。另一种情况是model对象是通过LitePal提供的查询API查岀来的,由于是从数据库中查到的对象,因此也会被认为是已存储的对象。
由于查询API相对复杂,因此只能先通过第一种情况来进行验证。修改MainActivity中的代码,如下所示:
private void updateDatabyLitePal() {
btn_db_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Book book = new Book();
book.setName("The Lost Symbol");
book.setauthor("Dan Brown");
book.setPages(510);
book.setPrice(19.95); // 第一次设置商品价格
book.save();
book.setPrice(10.99); // 第二次设置商品价格
book.save();
Log.i(TAG,"更新数据成功");
}
});
}
可以看到,我们做了跟插入数据类似的事情,但是我们对数据的价格进行了设置,运行程序,如图所示:
可以看到,除了刚刚插入的数据,还有第二条刚刚更新过后的数据。然而这种更新方式只能对已存储的对象进行操作,限制性比较大,接下来我们学习另外一种更加灵巧的更新方式,可以调用以下api:
book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");
这里仅贴出其中一条api,其他的可以参考官方文档,这里不再赘述。
使用Litepal删除数据的方式主要有两种,第一种比较简单,就是直接调用已存储对象的delete()方法就可以了,对于已存储对象的概念,我们在之前已经学习过了。也就是说,调用过save()方法的对象,或者是通过LitePal提供的查询API查出来的对象,都是可以直接使用delete()方法来删除数据的。这种方式比较简单,我们就不进行代码演示了,下面直接来看另外一种删除数据的方式。
代码如下:
private void deleteDatabyLitePal() {
btn_db_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LitePal.deleteAll(Book.class,"price < ?","15");
Log.i(TAG,"删除成功");
}
});
}
运行程序,删除过后,按照代码逻辑,已经删除掉了所有price小于15的条目,如图所示:
之前阅读了郭霖老师所著《Android第一行代码 第二版》时,所记载的Litepal版本为:
compile 'org.litepal.android:core:1.4.1'
而最新的Litepal版本(Java版本,另有Kotlin版本,导入的依赖稍有不同)为:
implementation 'org.litepal.android:java:3.0.0'
新旧版本的主要区别是一些类名的划分,例如老板本的DataSupport
变成了LitePalSupport
,除此之外,一些api的名称也稍有变动,读者在使用时最好可以参考GitHub上的官方文档,及时更新代码,做到与时俱进。
AFL——Android框架学习