您的位置:首页 > 数据库

Litepal (数据库框架) 学习笔记

2017-04-09 17:33 766 查看
作者: 夏至 欢迎转载,也请保留这段申明,谢谢。

http://blog.csdn.net/u011418943/article/details/69853971

现在越来越多的项目要用到数据库,但是Android 内置的 sqlite 相对于不熟悉数据库的人来说,每次的增删查改都是一次痛苦的经历;

最近也用到数据库比较多;但是以我抠脚得数据库知识,每次调试都要想很久;相信很多人跟我一样,于是面向的数据库框架越来越多,比较牛逼的算是 GreemDao,但是比较难用;对于处理小数据的数据库,推荐使用 郭神的 Litepal框架;简单实用把。

下面是简单认识把。

1、配置:

compile 'org.litepal.android:core:1.5.0'


现在已经是1.5版本了,支持异步操作,听说下个版本会支持加密,我只想说,郭神,你真的逆天成神了吗!!

2、然后在assert 新建一个litepal.xml文件,配置如下:

<?xml version="1.0" encoding="utf-8"?>
<litepal>
<dbname value="demo" ></dbname>
<version value="1" ></version>
<list>
<mapping class="com.toptech.downloadmanager.entity.TaskInfo"/>
</list>
</litepal>


上面的list下的,是你想导入数据库的实体类。

3、新建类,我的 TaskInfo

public class TaskInfo {
private String filename;
private String fileurl;
private int fileprogress;
private float filesize;
private float filelength;
.....


4、单例 LitePal

a、在application下:

<application
android:name="org.litepal.LitePalApplication"


b、或者自己的aplication

public class MyApplication extends LitePalApplication


5、获取实例

SQLiteDatabase db = Connector.getDatabase();


这样就可以了,应用运行一下,就可以看到创建表格好了:



6、数据库的 增删查改

a、增加一个数据

首先,先让你的实体类继承 Datasupport

public class TaskInfo extends DataSupport{


然后,这样:

TaskInfo taskInfo = new TaskInfo("baidu","www.test.com",0,0,0);
taskInfo.save();


其中,save会返回 boolean 值,true表示成功,false表示失败;但是这样的失败我们不知道哪里出错了,可以使用

taskInfo.saveThrows();


这样就可以跑出异常了。你可能对它的save源码有新股,源码如下:

public synchronized boolean save() {
try {
saveThrows();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}


恩,也加了锁的,所以多线程访问也没啥问题的。打印如下:

b、更新数据

更新就用 update 了。不过跟上面的有点不一样,首先看一下源码:

public static synchronized int update(Class<?> modelClass, ContentValues values, long id) {
UpdateHandler updateHandler = new UpdateHandler(Connector.getDatabase());
return updateHandler.onUpdate(modelClass, id, values);
}


我们上面的打印中,有个中文乱码的,那我们就把它该一下:

ContentValues values = new ContentValues();
values.put("filename","google");
values.put("fileurl","www.google.com");
DataSupport.update(TaskInfo.class,values,1);


打印如下:



可以看到,我们修改成功了。

不过,相信你也看到了,这种通过 id 来修改数据的,有时候约束性很大;比如像我上面 Taskinfo,我下载了很多任务,然后我想保存其中某一个任务的进度,那这样我并不知道id,如果取修改呢?

litepal 这里也提供了一个 updateall的函数:

public synchronized int updateAll(String... conditions) {
try {
UpdateHandler updateHandler = new UpdateHandler(Connector.getDatabase());
int rowsAffected = updateHandler.onUpdateAll(this, conditions);
getFieldsToSetToDefault().clear();
return rowsAffected;
} catch (Exception e) {
throw new DataSupportException(e.getMessage(), e);
}
}


可以看到,源码中,它是查找表中所有关联的限制符,然后修改所有的数据;虽说我们的 TaskInfo 每一条下载链接都是特殊的,但用这个函数名感觉就是乖乖的。那有没有其他函数替代呢?

在新版的1.5中,有个 saveorupdate 函数,就可以解决我们上面的问题了:

public synchronized boolean saveOrUpdate(String... conditions) {
if (conditions == null) {
return save();
}
List<DataSupport> list = (List<DataSupport>) where(conditions).find(getClass());
if (list.isEmpty()) {
return save();
} else {
SQLiteDatabase db = Connector.getDatabase();
db.beginTransaction();
try {
for (DataSupport dataSupport : list) {
baseObjId = dataSupport.getBaseObjId();
SaveHandler saveHandler = new SaveHandler(db);
saveHandler.onSave(this);
clearAssociatedData();
}
db.setTransactionSuccessful();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
db.endTransaction();
}
}


可以看到,它的意思是,没有就保存,有就更新。那么,我们觉得百度的下载进度要更新成50,应该怎么写呢?

TaskInfo taskInfo = new TaskInfo("baidu","www.test.com",0,0,0);
taskInfo.setFilename("baidu"); //为什么加上这一句,是为了保证,如果"baidu"不存在,则保存数据
//存在了,那么这句话相当于没用了。下面检测到name,则更新
taskInfo.setFileprogress(50);
taskInfo.saveOrUpdate("filename = ?",taskInfo.getFilename());


效果如下:



那,你可能会说,这样限制条件太少了,容易不准确,我想根据 name 和 url 来实现更新?怎么做?这个简单啊,可以看到上面的参数是String….,完全可以多个限制符,当然你的限制符必须满足 sqlite 的语法:

TaskInfo taskInfo = new TaskInfo("baidu","www.test.com",0,0,0);
taskInfo.setFilename("baidu"); //为什么加上这一句,是为了保证,如果"baidu"不存在,则保存数据
//存在了,那么这句话相当于没用了。下面检测到name,则更新
taskInfo.setFileprogress(80);
taskInfo.saveOrUpdate("filename = ? and fileurl = ?",taskInfo.getFilename(),taskInfo.getFileurl());


可以看一下:



c、删除

说道我们的删除了,同样,基本的 delete 函数如下:


public static synchronized int delete(Class<?> modelClass, long id) {
int rowsAffected = 0;
SQLiteDatabase db = Connector.getDatabase();
db.beginTransaction();
try {
DeleteHandler deleteHandler = new DeleteHandler(db);
rowsAffected = deleteHandler.onDelete(modelClass, id);
db.setTransactionSuccessful();
return rowsAffected;
} finally {
db.endTransaction();
}
}


不过,也是通过 id 来实现删除效果的。如果我们想把百度的数据给删了,那么只要:

DataSupport.delete(TaskInfo.class,2);


查一下数据库:



但,我们已经说过了,这个通过 id 来删除的,并不是很好,那有没有像上面的 saveOrUpdate 这种函数呢?很遗憾,并没有;不过我们可以使用 deleteAll 这个函数:

public static synchronized int deleteAll(Class<?> modelClass, String... conditions) {
DeleteHandler deleteHandler = new DeleteHandler(Connector.getDatabase());
return deleteHandler.onDeleteAll(modelClass, conditions);
}


所以,我们应该这么写:

DataSupport.deleteAll(TaskInfo.class,"fileurl = ? and filename = ?","www.google.com","google");


数据如下



郭霖哥哥竟然没适配条件符删除的函数,真的奇怪;用 deleteAll 怪怪的;恩,是时候去留言一波了。

d、查询

相比传统的 sqlite 查询,litepal的查询简直叼得不要不要的。先看 find 函数:


public static synchronized <T> T find(Class<T> modelClass, long id) {
return find(modelClass, id, false);
}


所以,我们可以这样写:

TaskInfo taskInfo = DataSupport.find(TaskInfo.class,3);
Log.d(TAG, "onCreate: "+taskInfo);


打印:

onCreate: TaskInfo{filename='baidu', fileurl='www.test.com', fileprogress=0, filesize=0.0, filelength=0.0}


直接封装成实体类啊,简直炸天;

在查询中,我们最常用的有 查找第一条数据和最好一条数据,这里litepal都提供了;所以,我们可以很方便的查找:

TaskInfo taskInfo = DataSupport.findFirst(TaskInfo.class);
Log.d(TAG, "onCreate: "+taskInfo);
taskInfo = DataSupport.findLast(TaskInfo.class);
Log.d(TAG, "onCreate: "+taskInfo);


打印如下:



用 findAll(TaskInfo.class) 则查找所有的数据



但是,你也可以看到,前面我们对其中两个数据删除,新增加的两个两个,它们的 id 从3那里重新自增了,这样就是说,用 id 来查询是非常不可取的;

不过我们可以用连缀查询来查看我们的数据;

比如,我们把 filesize = 0 的都 提取出来

List<TaskInfo> taskInfos = DataSupport.where("filesize = ?","0").find(TaskInfo.class);


打印如下:



当然,我们可以限制查询信息,比如,我只要看名字:

List<TaskInfo> taskInfos = DataSupport.select("filename").where("filesize = ?","0")
.find(TaskInfo.class);
for (int i = 0; i < taskInfos.size(); i++) {
Log.d(TAG, "onCreate: "+taskInfos.get(i));
}


打印如下:



可以看到跟上面的对比,打印只有名字了,其他的都恢复成默认值;

有时候,我们只要查询这个数据库是否存在而已,只要返回 true 或者 false,但LitePal 并没有相关的接口,但我们可以通过自己写一个:

private boolean isTaskInfoExsits(String url){

List<TaskInfo> taskInfos = DataSupport.where("fileurl = ?",url).find(TaskInfo.class);
if (taskInfos.isEmpty()){
return  false;
}else{
return true;
}
}


这里通过 url 为限制符,当然你也可以多个条件去检测

更多查询资料,直接去郭神的博客看吧http://blog.csdn.net/guolin_blog/article/details/40153833

7、聚合函数

什么叫聚合函数呢?聚合函数是数据库常见的一些操作数据操作集合,比如累加 sum,大小count,平均数等等;比如我想知道,我想知道数据库中共有多少个数据;那么这个时候,我们就可以用count函数来获取;这里用LitePal也是非常简单,就是一行代码的事情;

int count = DataSupport.count(Person.class);
Log.d(TAG, "onCreate: "+count);


好吧,多了打印一行;打印如下:



8、扩展,获取数据库自增长id

可以看到,上面很多函数都是基于id来的,其实也不能说id不好,因为考虑到如果有数据删除,id是从删除之后又自增上来的,比如你本来有两个数据,删掉之后,id是从3 开始的;这样如果你认为只有一条数据,然后查询时,输入1,那肯定是获取不到的。

不过LitePal 在查询id上比较好;就是我们可以在我们的工具类中,加一个 id 这个字段;必须 long 型,因为源码就是转换成long型的;然后在复制的时候,不要给id赋值,其实赋值了也没啥关系;因为 LitePal 会自动给这个 id 字段赋值;这样,我们就可以用id来作为标识了,当然这个id还是见仁见智;只是提供一个方法;

比如我随便新建一个 person 类,只有id和name两个属性;

Person p1 = new Person();
p1.setId(3);
p1.setName("shaorui");
p1.save();
Person p2 = new Person();
p2.setId(4);
p2.setName("ruishao");
p2.save();
List<Person> persons = DataSupport.findAll(Person.class);
Log.d(TAG, "onCreate: "+persons);


效果如下:



可以看到,就算我给id赋值了,但是LitePal还是会把自增的id添加个 Person 类的 id;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据库 litepal