LitePal是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式,将开发最常用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成建表、増删改查等操作。
我们的编程语言使用的是面向对象语言,数据库用的是关系型数据库,将面向对象语言和关系型数据库建立的一种映射关系称为对象关系映射。
每一张表,都有一个对应的JavaBean类,比如要创建一张news表,就需要去创建一个News { } 类。
(1) 添加依赖: (使用的最新2.0.0版本)
//LitePal数据库框架
compile 'org.litepal.android:core:2.0.0'
(2) 新建assets目录,然后创建 litepal.xml资源文件.
用于设定所有的映射模型,
注意:如果使用了
Android6.0之后的系统,不但要在AndroidManifest中添加权限,还要在应用运行的时候动态申请。下面是动态申请SD卡读写的权限
(1) 在AndroidManifest中添加SD卡读写的权限
(2) Activity中动态申请权限
// Storage Permissions SD卡权限
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
"android.permission.READ_EXTERNAL_STORAGE",
"android.permission.WRITE_EXTERNAL_STORAGE" };
public static void verifyStoragePermissions(Activity activity) {
try {
//检测是否有写的权限
int permission = ActivityCompat.checkSelfPermission(activity,
"android.permission.WRITE_EXTERNAL_STORAGE");
if (permission != PackageManager.PERMISSION_GRANTED) {
// 没有写的权限,去申请写的权限,会弹出对话框
ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
把verifyStoragePermissions方法放在onCreate方法中即可。
第一:如果自己项目中没有写BaseApplication这种基类的话,就直接在清单文件中配置 LitePalApplication,代码如下:
...
第二:如果自己的项目中定义了自己的 BaseApplication,那么就直接在BaseApplication中的onCreate()方法中初始化下 LitePal就ok。
...
public class BaseApplication extends Application
{
@Override
public void onCreate() {
super.onCreate();
// 初始化LitePal数据库
LitePal.initialize(this);
}
...
}
配置完成,开始进行创建表,增删改查等操作。
根据对象关系映射模式的理念,想要建一张news表,就应该有一个对应的News模型类。即新建一个News类。如下:
package com.example.cindy.intent.Data;
public class News {
}
表中的每一列其实就是对应了模型类中的一个字段,比如news表中有id、title、content、publishdate、commentcount这几个列,那么在News类中就也应该有这几个字段,代码如下所示:
package com.example.cindy.intent.Data;
import java.util.Date;
public class News {
private int id;
private String title;
private String content;
private Date publishDate;
private int commentCount;
// Alt+Insert 自动生成get、set方法
其中id这个字段可写可不写,因为即使不写这个字段,LitePal也会在表中自动生成一个id列,毕竟每张表都一定要有主键的。
现在模型类已经建好了,我们还差最后一步,就是将它配置到映射列表当中。编辑assets目录下的litepal.xml文件,在标签中加入News模型类的声明:
现在只要对数据库有任何的操作,news表就会被自动创建出来 。比如调用下面获取SQLiteDatabase实例的代码,news表就应该已经创建成功了。
private void initDb() {
SQLiteDatabase db = Connector.getDatabase();
}
//在onCreate方法中调用initDb即可
在手机下载SQLite 编辑器(数据库查看软件) ,在文件mnt/sdcard/intentdb/database目录下就能看到创建好的数据库intentdb.db数据库了,点开数据库能看到新创建的news表,表里有id,commentcount,content,publishdate,title字段。到此创建表功能完成。
不需要去编写任何与升级相关的逻辑,也不需要关心程序是从哪个版本升级过来的,唯一要做的就是确定好最新的Model结构是什么样的,然后将litepal.xml中的版本号加1(新增删除修改了表或者表中新增删除修改了字段,版本号要加1),所有的升级逻辑就都会自动完成了。
假如现在我们需要创建一张comment表,该怎么办呢,首先,先创建一个Comment类。
public class Comment {
private int id;
private String content;
private Date publishDate;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getPublishDate() {
return publishDate;
}
public void setPublishDate(Date publishDate) {
this.publishDate = publishDate;
}
}
接着修改litepal.xml中的配置,在映射列表中新增Cooment类,并将版本号加1,如下所示:
升级操作完成,现在只需要操作一下数据库,comment表就会自动生成了 。可以在手机SD中查看。
LitePal进行表管理操作时不需要这些实体类有任何的继承结构,但是LitePal要求所有的实体模型都继承LitePalSupport(以前版本是DataSupport)才能具备CRUD的操作,所以修改News类的代码:
import org.litepal.crud.LitePalSupport;
import java.util.Date;
public class News extends LitePalSupport {
private int id;
private String title;
private String content;
private Date publishDate;
private int commentCount;
// Alt+Insert 自动生成get、set方法
...
}
增加一条数据到news表:
只需要创建模型数据,调用sava()方法。save()方法还是有返回值的,我们可以根据返回值来判断存储是否成功。
//存储一条数据到news表当中
private void addNewsData(){
News news = new News();
news.setTitle("这是一条新闻标题");
news.setContent("这是一条新闻内容");
news.setPublishDate(new Date());
news.save();
//save()方法是有返回值的,返回值为boolel类型,可以根据返回值来判断存储是否成功
if (news.save()) {
Toast.makeText(this, "存储成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "存储失败", Toast.LENGTH_SHORT).show();
}
}
如果想存储失败的话抛出异常,而不是返回一个false,那就可以使用saveThrows()方法来代替,如下所示:
//存储一条数据到news表当中
private void addNewsData(){
News news = new News();
news.setTitle("这是一条新闻标题");
news.setContent("这是一条新闻内容");
news.setPublishDate(new Date());
news.saveThrows();
使用saveThrows()方法来存储数据,一旦存储失败就会抛出一个DataSupportException异常,我们可以通过对这个异常进行捕获来处理存储失败的情况。
注意:模型中的id是不需要赋值的,当调用save()方法或saveThrows()方法存储成功之后,LitePal会自动将该条数据对应的id赋值到实体类的id字段上。
存储News集合:(批量存储)
List newsList;
...
LitePal.saveAll(newsList);
saveAll()方法接收一个Collection集合参数,只要把待存储的集合数据传入即可。
修改的方法定义在LitePalSupport中。方法定义如下:
public static int update(Class> modelClass, ContentValues values, long id)
这个静态的update()方法接收三个参数,第一个参数是Class,传入我们要修改的那个类的Class就好,第二个参数是ContentValues对象,这三个参数是一个指定的id,表示我们要修改哪一行数据。
例如:修改id为2的数据
private void update(){
ContentValues contentValues = new ContentValues();
contentValues.put("title","修改过的标题");
LitePal.update(News.class,contentValues,2);
}
修改符合条件的所有数据,方法定义如下:
public static int updateAll(Class> modelClass, ContentValues values, String... conditions)
updateAll()方法表示修改多行记录,其中第一个参数仍然是Class,第二个参数还是ContentValues对象,第三个参数是一个conditions数组,用于指定修改哪些行的约束条件,返回值表示此次修改影响了多少行数据。例:
private void update(){
ContentValues contentValues = new ContentValues();
contentValues.put("title","修改过的标题");
// LitePal.update(News.class,contentValues,2);
LitePal.updateAll(News.class, contentValues, "title = ? and content= ?", "这是一条新闻标题","这是一条新闻内容");
}
最后的这个conditions数组,由于它的类型是一个String数组,我们可以在这里填入任意多个String参数,其中最前面一个String参数用于指定约束条件,后面所有的String参数用于填充约束条件中的占位符(即?号),比如约束条件中有一个占位符,那么后面就应该填写一个参数,如果有两个占位符,后面就应该填写两个参数,以此类推。
方法定义:
public static int delete(Class> modelClass, long id)
private void delete() {
LitePal.delete(News.class, 1);
}
这不仅仅会把id 为1的记录删除,同时还有将其他关联的外键数据都删除。
除了删除指定id的数据之外,DataSupport中也提供了一个通过where语句来批量删除数据的方法,先看一下方法定义:
public static int deleteAll(Class> modelClass, String... conditions)
例如:删除表中title为“这是一条新闻”,commentcount为0的数据。
private void delete() {
// LitePal.delete(News.class, 1);
LitePal.deleteAll(News.class, "title = ? and commentcount = ?", "这是一条新闻标题", "0");
}
删除表中所有数据:
LitePal.deleteAll(News.class);
查询news表中id为1的这条记录:
private void find(){
LitePal.find(News.class, 1);
}
查询表中第一条数据:
News firstNews = LitePal.findFirst(News.class);
查询news表中id为1、3、5、7的数据
List newsList = LitePal.findAll(News.class, 1, 3, 5, 7);
连缀查询:
List newsList = LitePal.select("title", "content")
.where("commentcount > ?", "0")
.order("publishdate desc").limit(10).offset(10)
.find(News.class);
title和content这两列数据,评论数大于零的新闻,将查询出的新闻按照发布的时间倒序排列,查询出前10条数据,对新闻进行分页展示,翻到第二页时,展示第11到第20条新闻
查询所有数据:
List allNews = LitePal.findAll(News.class);
LitePal中一共提供了count()、sum()、average()、max()和min()这五种聚合函数,基本上已经将SQL语句当中最常用的几种聚合函数都覆盖了。
count(): count()方法主要是用于统计行数的
int result = LitePal.count(News.class);
统计一共有多少条新闻是零评论:
int result = LitePal.where("commentcount = ?", "0").count(News.class);
sum() :sum()方法主要是用于对结果进行求合的
统计news表中评论的总数量:
int result = LitePal.sum(News.class, "commentcount", int.class);
第一个参数是传入的Class,用于指定去统计哪张表当中的数据。第二个参数是列名,表示希望对哪一个列中的数据进行求合。第三个参数用于指定结果的类型,这里指定成int型,因此返回结果也是int型。
首先我们在Model中添加一个byte[]类型的字段:
public class News extends DataSupport {
private String name;
private double price;
private byte[] image;
// generated getters and setters.
...
}
存储图片:
//存储一条数据到news表当中
private void addNewsData(){
byte[] imageBytes = getImageBytesFromSomewhere();
News news = new News();
news.setTitle("这是一条新闻标题");
news.setContent("这是一条新闻内容");
news.setPublishDate(new Date());
news.setImage(imageBytes); //存储图片
news.save();
查询图片:查询方式没有改变,自动就可以把当前对象的二进制数据一起给查出来。
private void find(){
LitePal.find(News.class, 1);
}
但是如果有时候想只查一些基本数据呢?毕竟把二进制数据一起查出来了肯定会影响效率的,这种情况我们可以借助LitePal的级联查询来解决:
News news = LitePal.select("title", "content").where("id = ?", 1).find(News.class);
这种写法指定只查询id为1的title和content这两列,image这一列数据是不会被查询出来的,因此就完全不会影响效率了。
比如我们组装了一个名叫小明的Person对象,然后需求是,如果表中已经存在小明这个人了,就更新他的数据,如果不存在,就将小明的数据插入到表中。
saveOrUpdate()方法,专门用来处理这种不存在就存储,已存在就更新的需求。
Person p = new Person();
p.setName("小明");
p.setAge(16);
p.saveOrUpdate("name=?", p.getName());
调用saveOrUpdate()方法后,LitePal内部会自动判断,如果表中已经存在小明这条记录了,就会自动更新,如果不存在的话,就会自动插入。