作者: 夏至 欢迎转载,也请保留这段申明,谢谢。
http://blog.csdn.net/u011418943/article/details/69853971
现在越来越多的项目要用到数据库,但是Android 内置的 sqlite 相对于不熟悉数据库的人来说,每次的增删查改都是一次痛苦的经历;
最近也用到数据库比较多;但是以我抠脚得数据库知识,每次调试都要想很久;相信很多人跟我一样,于是面向的数据库框架越来越多,比较牛逼的算是 GreemDao,但是比较难用;对于处理小数据的数据库,推荐使用 郭神的 Litepal框架;简单实用把。
下面是简单认识把。
compile 'org.litepal.android:core:1.5.0'
现在已经是1.5版本了,支持异步操作,听说下个版本会支持加密,我只想说,郭神,你真的逆天成神了吗!!
<litepal>
<dbname value="demo" >dbname>
<version value="1" >version>
<list>
<mapping class="com.toptech.downloadmanager.entity.TaskInfo"/>
list>
litepal>
上面的list下的,是你想导入数据库的实体类。
public class TaskInfo {
private String filename;
private String fileurl;
private int fileprogress;
private float filesize;
private float filelength;
.....
<application
android:name="org.litepal.LitePalApplication"
public class MyApplication extends LitePalApplication
SQLiteDatabase db = Connector.getDatabase();
首先,先让你的实体类继承 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;
}
}
恩,也加了锁的,所以多线程访问也没啥问题的。打印如下:
更新就用 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 list = (List) 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());
说道我们的删除了,同样,基本的 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 怪怪的;恩,是时候去留言一波了。
相比传统的 sqlite 查询,litepal的查询简直叼得不要不要的。先看 find 函数:
public static synchronized T find(Class 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 taskInfos = DataSupport.where("filesize = ?","0").find(TaskInfo.class);
当然,我们可以限制查询信息,比如,我只要看名字:
List 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 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
什么叫聚合函数呢?聚合函数是数据库常见的一些操作数据操作集合,比如累加 sum,大小count,平均数等等;比如我想知道,我想知道数据库中共有多少个数据;那么这个时候,我们就可以用count函数来获取;这里用LitePal也是非常简单,就是一行代码的事情;
int count = DataSupport.count(Person.class);
Log.d(TAG, "onCreate: "+count);
可以看到,上面很多函数都是基于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 persons = DataSupport.findAll(Person.class);
Log.d(TAG, "onCreate: "+persons);
可以看到,就算我给id赋值了,但是LitePal还是会把自增的id添加个 Person 类的 id;