1.LitePal简介
LitePal是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式,并将我们平时开发最常用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成各种建表和增删改查的操作,LitePal的项目主页上也有详细的使用文档,地址是:http://github.com/LitePalFramework/LitePal
2.配置LitePal
要使用LitePal,第一步就是编辑app/build.gradle文件,在dependencies闭包中添加如下内容:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:25.3.1'
testCompile 'junit:junit:4.12'
compile 'org.litepal.android:core:1.3.2'
}
添加的这一行声明中,前面的部分是固定的,最后的1.3.2是版本号的意思,最新的版本号可以到LitePal的项目主页上去查看
这样我们就把LitePal成功引入到当前的项目中了,然后配置litepal.xml文件,右击app/src/main目录–New–Directory,创建一个assets目录,然后在assets目录下再新建一个litepal.xml文件,然后编辑
<litepal>
<dbname value="BookStore" >dbname>
<version value="1" >version>
<list>
list>
litepal>
其中dbname标签用于指定数据库名,version标签用于指定数据库版本号,list标签用于指定所有的映射模型
最后还需要在配置一下LitePalApplication,修改AndroidManifest.xml中的代码
"http://schemas.android.com/apk/res/android"
package="com.example.litepaltest">
"org.litepal.LitePalApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
...
这里将项目的application配置为org.litepal.LitePalApplication,这样才能让LitePal的所有功能都可以正常工作.
现在LitePal的配置工作已经全部结束,下面就是如何使用
3.创建和升级数据库
LitePal采用的是对象关系映射(ORM)的模式,那么什么是关系映射,简单的说,我们使用的编程语言是面向对象语言,而使用的数据库则是关系型数据库,那么将面向对象的语言和面向关系的数据库之间建立一种映射关系,这就是对象关系映射了.
对象关系映射模式,就是可以用面向对象的思维来操作数据库,而不用再和SQL语句打交道,比如,为了创建一张Book表,需要先分析表中应该包含那些列,然后再编写出一条建表语句,最后在自定义SQLiteOpenHelper中执行这条建表语句,但是使用LitePal,你就可以用面向对象的思维来实现同样的功能,定义一个Book类,代码如下:
public class Book {
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="BookStore" >dbname>
<version value="1" >version>
<list>
<mapping class="com.example.litepaltest.Book">mapping>
list>
litepal>
这里使用mapping标签来声明我们要配置的映射模型类,注意一定要使用完整的类名.不管有多少模型类需要映射,都使用同样的方式配置在list标签下
修改MainActivity代码
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button createDatabase = (Button)findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Connector.getDatabase();
}
});
}
}
其中,调用Connector.getDatabase()方法就是一次简单的数据库操作,只要点击一下按钮,数据库就会自动创建完成.
使用SQLiteOpenHelper来升级数据库,有一个问题就是升级数据库的时候我们需要先把之前的表drop掉,然后再重新创建才行,这其实是一个非常严重的问题,因为这样会造成数据丢失,每当升级一次数据库,之前表中的数据就全没了.当使用LitePal时,这些都不再是问题了.使用LitePal来升级数据库非常非常简单,你完全不用考虑任何的逻辑,只需要改你想改的内容,然后版本号加1就可以了
比如我们想要向Book表中添加一个press(出版社)列,直接修改Book类中的代码,添加一个press字段
public class Book {
...
private String press;
...
public String getPress() {
return press;
}
public void setPress(String press) {
this.press = press;
}
}
于此同时,我们还想再添加一张Category表,那么只需要新建一个Category类就可以了
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;
}
}
改完了所有我们想改的东西,只需要记得将版本号加1就行了.这里添加一个新的模型类,因此也需要将它添加到映射模型列表中.修改litepal.xml中的代码
<litepal>
<dbname value="BookStore" >dbname>
<version value="2" >version>
<list>
<mapping class="com.example.litepaltest.Book">mapping>
<mapping class="com.example.litepaltest.Category">mapping>
list>
litepal>
4.使用LitePal添加数据
使用LitePal添加数据,只需要创建出模型类的实例,然后将所有的要存储的数据设置好,最后调用一下save()方法就可以了
模型类必须继承DataSupport类,修改Book类
public class Book extends DataSupport{
...
}
修改MainActivity代码
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
Button addData = (Button)findViewById(R.id.add_data);
addData.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.setPress("Unknow");
book.save();
}
});
}
}
在添加数据按钮的点击事件里面,首先是创建出了一个Book实例.然后调用Book类中的各种set方法对数据进行设置,最后调用book.save()方法就能完成数据添加操作.这个save()方法是从DataSupport类中继承来的.除了save()方法,还有一些其他方法.
5.使用LitePal更新数据
首先,最简单的一种更新方式就是对已存储的对象重新设值,然后 重新调用save()方法.什么是已存储的对象,对于LitePal来说,对象是否已存储就是根据调用model.isSaved()方法的结果来判断的,返回true就表示已存储,返回false就表示未存储.实际上只有两种情况下model.isSaved()方法才会返回true,一种情况是已经调用过model.save()方法去添加数据,此时model会被认为是已存储的对象,另一种情况是model对象是通过LitePal提供的查询API查出来的,由于是从数据库中查到的对象,因此也会被认为是已存储的对象.
第一种情况的代码,修改MainActivity代码
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
Button updateData = (Button)findViewById(R.id.update_data);
updateData.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.setPress("Unknow");
book.save();
book.setPrice("10.99");
book.save();
}
});
}
}
在更新数据按钮的点击事件里面,首先添加一条Book数据,然后调用setPrice()方法将这本书的价格进行修改,之后再调用了save()方法.此时LitePal会发现当前的Book对象是已存储的,因此不会再向数据库中去添加一条新的数据,而是会直接更新当前的数据.
另外一种更新方法,修改MainActivity代码
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
Button updateData = (Button)findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Book book = new Book();
book.setPrice(14.95);
book.setPress("Anchor");
book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");
}
});
}
}
这里首先new一个Book实例,然后直接调用setPrice()方法和setPress()方法来设置要更新的数据,最后再调用updateAll()方法去执行更新操作.
注意:updateAll()方法中可以指定一个约束条件,和SQLiteDatabase中的update()方法的where参数部分有点类似,如果不指定条件约束的话,就表示更新所有数据.
注意:在使用updateAll()方法时,当你想把一个字段的值更新成默认值时,是不可以使用上面的方式来set数据的,在java中任何一种数据类型的字段都有一个默认值,例如int类型的默认值为0,boolean类型的默认值为false,String类型的默认值为null,因此,如果当new出一个Book对象时,其实所有字段都已经被初始化为默认值.比如说pages字段的值为0,因此,想把数据表中的pages更新为0,直接调用book.setPages(0)是不可以的,因为即使不调用这段代码,pages字段本身也是0,LitePal不会对这个列进行更新,对于所有想要将为数据更新成默认值的操作,LitePal提供了一个setToDefault()方法,然后传入相应的列名就可以了实现了.例如:
Book book = new Book();
book.setToDefault("pages);
book.updateAll();
6.使用LitePal删除数据
有两种方式,第一种比较简单,就是直接调用已存储对象的delete()方法就可以了.调用过save()方法的对象,或是通过LitePal提供的查询API查出来的对象,都是可以直接使用delete()方式来删除数据的.
另一种删除方式的代码如下:
修改MainActivity代码:
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
Button deleteButton = (Button)findViewById(R.id.delete_data);
deleteButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
DataSupport.deleteAll(Book.class,"price < ?","15");
}
});
}
}
这里调用DataSupport.deleteAll()方法来删除数据,其中deleteAll()方法的第一个参数用于指定删除哪张表中的数据,Book.class表示删除Book表中的数据,后面的参数用于指定约束条件,这里的代码表示,删除Book表中的价格低于15的书.
deleteAll()方法不指定约束条件,就是要删除表中的所有数据
7.使用LitePal查询数据
LitePal查询表中的所有数据:
List books = DataSuppport.findAll(Book.class);
findAll()方法返回一个Book类型的List集合,我们不用像之前那样通过Cursor对象一行行去取值,LitePal已经自动帮助我们完成了赋值操作.
修改MainActivity代码:
public class MainActivity extends AppCompatActivity{
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
Button queryButton = (Button)findViewById(R.id.query_data);
queryButton.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
List books = DataSuppport.findAll(Book.class);
for(Book book:books){
Log.d("MainActivity", "book name is " + book.getName());
Log.d("MainActivity", "book author is " + book.getAuthor());
Log.d("MainActivity", "book pages is " + book.getPages());
Log.d("MainActivity", "book price is " + book.getPrice());
Log.d("MainActivity", "book press is " + book.getPress());
}
}
});
}
}
查询Book表中的第一条数据可以这样写:
Book firstBook = DataSuppport.findFirst(Book.class);
查询Book表中的最后一条数据可以这样写
Book lastBook = DataSuppport.findLast(Book.class);
我们可以通过连缀查询来定制更多的查询功能
select()方法用于指定查询那几列的数据,对应了SQL当中的select关键字,比如只查询name和author这两列的数据
List books = DataSuppport.select("name", "author").find(Book.class);
where()方法用于指定查询的约束条件,对应了SQL当中的关键字where,比如只查询页数大于400的数据,
List books = DataSuppport.where("pages > ?", "400").find(Book.class);
order()方法用于指定结果的排序方式,对应了SQL当中的order by 关键字,比如将查询结果按照书价格从高到低排序
desc表示降序排列,asc或者不写表示升序排列
List books = DataSuppport.order("price desc").find(Book.class);
limit()方法用于指定查询结果的数量,比如只查询表中的前3条数据
List books = DataSuppport.limit(3).find(Book.class);
offset()方法用于指定查询结果的偏移量,比如查询表中的第2条,第3条,第4条数据
List books = DataSuppport.limit(3).offset(1).find(Book.class);
还可以对这5个方法进行任意组合的连缀,来完成一个比较复杂的查询操作
查询Book表中第11–20条满足页数大于400这个条件的name,author和pages这3列数据,并将查询结果按照页数升序排列
List books = DataSuppport.select("name", "author", "pages")
.where("pages > ?", "400")
.order("pages")
.limit(20)
.offset(10)
.find(Book.class);
LitePal也支持使用原生的SQL来查询:
Cursor c = DataSupport.findBySQL("select * from Book where pages > ? and price < ?", "400", "20");
调用DataSupport.findBySQL()方法来进行原生查询,其中第一个参数用于指定SQL语句,后面的参数用于指定占位符的值,注意findBySQL()方法返回的是一个Cursor对象,需要将数据一一取出才行.