LitePal是郭神(郭霖)在2014年的杰作,三年后在github上有了一个更新,故来学习一番,没想到还挺好用,这里做下笔记。LitePal是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式,并将我们平时开发时最常用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成各种建表、増删改查的操作。并且LitePal很“轻”,jar包只有100k不到,而且近乎零配置,这一点和hibernate这类的框架有很大区别。
1)使用gradle依赖
dependencies {
compile 'org.litepal.android:core:1.5.0'
}
2)导入jar包
①jar包:https://github.com/LitePalFramework/LitePal/raw/master/downloads/litepal-1.5.0.jar
②源码包:https://github.com/LitePalFramework/LitePal/raw/master/downloads/litepal-1.5.0-src.jar
通过上述地址将jar包下载下来后复制到libs目录下即可。
在assets目录下创建一个litepal.xml文件,复制以下代码进去:
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="数据库名" ></dbname>
<span class="hljs-tag"><<span class="hljs-name">version</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"数据库版本号,用于更新数据库"</span> ></span><span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"><<span class="hljs-name">list</span>></span> <span class="hljs-comment"><!--这里是类映射--></span> <span class="hljs-tag"></<span class="hljs-name">list</span>></span>
</litepal>
<manifest>
<application
android:name="org.litepal.LitePalApplication"
...
>
...
</application>
</manifest>
①把 extends Application 换成 extends LitePalApplication.
②在清单文件中使用你自定义的APP,如自定义的APP为MyApplication:
<manifest>
<application
android:name="com.example.MyApplication"
...
>
...
</application>
</manifest>
方案一:
方案二:
public class MyOwnApplication extends AnotherApplication {
<span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">super</span>.onCreate(); LitePal.initialize(<span class="hljs-keyword">this</span>); } ...
}
例子:假设开发的是一款新闻类APP,本地需要一张新闻表记录新闻,因为该框架是ORM框架,即需要一个新闻类News来表示新闻表,通过新闻类创建新闻表,只需要2步:
public class News {
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> id; <span class="hljs-keyword">private</span> String title; <span class="hljs-keyword">private</span> String content; <span class="hljs-keyword">private</span> Date publishDate; <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> commentCount; <span class="hljs-comment">// 自动生成get、set方法 </span> ...
}
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="demo" ></dbname>
<span class="hljs-tag"><<span class="hljs-name">version</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"1"</span> ></span><span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"><<span class="hljs-name">list</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.News"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"></<span class="hljs-name">list</span>></span>
</litepal>
升级表的场景有两种:
① 增加表
② 修改表结构
例子:对上述的新闻类APP增加记录评论的功能,那么就需要多一张评论表,即多一个评论类Comment(这里不考虑表之间的关系,本次升级只是多增加一张表)。
public class Comment {
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> id; <span class="hljs-keyword">private</span> String content; <span class="hljs-comment">// 自动生成get、set方法 </span> ...
}
升级需要将version节点的value值+1。
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="demo" ></dbname>
<span class="hljs-tag"><<span class="hljs-name">version</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"2"</span> ></span><span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"><<span class="hljs-name">list</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.News"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.Comment"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"></<span class="hljs-name">list</span>></span>
</litepal>
例子:假设上述的评论类Comment是上个版本中少了一个表示发布时间的属性publishDate,则在这次要发布的版本中需要加入。
public class Comment {
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> id; <span class="hljs-keyword">private</span> String content; <span class="hljs-keyword">private</span> Date publishDate; <span class="hljs-comment">//多了一个表示发布时间的属性</span> <span class="hljs-comment">// 自动生成get、set方法 </span> ...
}
<litepal> <dbname value="demo" ></dbname>
<span class="hljs-tag"><<span class="hljs-name">version</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"3"</span> ></span><span class="hljs-tag"></<span class="hljs-name">version</span>></span> ...
</litepal>
例子:每篇新闻(类News)都对应着一个简介(类Introduction),一个简介对应着一篇新闻。
①简介类
public class Introduction {
<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> id; <span class="hljs-keyword">private</span> String guide; <span class="hljs-keyword">private</span> String digest; <span class="hljs-comment">// 自动生成get、set方法 </span>
}
②新闻类(持有简介类)
public class News { ... private Introduction introduction; //一篇新闻对应着一个简介
<span class="hljs-comment">// 自动生成get、set方法 </span>
}
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="demo" ></dbname>
<span class="hljs-tag"><<span class="hljs-name">version</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"4"</span> ></span><span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"><<span class="hljs-name">list</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.News"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.Introduction"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"></<span class="hljs-name">list</span>></span>
</litepal>
例子:一篇新闻(类News)有多条评论(类Comment)
①新闻类(少的一方)
public class News { ... private Introduction introduction;
<span class="hljs-keyword">private</span> <span class="hljs-keyword">List</span><Comment> commentList = <span class="hljs-keyword">new</span> ArrayList<Comment>(); <span class="hljs-comment">//一篇新闻对应多条评论 </span> <span class="hljs-comment">// 自动生成get、set方法 </span>
}
②简介类(多的一方)
public class Comment { ... private News news; //一条评论对应一篇新闻
<span class="hljs-comment">// 自动生成get、set方法 </span>
}
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="demo" ></dbname>
<span class="hljs-tag"><<span class="hljs-name">version</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"5"</span> ></span><span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"><<span class="hljs-name">list</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.News"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.Introduction"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.Comment"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"></<span class="hljs-name">list</span>></span>
</litepal>
例子:一篇新闻(类News)对应的多个类别(类Category),如iphone2000发布,可以是手机类、科技类,还可以是头条。
①新闻类
public class News { ... private Introduction introduction;
<span class="hljs-keyword">private</span> <span class="hljs-keyword">List</span><Comment> commentList = <span class="hljs-keyword">new</span> ArrayList<Comment>(); <span class="hljs-keyword">private</span> <span class="hljs-keyword">List</span><Category> categoryList = <span class="hljs-keyword">new</span> ArrayList<Category>(); <span class="hljs-comment">//一篇新闻对应多种类别</span> <span class="hljs-comment">// 自动生成get、set方法 </span>
}
②类别类
public class Category { ... private List<News> newsList = new ArrayList<News>(); //一种类别对应多篇新闻
<span class="hljs-comment">// 自动生成get、set方法 </span>
}
<?xml version="1.0" encoding="utf-8"?> <litepal> <dbname value="demo" ></dbname>
<span class="hljs-tag"><<span class="hljs-name">version</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"6"</span> ></span><span class="hljs-tag"></<span class="hljs-name">version</span>></span> <span class="hljs-tag"><<span class="hljs-name">list</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.News"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.Comment"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.Introduction"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"><<span class="hljs-name">mapping</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"com.example.databasetest.model.Category"</span>></span><span class="hljs-tag"></<span class="hljs-name">mapping</span>></span> <span class="hljs-tag"></<span class="hljs-name">list</span>></span>
</litepal>
因为LitePal可以通过一个普通的实体类把对应的表创建出来或者对表升级,所以没有要求继承指定的类或实现指定的接口。但是,如果要对表进行存储操作,则就有一个要求了,那就是这些实体类必须继承DataSupport。
public class News extends DataSupport{
...... <span class="hljs-comment">// 自动生成get、set方法 </span>
}
例子:对一篇新闻进行保存
News news = new News();
news.setTitle("这是一条新闻标题");
news.setContent("这是一条新闻内容");
news.setPublishDate(new Date());
news.save();
常规判断数据是否保存成功的方式如下:
if (news.save()) {
Toast.makeText(context, "存储成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "存储失败", Toast.LENGTH_SHORT).show();
}
例子:对一篇新闻进行保存
News news = new News();
news.setTitle("这是一条新闻标题");
news.setContent("这是一条新闻内容");
news.setPublishDate(new Date());
news.saveThrows();
List<News> newsList;
...
DataSupport.saveAll(newsList);
Comment comment1 = new Comment();
comment1.setContent("好评!");
comment1.setPublishDate(new Date());
comment1.save(); //保存了第一条评论
Comment comment2 = new Comment();
comment2.setContent("赞一个");
comment2.setPublishDate(new Date());
comment2.save(); //保存了第二条评论
News news = new News();
news.getCommentList().add(comment1);
news.getCommentList().add(comment2); //把前两条保存过的评论添加到News对象的评论集合中
news.setTitle("第二条新闻标题");
news.setContent("第二条新闻内容");
news.setPublishDate(new Date());
news.setCommentCount(news.getCommentList().size());
news.save(); //保存News对象
News news = new News(); news.setTitle("这是一条新闻标题"); news.setContent("这是一条新闻内容"); news.setPublishDate(new Date()); news.saveAsync().listen(new SaveCallback() { @Override public void onFinish(boolean success) {
}
});
当调用save()方法或saveThrows()方法存储成功之后,LitePal会自动将该条数据对应的id赋值到实体类的id字段上。
以下做个实验:
News news = new News();
news.setTitle("这是一条新闻标题");
news.setContent("这是一条新闻内容");
news.setPublishDate(new Date());
Log.d("TAG", "news id is " + news.getId());
news.save();
Log.d("TAG", "news id is " + news.getId());
打印结果如下:
news id is 0
news id is 1
使用LitePal的修改和删除操作分别有两种方式:
*tip:方式①中必须使用ContentValues类,而方式②不用。
DataSupport.update(实体类.class,ContentValues,id值)
ContentValues values = new ContentValues();
values.put("title", "今日iPhone6发布");
DataSupport.update(News.class, values, 2);
obj.update(id值)
News updateNews = new News(); //News必须继承DataSupport
updateNews.setTitle("今日iPhone6发布");
updateNews.update(2);
DataSupport.updateAll(实体类.class,ContentValues,条件组)
ContentValues values = new ContentValues();
values.put("title", "今日iPhone6 Plus发布");
DataSupport.updateAll(News.class, values, "title = ? and commentcount > ?", "今日iPhone6发布", "0");
obj.updateAll(条件组)
News updateNews = new News(); //News必须继承DataSupport
updateNews.setTitle("今日iPhone6发布");
updateNews.updateAll("title = ? and commentcount > ?", "今日iPhone6发布", "0");
DataSupport.updateAll(实体类.class,ContentValues)
例子:把news表中所有新闻的标题都改成“今日iPhone6发布”。
ContentValues values = new ContentValues();
values.put("title", "今日iPhone6 Plus发布");
DataSupport.updateAll(News.class, values);
obj.setToDefault("字段名")
该方法是针对继承了DataSupport的实体类来说的!!!
News updateNews = new News(); //News必须继承DataSupport
updateNews.setToDefault("commentCount");
updateNews.updateAll();
*删除操作的有几个地方需要注意:
DataSupport.delete(实体类.class,id值)
DataSupport.delete(News.class, 2);
obj.delete(id值)
这里new出了一个News对象,这个对象明显是没有持久化的,此时调用delete()方法则不会删除任何数据。
News news = new News();
news.delete();
如果我们之前将这个对象持久化过了,那么调用delete()方法就会把这个对象对应的数据删除掉了。
News news = new News();
news.setTitle("这是一条新闻标题");
news.setContent("这是一条新闻内容");
news.save();
...
news.delete();
可以使用DataSupport中的isSaved()方法,判断对象是否持久化。
News news;
...
if (news.isSaved()) {
news.delete();
}
DataSupport.deleteAll(实体类.class,条件组)
DataSupport.deleteAll(News.class, "title = ? and commentcount = ?", "今日iPhone6发布", "0");
DataSupport.deleteAll(实体类.class)
DataSupport.deleteAll(News.class);
①查询指定id数据
News news = DataSupport.find(News.class, 1);
②查询第一条数据
News firstNews = DataSupport.findFirst(News.class);
③查询最后一条数据
News lastNews = DataSupport.findLast(News.class);
①查询指定多个id数据
//方式一:
List<News> newsList = DataSupport.findAll(News.class, 1, 3, 5, 7);
//方式二:
long[] ids = new long[] { 1, 3, 5, 7 };
List<News> newsList = DataSupport.findAll(News.class, ids);
②查询表中所有数据
List<News> allNews = DataSupport.findAll(News.class);
例子:查询 新闻表 中 评论数大于0 的 第11到20条 数据,以 发布时间倒序 排序方式呈现,只取 “标题”和“内容” 两列内容。
一般的sql语句如下:
select title,content from news where commentcount > 0 order by publishdate desc limit 10,10;
使用LitePal的连缀查询,代码如下:
List<News> newsList = DataSupport.select("title", "content")
.where("commentcount > ?", "0")
.order("publishdate desc")
.offset(10)
.limit(10)
.find(News.class);
*tip:其中select、where、order、offset、limit可以任意放置位置,可以根据自己的需求任意删减,但find不能去掉。
LitePal的默认查询是懒查询,即:只能查询单表中的数据,关联表中的数据无法查到。
如果需要将关联表中的数据一起查出(激进查询),可以将find()方法的最后一个参数(boolean isEager)设置为true即可,如:
News news = DataSupport.find(News.class, 1, true);
List<Comment> commentList = news.getCommentList();
激进查询不被LitePal推荐,推荐使用懒查询,如果要使用懒查询,可以对实体类做如下修改,以做到关联表查询:
public class News extends DataSupport{
...
public List<Comment> getComments() {
return DataSupport.where("news_id = ?", String.valueOf(id)).find(Comment.class);
}
}
*tip:只有该getComments()方法被调用的时候,才会去查询关联数据。这种写法会比激进查询更加高效也更加合理。
DataSupport.findBySQL(条件组);
Cursor cursor = DataSupport.findBySQL("select * from news where commentcount>?", "0");
LitePal中一共提供了count()、sum()、average()、max()和min()这五种聚合函数
count()方法主要是用于统计行数的
DataSupport.count(实体类.class)
例子:统计news表中一共有多少行
int result = DataSupport.count(News.class);
//使用连缀查询,增加查询条件
int result = DataSupport.where("commentcount = ?", "0").count(News.class);
sum()方法主要是用于对结果进行求合的
DataSupport.sum(实体类.class, "要求合的字段", 结果类型)
例子:统计news表中评论的总数量
int result = DataSupport.sum(News.class, "commentcount", int.class); // 也可以是float、double
*tip:如果要求合的字段是非法的,如String,则返回结果是0。
average()方法主要是用于统计平均数的
DataSupport.average(实体类.class, "要求平均数的字段")
例子:统计news表中平均每条新闻有多少评论
double result = DataSupport.average(News.class, "commentcount"); //默认返回值就是double,因为求平均数大多时候都会有小数,
//使用double可以最大程序保留小数位的精度。
*tip:如果要求平均数的字段是非法的,如String,则返回结果是0。
max()方法主要用于求出某个列中最大的数值
DataSupport.max(实体类.class, "要求最大值的字段", 结果类型)
例子:news表中所有新闻里面最高的评论数是多少
int result = DataSupport.max(News.class, "commentcount", int.class);
min()方法主要用于求出某个列中最小的数值
DataSupport.min(实体类.class, "要求最小值的字段", 结果类型)
例子:news表中所有新闻里面最少的评论数是多少
int result = DataSupport.min(News.class, "commentcount", int.class);
使用注解@Column可以对字段进行约束,共有4种如下约束:
@Column(unique = true) //是否唯一
@Column(defaultValue = "unknown") //指定字段默认值
@Column(nullable = false) //是否可以为空
@Column(ignore = true) //是否可以忽略
例子:一张新闻表,主键唯一,标题不能为空,内容有默认值。
public class News extends DataSupport {
<span class="hljs-meta">@Column</span>(unique = <span class="hljs-keyword">true</span>) <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> id; <span class="hljs-meta">@Column</span>(nullable = <span class="hljs-keyword">false</span>) <span class="hljs-keyword">private</span> String title; <span class="hljs-meta">@Column</span>(defaultValue = <span class="hljs-string">"我是内容"</span>) <span class="hljs-keyword">private</span> String content; <span class="hljs-keyword">private</span> Date publishDate; <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> commentCount; <span class="hljs-comment">// 自动生成get、set方法 </span> ...
}
如果项目中用到了多个数据库,LitePal也可以支持,在程序运行的时候可以创建多个数据库。:
LitePalDB litePalDB = new LitePalDB("db2", 1); //创建一个名为"db2"的第二个数据库
litePalDB.addClassName(Singer.class.getName()); //将实体类Singer等与db2进行关联
litePalDB.addClassName(Album.class.getName());
litePalDB.addClassName(Song.class.getName());
LitePal.use(litePalDB); //使用db2数据库
结构跟旧数据库一样的意思是:litepal.xml配置一样。
LitePalDB litePalDB = LitePalDB.fromDefault("新数据库名");
LitePal.use(litePalDB);
LitePal.useDefault();
LitePal.deleteDatabase("数据库名");
</div>
</div>