【Android】ORM数据库框架之GreenDao快速入门与使用

先润润嗓子,听我细细道来:

今天我们来介绍一款性能非常好的orm数据库框架GreenDao,相信大家一定对它有所耳闻,或者已经在项目中在使用它了,虽然我在去年就开始使用这款框架,但是一直没有做过系统的整理和梳理,有些地方也是含糊不清,今天就和大家一起来揭开GreenDao的神秘面纱。

1.为什么要使用greendao?

下面是官网对greendao以及其他常见的orm数据库的性能测试图:

通过这张图我们可以看出其性能远远超出其他同类型的数据库框架,greendao执行效率很高,且引入库文件较小,占用更少的内存 ,操作实体灵活,它可以帮助Android开发者快速将Java对象映射到SQLite数据库中,通过使用一个简单的面向对象API,开发者可以对Java对象进行存储、更新、删除和查询。

2.集成greendao

下面我以eclipse开发工具为例讲解,至于android studio一句话即可搞定:

compile ‘org.greenrobot:greendao:2.2.0‘
  • 值得注意的是:【greendao在使用的使用需要两个工程,一个是java工程,负责生成需要的实体类和数据库管理类,一个是android工程,执行具体的数据库操作,后面我们会分别介绍】

一、android工程

新建android工程,指定自己的包名,此处我的包名为com.greendao.use,在com.greendao.use路径下新建package,com.greendao.use.db.localdb用于存放生成的数据库文件,此处的包名大家可以随意指定,但是在java工程中需要指定对应的包路径,方便引入的时候不会产生包名错误;

二、java工程

首先我们新建java工程,在工程根目录新建libs目录,把需要的包复制到libs下,这两个包在文章结尾的源码里会有,需要的可以去下载:

此处我们准备在数据库中添加一个student表,那么现在我们新建一个java类,并添加如下方法:

/**
     * <学生表>
     */
    private static void addStudent(Schema schema)
    {
        // 设置表名,此处的对应android工程中的实体类名称
        Entity student = schema.addEntity("Student");
        // 设置表名,此处的对应数据库中的表名
        student.setTableName("t_student");
        // 设置主键为默认增长的id,也可以使用primaryKey指定任意属性为主键
        student.addIdProperty().primaryKey().autoincrement();
        // 学号,notNull指定为非空
        student.addStringProperty("stuNum").columnName("stu_num").notNull();
        // 名字
        student.addStringProperty("name").columnName("name");
        // 性别
        student.addStringProperty("sex").columnName("sex");
        // 年龄
        student.addStringProperty("age").columnName("age");
        // 学分score
        student.addStringProperty("score").columnName("score");
        // school名称
        student.addStringProperty("school").columnName("school");
    }

然后我们在java类的main方法中添加如下代码:

public static void main(String[] args)
        throws IOException, Exception
    {
        Schema schema = new Schema(1, "com.greendao.use.db.localdb");
        addStudent(schema);
        new DaoGenerator().generateAll(schema, "./blog");
    }

注意:此处的com.greendao.use.db.localdb就是我android工程中用来存放数据库映射实体类的目录!blog是我在java工程中新建的一个用于存放生成实体类的目录:

此时我们运行java类,然后右键工程,Refresh一下工程,在blog目录下就会生成对应的数据库实体类文件:

—–Student.java类就是对应t_student表的实体类文件,

核心类介绍

DaoMaster:

daomaster以一定的模式持有数据库对象(SQLiteDatabase)并管理一些DAO类(而不是对象)。

有一个静态的方法创建和drop数据库表。它的内部类OpenHelper和DevOpenHelper是SQLiteOpenHelper的实现类,用于创建SQLite数据库的模式。

DaoSession:

管理指定模式下所有可用的DAO对象,你可以通过某个get方法获取到。DaoSession提供一些通用的持久化方法,比如对实体进行插入,加载,更新,刷新和删除。最后DaoSession对象会跟踪identity scope,更多细节,可以参看 session文档。

DAOs(Data access objects):

数据访问对象,用于实体的持久化和查询。对于每一个实体,greenDao会生成一个DAO,相对于DaoSession它拥有更多持久化的方法。

当然在DaoMaster类中还有一个比较重要的内部类DevOpenHelper,代码如下:

/** WARNING: Drops all table on Upgrade! Use only during development. */
    public static class DevOpenHelper extends OpenHelper {
        public DevOpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
            dropAllTables(db, true);
            onCreate(db);
        }
    }

咦,onUpgrade方法,不是处理数据库升级的嘛,是的,此类就是处理数据库升级的类,但是注意看上面的警告 WARNING: Drops all table on Upgrade! Use only during development.意思就是如果使用默认生成的代码进行greedDao的升级,会删除所有的表之后重新创建,是无法保留老版本数据库中的数据的,所以实际情况下我们需要自己处理升级逻辑;

下面我们把生成的代码复制到android工程下

此处我们简单封装了一个数据库操作类DBController,方便进行数据库操作,代码都很简单,主要是获取DaoSession实例,大家也可以根据自己项目的实际需求封装一些常用的增删查改的方法,DBController.java类代码如下:

/**
 * 数据库控制类
 */
public class DBController
{
    private static DaoMaster daoMasterEcmc;

    private static DaoSession daoSessionEcmc;

    /**
     * 数据库名称:localdata
     */
    public static final String DATABASE_NAME = "localdata.db";

    private static DaoMaster obtainMaster(Context context, String dbName)
    {
        return new DaoMaster(new DaoMaster.DevOpenHelper(context, dbName, null).getWritableDatabase());
    }

    private static DaoMaster getDaoMaster(Context context, String dbName)
    {
        if (dbName == null)
            return null;
        if (daoMasterEcmc == null)
        {
            daoMasterEcmc = obtainMaster(context, dbName);
        }
        return daoMasterEcmc;
    }

    /**
     * 取得DaoSession
     *
     * @return
     */
    public static DaoSession getDaoSession(String dbName)
    {

        if (daoSessionEcmc == null)
        {
            daoSessionEcmc = getDaoMaster(MainApplication.getIns(), dbName).newSession();
        }
        return daoSessionEcmc;
    }

    /**
     * 默认操作localdata数据库
     */
    public static DaoSession getDaoSession()
    {

        if (daoSessionEcmc == null)
        {
            daoSessionEcmc = getDaoMaster(MainApplication.getIns(), DATABASE_NAME).newSession();
        }
        return daoSessionEcmc;
    }
}
  • 在Android项目中使用

在libs下添加需要greendao库文件:

下面我们在MainActivity的onCreate中获取StudentDao实例,一句话:

StudentDao studentDao = DBController.getDaoSession().getStudentDao();

然后我们先不进行任何操作,直接运行,然后去DDMS中查看我们的数据库有没有生成成功,路径为data/data/包名/databases下:

我们使用sqllitemanager工具打开localdata.db文件,看看咱们的t_student表有没有创建成功,可以看到表创建成功:

对应的属性:

至此,我们的数据库创建阶段已经完成,下面我们将学习一下基本的增删查改的使用,请各位备好纸巾!

3.增删查改样样行

  • 插入操作
/**
     * <插入数据>
     *
     * @see [类、类#方法、类#成员]
     */
    private void addData()
    {
        StudentDao studentDao = DBController.getDaoSession().getStudentDao();
        //新建一个Student对象就相当于一条数据库的记录
        Student student = new Student();
        student.setStuNum("1001");
        student.setName("刘德华");
        student.setAge("22");
        student.setSex("男");
        student.setScore("3");
        student.setSchool("清华大学");
        //插入到数据库
        studentDao.insert(student);

        Student student1 = new Student();
        student1.setStuNum("1002");
        student1.setName("刘亦菲");
        student1.setAge("26");
        student1.setSex("女");
        student1.setScore("5");
        student1.setSchool("北京大学");
        // 开启事务插入
        studentDao.insertInTx(student1);

        Student student2 = new Student();
        student2.setStuNum("1003");
        student2.setName("黄渤");
        student2.setAge("30");
        student2.setSex("男");
        student2.setScore("12");
        student2.setSchool("南京大学");
        // 插入,如果存在覆盖之前的记录
        studentDao.insertOrReplace(student2);
    }

打开db文件看下有么有插入成功:

OK,成功!

至于插入操作,greendao还提供了其他的插入方法,比如批量插入等,大家可以自己去尝试使用下,以便做出更好的选择:

  • 查询操作

1.查询所有数据库所有记录:

StudentDao studentDao = DBController.getDaoSession().getStudentDao();
//list()方法查询所有记录
List<Student> students = studentDao.queryBuilder().list();

2.查询指定条件(此处为性别为男的记录)的记录:

StudentDao studentDao = DBController.getDaoSession().getStudentDao();
        List<Student> students = studentDao.queryBuilder().where(StudentDao.Properties.Sex.eq("男")).list();

3.使用sql语句进行查询:

StudentDao studentDao = DBController.getDaoSession().getStudentDao();
List<Student> students = studentDao.queryRaw("where name = ?", "刘德华");

我们再插入两条数据:

4.limit用法是取出查询记录的N条:

//查询前四条limit(4)
StudentDao studentDao = DBController.getDaoSession().getStudentDao();
List<Student> students = studentDao.queryBuilder().limit(4).list();

4.offset是设置偏移量,下面是从第二个开始,查询三条数据:

StudentDao studentDao = DBController.getDaoSession().getStudentDao();
List<Student> students = studentDao.queryBuilder().limit(3).offset(1).list();        

5.orderAsc是对查询的数据进行升序排序,orderDesc是降序排序,此处根据年龄进行升序排序:

StudentDao studentDao = DBController.getDaoSession().getStudentDao();
List<Student> students = studentDao.queryBuilder().orderAsc(StudentDao.Properties.Age).list();

关于greendao的查询功能,除了上述查询之外,还有一些其他的功能,大家可以自行尝试:

  • 修改操作
//把刘德华修改成周星驰,如果有多个,此处只修改第一个
List<Student> students = studentDao.queryBuilder().where(StudentDao.Properties.Name.eq("刘德华")).list();
Student entity = students.get(0);
entity.setName("周星驰");
studentDao.update(entity);

其他的更新方法:

  • 删除操作
//删除周星驰的第一条记录
StudentDao studentDao = DBController.getDaoSession().getStudentDao();
QueryBuilder<Student> builder = studentDao.queryBuilder().where(StudentDao.Properties.Name.eq("周星驰"));
List<Student> students = builder.list();
studentDao.delete(students.get(0));

其他的删除方法:

4.GreenDao数据库升级

升级没有什么好说的,就像前面讲到的那样,现在描述一下基本流程,比如我们添加个表或者修改某些字段,之后需要提高数据库版本,当执行程序的时候一旦发现新版本大于老版本,就会执行数据库更新操作,执行onUpgrade方法,我们的就在此处处理升级逻辑;

比如如果我们要给t_stduent表添加个字段的话,就可以这样来写:

public void onUpgrade(SQLiteDatabase db) {
    db.execSQL("ALTER TABLE ‘t_student‘ ADD favorite TEXT");
 }

运行查看数据表:

添加一张t_school表,我们可以这样写:

db.execSQL("CREATE TABLE ‘t_school‘ (‘_id‘ INTEGER PRIMARY KEY AUTOINCREMENT ,‘name‘ TEXT,‘address‘ TEXT)");

查看数据库,添加成功:

虽然这样可以添加成功,别忘记使用java工程生成对应的实体类和Dao类等数据库操作类,不然是无法操作数据表的;写到这,大家说,麻烦死了,还得写sql语句,复杂的话又比较容易出问题,所以下面推荐一种好的方式来进行更新(代码来自:Pedro Okawa):

首先创建一个MyMigrationHelper数据库更新的工具类,具体代码如下:

/**
 *
 * please call {@link #migrate(SQLiteDatabase, Class[])}
 *
 */
public final class MyMigrationHelper {

    private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN‘T MATCH WITH THE CURRENT PARAMETERS";
    private static MyMigrationHelper instance;

    public static MyMigrationHelper getInstance() {
        if(instance == null) {
            instance = new MyMigrationHelper();
        }
        return instance;
    }

    public void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        //创建临时表保存数据
        generateTempTables(db, daoClasses);
        //删除所有
        DaoMaster.dropAllTables(db, true);
        //创建所有
        DaoMaster.createAllTables(db, false);
        //恢复数据,删除临时表
        restoreData(db, daoClasses);
    }

    private void generateTempTables(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for(int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);

            String divider = "";
            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");
            ArrayList<String> properties = new ArrayList<>();

            StringBuilder createTableStringBuilder = new StringBuilder();

            createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");

            for(int j = 0; j < daoConfig.properties.length; j++) {
                String columnName = daoConfig.properties[j].columnName;

                if(getColumns(db, tableName).contains(columnName)) {
                    properties.add(columnName);

                    String type = null;

                    try {
                        type = getTypeByClass(daoConfig.properties[j].type);
                    } catch (Exception exception) {
//                        Crashlytics.logException(exception);
                    }

                    createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);

                    if(daoConfig.properties[j].primaryKey) {
                        createTableStringBuilder.append(" PRIMARY KEY");
                    }

                    divider = ",";
                }
            }
            createTableStringBuilder.append(");");

            db.execSQL(createTableStringBuilder.toString());

            StringBuilder insertTableStringBuilder = new StringBuilder();

            insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append(" (");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(" FROM ").append(tableName).append(";");

            db.execSQL(insertTableStringBuilder.toString());
        }
    }

    private void restoreData(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for(int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);

            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");
            ArrayList<String> properties = new ArrayList();

            for (int j = 0; j < daoConfig.properties.length; j++) {
                String columnName = daoConfig.properties[j].columnName;

                if(getColumns(db, tempTableName).contains(columnName)) {
                    properties.add(columnName);
                }
            }

            StringBuilder insertTableStringBuilder = new StringBuilder();

            insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");

            StringBuilder dropTableStringBuilder = new StringBuilder();

            dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);

            db.execSQL(insertTableStringBuilder.toString());
            db.execSQL(dropTableStringBuilder.toString());
        }
    }

    private String getTypeByClass(Class<?> type) throws Exception {
        if(type.equals(String.class)) {
            return "TEXT";
        }
        if(type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class)) {
            return "INTEGER";
        }
        if(type.equals(Boolean.class)) {
            return "BOOLEAN";
        }

        Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));
        // Crashlytics.logException(exception);
        throw exception;
    }

    private static List<String> getColumns(SQLiteDatabase db, String tableName) {
        List<String> columns = new ArrayList<>();
        Cursor cursor = null;
        try {
            cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);
            if (cursor != null) {
                columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));
            }
        } catch (Exception e) {
            Log.v(tableName, e.getMessage(), e);
            e.printStackTrace();
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return columns;
    }

}

使用时我们只需要在onUpgrade方法中调用migrate方法指定数据库中所有的Dao-class即可:

@Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //注意:class只需要添加已经存在的表,新增的表不需要添加
           MyMigrationHelper.getInstance().migrate(db,StudentDao.class,SchoolDao.class);
        }

我们现在添加一个t_teacher表,提高数据库版本,把生成的代码复制到android 工程,运行工程,执行后就会发现数据库里t_teacher表已经添加成功了。

而且比较重要的是原来stduent表中的信息依然被保留了下来:

只需简单的一句话即可完成数据库的升级操作;

总结:至此,关于greenDao的基本使用到这里已经完成了,本篇博文主要是记录了数据库的基本使用 ,都是对单表的一些基本操作,如果你仔细看了这篇博文,相信会对你有所帮助,如果你有任何问题,欢迎下方评论讨论,关于数据表的关联关系等操作,下篇会重点介绍;

源码下载:博文工程源码,需要jar包的也可以下载

时间: 2024-10-11 17:51:46

【Android】ORM数据库框架之GreenDao快速入门与使用的相关文章

【Android】ORM数据库框架之GreenDao【关联】关系操作

上一篇我们介绍了greendao的基本使用方法,如果您还不了解,建议先看下上篇文章再来看这篇会有更好的效果.今天我们来继续学习greendao的relation部分,即数据表的关联操作部分,greendao默认支持一对一,一对多的关系操作,多对多目前暂不支持,下面我们来一步步实现greendao的关联操作: 1.一对一关系 假设我们现在只允许一夫一妻制,好像现在就是啊,哈哈哈哈哈,那么一个男人可以娶一个老婆,一个老婆也只能有一个男人,下面我们分别创建man表和women表,来演示一对一的关联操作

Android ORM数据库之GreenDao使用教程及源码分析

一.简介 1.Android ORM介绍 ?在平时的开发过程中,大家一定会或多或少地接触到 SQLite.然而在使用它时,我们往往需要做许多额外的工作,像编写 SQL 语句与解析查询结果等.所以,适用于 Android 的ORM 框架也就孕育而生了,现在市面上主流的框架有 OrmLite.SugarORM.Active Android.Realm 与 GreenDAO.下面先介绍下当前流行的5种ORM数据库框架: 1)OrmLite ?OrmLite不是Android 平台专用的ORM框架,它是

微服务的入门级微框架Spring Boot快速入门

详情请交流  QQ  709639943 00.微服务的入门级微框架Spring Boot快速入门 00.基于java的微信公众号二次开发视频教程 00.leetcode 算法 面试 00.北风网 零基础到数据(大数据)分析专家-首席分析师 00.快速上手JMeter 00.Jmeter 00.2017年Java web开发工程师成长之路 00.R语言速成实战 00.R语言数据分析实战 00.Python+Django+Ansible Playbook自动化运维项目实战 00.Java深入微服务

实体框架(Entity Framework)快速入门--实例篇

在上一篇 <实体框架(Entity Framework)快速入门> 中我们简单了解的EF的定义和大体的情况,我们通过一步一步的做一个简单的实际例子来让大家对EF使用有个简单印象,看操作步骤 第一步:创建控制台项目 这个就不多说了,如果新建项目你还不知道,那先去学学基础吧. 第二步:创建实体模型 在项目上右击 添加新建项目→Ado .Net 实体数据模型 如下图所示: 第三步:与现有的 数据库 进行连接生成EF实体 在做这步之前,首先确定你是否已经有现有数据库,当然在这提供我自己的数据库脚本.

CTK框架——CTK Widgets快速入门

CTK框架--CTK Widgets快速入门 一.CTK Widgets模块简介 1.CTK Widgets模块简介 CTK Widgets模块是CTK封装的用于通用功能以及生物医学成像专用领域的Qt组件库.CTK中所有部分都有大量的测试相关代码,在源码目录下Libs/xxx/Testin/Cpp目录中.CTK官方文档:http://www.commontk.org/docs/html/modules.htmlImageGallery演示了CTK Widgets模块大部分组件的效果:http:/

android高效ORM数据库框架greenDao使用

     因为项目中多处用到了数据库,需要对数据库频繁的读写操作,虽然android 自带的SQLiteOpenHelper的.这种方式比较方便易懂,但是在使用过程中需要写很多的sql语句,而且需要及时的关闭和释放资源,使用起来相对复杂,鉴于性能的考虑,决定采用一种ORM框架来解决,     在android中开源的数据库ORM解决方案中.主要有那么几种.综合各方面评价,觉得greenDao相对来说是一个综合考量最好的方案,所以决定试一下,     但是greenDao使用相关资料网上确实不多,

JAVAWEB开发之Struts2详解(一)——Struts2框架介绍与快速入门、流程分析与工具配置以及Struts2的配置以及Action和Result的详细使用

Struts2框架介绍 三大框架:是企业主流JavaEE开发的一套架构.Struts2 + Spring + Hibernate 什么是框架?为什么要学习框架? 框架是实现部分功能的代码(半成品),使用框架简化企业级软件开发. Struts2与MVC? Struts是一款优秀的MVC框架 MVC:是一种思想,是一种模式,将软件分为Model模型.View视图.Controller控制器 JAVAEE软件三层架构:web层(表现层).业务逻辑层.数据持久层(Sun提供javaEE开发规范) Jav

玩转Android之数据库框架ActiveAndroid的使用

ActiveAndroid是一个开源的数据库框架,使我们在Android中使用数据库变得更为简单,今天我们就来看看这个数据库框架的使用. 1.引入ActiveAndroid 首先创建我们自己的项目,在我们的项目中引入ActiveAndroid,引入ActiveAndroid需要我们修改两个地方,一个全局gradle文件,还有一个是局部gradle文件,修改方式如下: 全局gradle文件,在jcenter()下方添加如下代码: mavenCentral() maven { url "https:

Spring MVC入门第1天--框架说明与快速入门

文档版本 开发工具 测试平台 工程名字 日期 作者 备注 V1.0 2016.06.29 lutianfei none springmvc框架 springmvc业务流程框架 springmvc框架组件说明 Spring MVC入门程序 配置前端控制器 配置处理器适配器 开发Handler 视图编写 配置Handler 配置处理器映射器 配置视图解析器 部署调试 非注解的处理器映射器 和 适配器 非注解的处理器映射器 非注解的处理器适配器 springmvc的默认加载 注解的处理器映射器和适配器