美文网首页Android
[转]Android数据库框架 - LitePal学习笔记

[转]Android数据库框架 - LitePal学习笔记

作者: 努力深耕Android的小透明 | 来源:发表于2018-05-17 16:01 被阅读27次

    简述

    LitePal是郭神(郭霖)在2014年的杰作,三年后在github上有了一个更新,故来学习一番,没想到还挺好用,这里做下笔记。LitePal是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式,并将我们平时开发时最常用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成各种建表、増删改查的操作。并且LitePal很“轻”,jar包只有100k不到,而且近乎零配置,这一点和hibernate这类的框架有很大区别。

    github:https://github.com/LitePalFramework/LitePal

    一、LitePal的基本用法

    1. 引入Jar包或依赖

    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目录下即可。

    2. 配置litepal.xml

    在assets目录下创建一个litepal.xml文件,复制以下代码进去:

    <?xml version="1.0" encoding="utf-8"?>  
    <litepal>  
        <dbname value="数据库名" ></dbname>  
    
        <version value="数据库版本号,用于更新数据库" ></version>  
    
        <list>
            <!--这里是类映射-->  
        </list>  
    </litepal>  
    
    

    3. 配置LitePalApplication

    1)如果你的项目中没有自定义APP,则直接在清单文件中进行如下配置:

    <manifest>  
        <application  
            android:name="org.litepal.LitePalApplication"  
            ...  
        >  
        ...  
        </application>  
    </manifest>  
    
    

    2)如果你项目中有继承Application的自定义APP:

    ①把 extends Application 换成 extends LitePalApplication.

    ②在清单文件中使用你自定义的APP,如自定义的APP为MyApplication:

    <manifest>  
        <application  
            android:name="com.example.MyApplication"  
            ...  
        >  
        ...  
        </application>  
    </manifest> 
    
    

    3)如果你项目中的自定义APP是继承了一个jar包中的第三方Application(即:源码修改不了):

    方案一:

    1. 你可以把LitePal的源码下载下来,然后把src目录下的所有代码直接拷贝到你项目的src目录下面
    2. 接着打开LitePalApplication类,将它的继承结构改成继承自第三方Application
    3. 再让MyApplication继承自LitePalApplication,这样所有的Application就都可以在一起正常工作了。

    方案二:

    public class MyOwnApplication extends AnotherApplication {
    
        @Override
        public void onCreate() {
            super.onCreate();
            LitePal.initialize(this);
        }
        ...
    }
    
    

    二、使用LitePal建表

    例子:假设开发的是一款新闻类APP,本地需要一张新闻表记录新闻,因为该框架是ORM框架,即需要一个新闻类News来表示新闻表,通过新闻类创建新闻表,只需要2步:

    1、创建一个实体类

    public class News {  
    
        private int id;  
    
        private String title;  
    
        private String content;  
    
        private Date publishDate;  
    
        private int commentCount;  
    
        // 自动生成get、set方法  
        ...  
    } 
    
    

    2、修改assets目录下的litepal.xml文件

    <?xml version="1.0" encoding="utf-8"?>  
    <litepal>  
        <dbname value="demo" ></dbname>  
    
        <version value="1" ></version>  
    
        <list>  
            <mapping class="com.example.databasetest.model.News"></mapping>  
        </list>  
    </litepal>  
    
    

    三、使用LitePal升级表

    升级表的场景有两种:

    • ① 增加表

    • ② 修改表结构

    1、增加一张表

    例子:对上述的新闻类APP增加记录评论的功能,那么就需要多一张评论表,即多一个评论类Comment(这里不考虑表之间的关系,本次升级只是多增加一张表)。

    1)、根据升级需求创建实体类

    public class Comment {  
    
        private int id;  
    
        private String content;  
    
        // 自动生成get、set方法   
        ...  
    }  
    
    

    2)、修改assets目录下的litepal.xml文件

    升级需要将version节点的value值+1。

    <?xml version="1.0" encoding="utf-8"?>  
    <litepal>  
        <dbname value="demo" ></dbname>  
    
        <version value="2" ></version>  
    
        <list>  
            <mapping class="com.example.databasetest.model.News"></mapping>  
            <mapping class="com.example.databasetest.model.Comment"></mapping>  
        </list>  
    </litepal>  
    
    

    2、修改之前的表结构

    例子:假设上述的评论类Comment是上个版本中少了一个表示发布时间的属性publishDate,则在这次要发布的版本中需要加入。

    1)、根据升级需求修改实体类

    public class Comment {  
    
        private int id;  
    
        private String content;  
    
        private Date publishDate;  //多了一个表示发布时间的属性
    
        // 自动生成get、set方法   
        ...  
    }  
    
    

    2)、修改assets目录下的litepal.xml文件

    <litepal>  
        <dbname value="demo" ></dbname>  
    
        <version value="3" ></version>  
        ...  
    </litepal>  
    
    

    四、使用LitePal建立表关联

    1、一对一:

    例子:每篇新闻(类News)都对应着一个简介(类Introduction),一个简介对应着一篇新闻。

    1)、在一个类中持有另一方的引用

    ①简介类

    public class Introduction {  
    
        private int id;  
    
        private String guide;  
    
        private String digest;  
    
        // 自动生成get、set方法  
    }  
    
    

    ②新闻类(持有简介类)

    public class News {  
        ...  
        private Introduction introduction;  //一篇新闻对应着一个简介
    
        // 自动生成get、set方法  
    }  
    
    

    2)、修改litepal.xml中的版本号,加入新表对就实体类的引用,更新数据库。

    <?xml version="1.0" encoding="utf-8"?>  
    <litepal>  
        <dbname value="demo" ></dbname>  
    
        <version value="4" ></version>  
    
        <list>  
            <mapping class="com.example.databasetest.model.News"></mapping>  
            <mapping class="com.example.databasetest.model.Introduction"></mapping>  
        </list>  
    </litepal>  
    
    

    2、多对一:

    例子:一篇新闻(类News)有多条评论(类Comment)

    1)、在少的一方持有多的一方的集合,在多的一方持有少的一方的引用

    ①新闻类(少的一方)

    public class News {  
        ...  
        private Introduction introduction;  
    
        private List<Comment> commentList = new ArrayList<Comment>(); //一篇新闻对应多条评论 
    
        // 自动生成get、set方法  
    }  
    
    

    ②简介类(多的一方)

    public class Comment {  
        ...  
        private News news;  //一条评论对应一篇新闻
    
        // 自动生成get、set方法   
    }  
    
    

    2)、修改litepal.xml中的版本号,更新数据库。

    <?xml version="1.0" encoding="utf-8"?>  
    <litepal>  
        <dbname value="demo" ></dbname>  
    
        <version value="5" ></version>  
    
        <list>  
            <mapping class="com.example.databasetest.model.News"></mapping> 
            <mapping class="com.example.databasetest.model.Introduction"></mapping>  
            <mapping class="com.example.databasetest.model.Comment"></mapping>  
        </list>  
    </litepal>  
    
    

    3、多对多:

    例子:一篇新闻(类News)对应的多个类别(类Category),如iphone2000发布,可以是手机类、科技类,还可以是头条。

    1)、彼此持有对方的集合

    ①新闻类

    public class News {  
        ...  
        private Introduction introduction;  
    
        private List<Comment> commentList = new ArrayList<Comment>();  
    
        private List<Category> categoryList = new ArrayList<Category>();  //一篇新闻对应多种类别
    
        // 自动生成get、set方法  
    } 
    
    

    ②类别类

    public class Category {  
        ...  
        private List<News> newsList = new ArrayList<News>();  //一种类别对应多篇新闻
    
        // 自动生成get、set方法  
    } 
    
    

    2)、修改litepal.xml中的版本号,更新数据库。

    <?xml version="1.0" encoding="utf-8"?>  
    <litepal>  
        <dbname value="demo" ></dbname>  
    
        <version value="6" ></version>  
    
        <list>  
            <mapping class="com.example.databasetest.model.News"></mapping>  
            <mapping class="com.example.databasetest.model.Comment"></mapping>  
            <mapping class="com.example.databasetest.model.Introduction"></mapping>  
            <mapping class="com.example.databasetest.model.Category"></mapping>  
        </list>  
    </litepal> 
    
    

    五、使用LitePal的存储操作(增)

    因为LitePal可以通过一个普通的实体类把对应的表创建出来或者对表升级,所以没有要求继承指定的类或实现指定的接口。但是,如果要对表进行存储操作,则就有一个要求了,那就是这些实体类必须继承DataSupport。

    1、让实体类继承DataSupport

    public class News extends DataSupport{  
    
        ......  
    
        // 自动生成get、set方法  
    }  
    
    

    2、使用save()对单个实体数据进行保存

    例子:对一篇新闻进行保存

    News news = new News();  
    news.setTitle("这是一条新闻标题");  
    news.setContent("这是一条新闻内容");  
    news.setPublishDate(new Date());  
    news.save();  
    
    
    • tip:save()是不会抛出异常的,且返回的是boolean。

    常规判断数据是否保存成功的方式如下:

    if (news.save()) {  
        Toast.makeText(context, "存储成功", Toast.LENGTH_SHORT).show();  
    } else {  
        Toast.makeText(context, "存储失败", Toast.LENGTH_SHORT).show();  
    } 
    
    

    3、使用saveThrows()对单个实体数据进行保存

    例子:对一篇新闻进行保存

    News news = new News();  
    news.setTitle("这是一条新闻标题");  
    news.setContent("这是一条新闻内容");  
    news.setPublishDate(new Date());  
    news.saveThrows();  
    
    
    • tip:saveThrows()是当保存失败时会抛出异常的,所以,可以通过对这个异常进行捕获来处理存储失败的情况。

    3、使用DataSupport.saveAll()对多个实体数据进行保存

    List<News> newsList;  
    ...  
    DataSupport.saveAll(newsList);  
    
    

    4、多对一的关系的数据保存

    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对象
    
    

    5、使用saveAsync()对单个数据进行异步保存

    News news = new News();  
    news.setTitle("这是一条新闻标题");  
    news.setContent("这是一条新闻内容");  
    news.setPublishDate(new Date()); 
    news.saveAsync().listen(new SaveCallback() {
        @Override
        public void onFinish(boolean success) {
    
        }
    });
    
    

    6、当数据保存成功时,对象中的id会被赋值

    当调用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的修改和删除操作(删改)

    使用LitePal的修改和删除操作分别有两种方式:

    • ①使用DataSupport类中的静态方法(即实体类不需要继承DataSupport)。
    • ②使用继承了DataSupport的实体类中的方法。

    *tip:方式①中必须使用ContentValues类,而方式②不用。

    1、修改操作

    1)修改指定id的数据

    ①使用DataSupport类中的update()静态方法:

    DataSupport.update(实体类.class,ContentValues,id值)

    ContentValues values = new ContentValues();  
    values.put("title", "今日iPhone6发布");  
    DataSupport.update(News.class, values, 2);  
    
    
    ②使用继承了DataSupport的实体类中的方法:

    obj.update(id值)

    News updateNews = new News(); //News必须继承DataSupport
    updateNews.setTitle("今日iPhone6发布");  
    updateNews.update(2);  
    
    

    2)修改指定条件的数据

    ①使用DataSupport类中的updateAll()静态方法:

    DataSupport.updateAll(实体类.class,ContentValues,条件组)

    ContentValues values = new ContentValues();  
    values.put("title", "今日iPhone6 Plus发布");  
    DataSupport.updateAll(News.class, values, "title = ? and commentcount > ?", "今日iPhone6发布", "0");  
    
    
    ②使用继承了DataSupport的实体类中的方法:

    obj.updateAll(条件组)

    News updateNews = new News();  //News必须继承DataSupport
    updateNews.setTitle("今日iPhone6发布");  
    updateNews.updateAll("title = ? and commentcount > ?", "今日iPhone6发布", "0"); 
    
    

    3)修改全表数据

    DataSupport.updateAll(实体类.class,ContentValues)

    例子:把news表中所有新闻的标题都改成“今日iPhone6发布”。

    ContentValues values = new ContentValues();  
    values.put("title", "今日iPhone6 Plus发布");  
    DataSupport.updateAll(News.class, values);  
    
    

    4)将对应字段修改为默认值

    obj.setToDefault("字段名")

    该方法是针对继承了DataSupport的实体类来说的!!!

    News updateNews = new News();  //News必须继承DataSupport
    updateNews.setToDefault("commentCount");  
    updateNews.updateAll();  
    
    

    2、删除操作

    *删除操作的有几个地方需要注意:

    • 只能删除持久化过的数据,所谓持久化过的数据,通俗的说就是保存过的数据,也就是保存或查询出来的数据才能被删除。
    • 如果删除的数据id是另一张表中的外键,则在删除该数据时,别一张表中的关联数据也会被删除。
    • 继承了DataSupport的实体类没有deleteAll方法,所以只有一种删除指定条件的数据方式(即:DataSupport.deleteAll())。
    • 最后切记,多条件时只能用单词and连接条件,不能用符号&!!!

    1)删除指定id的数据

    ①使用DataSupport类中的delete()静态方法:

    DataSupport.delete(实体类.class,id值)

    DataSupport.delete(News.class, 2);  
    
    
    ②使用继承了DataSupport的实体类中的方法:

    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();  
    }  
    
    

    2)删除指定条件的数据

    DataSupport.deleteAll(实体类.class,条件组)

    DataSupport.deleteAll(News.class, "title = ? and commentcount = ?", "今日iPhone6发布", "0");
    
    

    3)删除全表数据

    DataSupport.deleteAll(实体类.class)

    DataSupport.deleteAll(News.class);  
    
    

    七、使用LitePal的查询(查)

    1、简单查询

    1)查询单条数据

    ①查询指定id数据

    News news = DataSupport.find(News.class, 1);
    
    

    ②查询第一条数据

    News firstNews = DataSupport.findFirst(News.class);
    
    

    ③查询最后一条数据

    News lastNews = DataSupport.findLast(News.class);
    
    

    2)查询多条数据

    ①查询指定多个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);    
    
    

    2、连缀查询

    例子:查询 新闻表评论数大于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不能去掉。

    3、激进查询

    LitePal的默认查询是懒查询,即:只能查询单表中的数据,关联表中的数据无法查到。

    1)如何使用激进查询

    如果需要将关联表中的数据一起查出(激进查询),可以将find()方法的最后一个参数(boolean isEager)设置为true即可,如:

    News news = DataSupport.find(News.class, 1, true);  
    List<Comment> commentList = news.getCommentList();  
    
    

    2)激进查询的缺点:

    • 一旦关联表中的数据很多,查询速度可能就会非常慢。
    • 激进查询只能查询出指定表的关联表数据,但是没法继续迭代查询关联表的关联表数据。

    3)使用懒查询代替激进查询

    激进查询不被LitePal推荐,推荐使用懒查询,如果要使用懒查询,可以对实体类做如下修改,以做到关联表查询:

    public class News extends DataSupport{      
        ...  
        public List<Comment> getComments() {  
            return DataSupport.where("news_id = ?", String.valueOf(id)).find(Comment.class);  
        }  
    }
    
    

    *tip:只有该getComments()方法被调用的时候,才会去查询关联数据。这种写法会比激进查询更加高效也更加合理。

    4、原生查询

    DataSupport.findBySQL(条件组);

    Cursor cursor = DataSupport.findBySQL("select * from news where commentcount>?", "0");  
    
    

    八、使用LitePal的聚合函数

    LitePal中一共提供了count()、sum()、average()、max()和min()这五种聚合函数

    1、count()

    count()方法主要是用于统计行数的

    DataSupport.count(实体类.class)

    例子:统计news表中一共有多少行

    int result = DataSupport.count(News.class); 
    //使用连缀查询,增加查询条件
    int result = DataSupport.where("commentcount = ?", "0").count(News.class);  
    
    

    2、sum()

    sum()方法主要是用于对结果进行求合的

    DataSupport.sum(实体类.class, "要求合的字段", 结果类型)

    例子:统计news表中评论的总数量

    int result = DataSupport.sum(News.class, "commentcount", int.class); // 也可以是float、double  
    
    

    *tip:如果要求合的字段是非法的,如String,则返回结果是0。

    3、average()

    average()方法主要是用于统计平均数的

    DataSupport.average(实体类.class, "要求平均数的字段")

    例子:统计news表中平均每条新闻有多少评论

    double result = DataSupport.average(News.class, "commentcount");  //默认返回值就是double,因为求平均数大多时候都会有小数,
                                                                    //使用double可以最大程序保留小数位的精度。
    
    

    *tip:如果要求平均数的字段是非法的,如String,则返回结果是0。

    4、max()

    max()方法主要用于求出某个列中最大的数值

    DataSupport.max(实体类.class, "要求最大值的字段", 结果类型)

    例子:news表中所有新闻里面最高的评论数是多少

    int result = DataSupport.max(News.class, "commentcount", int.class);
    
    

    5、min()

    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 {  
    
        @Column(unique = true)
        private int id;  
    
        @Column(nullable = false)
        private String title;  
    
        @Column(defaultValue = "我是内容")
        private String content;  
    
        private Date publishDate;  
    
        private int commentCount;  
    
        // 自动生成get、set方法  
        ...  
    } 
    
    

    十、对多个数据库的支持

    如果项目中用到了多个数据库,LitePal也可以支持,在程序运行的时候可以创建多个数据库。:

    1、创建一个全新的数据库

    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数据库
    
    

    2、创建一个结构跟旧数据库一样的数据库

    结构跟旧数据库一样的意思是:litepal.xml配置一样。

    LitePalDB litePalDB = LitePalDB.fromDefault("新数据库名");
    LitePal.use(litePalDB);
    
    

    3、换回默认数据库(原来旧的数据库)

    LitePal.useDefault();
    
    

    4、删除任意数据库

    LitePal.deleteDatabase("数据库名");
    

    转载文章: Android数据库框架 - LitePal学习笔记

    相关文章

      网友评论

        本文标题:[转]Android数据库框架 - LitePal学习笔记

        本文链接:https://www.haomeiwen.com/subject/zrgbdftx.html