zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

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

2023-09-27 14:28:04 时间

作者: 夏至 欢迎转载,也请保留这段申明,谢谢。

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;