美文网首页Android框架Android开发
GreenDAO系列(一) GreenDAO和Realm的简单使

GreenDAO系列(一) GreenDAO和Realm的简单使

作者: 嘎啦果安卓兽 | 来源:发表于2018-05-14 11:38 被阅读401次

    概述

    最近打算研究一下Android的ORM框架,即对象关系数据映射,ORM框架能很好的帮我们简化数据库操作逻辑,增加开发效率,而且好的ORM还能帮我们增加执行效率。世面上有很多ORM框架,比如OrmLite、SugarORM、GreenDAO、ActiveAndroid、realm等,搜了一些资料进行对比,感觉GreenDAO和Realm这两款还是比较优秀的,所以这里先介绍下GreenDAO和Realm的简单使用。

    GreenDAO

    GreenDAO是基于Android原生数据库框架进行封装的,使我们不用写复杂的SQL语句及很多的重复性语句。GreenDAO对Android进行了高度优化,具有轻量级、高性能、支持加密、支持protobuf、对象激活、代码自动生成等特点。

    • 配置依赖
      引入GreenDAO需要分别在工程的build.gradle和Module的build.gradle进行配置。

    工程的build.gradle:

    buildscript {
        repositories {
            jcenter()
            mavenCentral() // 添加这个仓库
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:3.1.1'
            classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // 添加插件路径
        }
    }
    

    Module的build.gradle:

    apply plugin: 'com.android.application'
    apply plugin: 'org.greenrobot.greendao' // 添加插件的调用
     
    dependencies {
        implementation 'org.greenrobot:greendao:3.2.2' // 添加库的依赖
    }
    
    • 配置数据库版本号和代码生成路径
      GreenDAO可以管理数据库版本、自动生成代码,需要我们通过配置告诉它数据库版本号以及生成代码的路径,配置写在Module的build.gradle,如下:
    greendao {
        schemaVersion 6                       //数据库版本号
        targetGenDir "src/main/java"     //生成代码的根路径
        daoPackage "com.example.lenovo.dao"          //在根路径下生成类的包名
    }
    
    • 编写Entity类
      GreedDAO会根据注解自动生成代码,所以开始只需要编写实体类即可,GreenDAO会帮我们自动生成创建数据库的代码及各个表对应的dao类。如下是一个Entity类:
    @Entity
    public class Student {
        @Id(autoincrement = true) Long id;
        String name;
        int age;
        String grades;
        @ToMany(referencedJoinProperty = "studentId")
        List<Course> courseList;
    }
    

    这就是个简单的数据类,只是增加了几个注解,然后编译一下工程,神奇的事情就会发生,这个类会扩张成一个比较复杂的类,其中Course是另一个类似的实体类,这里不再贴出代码了。扩张后的Student类如下:

    @Entity
    public class Student {
        @Id(autoincrement = true) Long id;
        String name;
        int age;
        String grades;
        @ToMany(referencedJoinProperty = "studentId")
        List<Course> courseList;
        /** Used to resolve relations */
        @Generated(hash = 2040040024)
        private transient DaoSession daoSession;
        /** Used for active entity operations. */
        @Generated(hash = 1943931642)
        private transient StudentDao myDao;
        @Generated(hash = 990890750)
        public Student(Long id, String name, int age, String grades) {
            this.id = id;
            this.name = name;
            this.age = age;
            this.grades = grades;
        }
        @Generated(hash = 1556870573)
        public Student() {
        }
        public Long getId() {
            return this.id;
        }
        public void setId(Long id) {
            this.id = id;
        }
        public String getName() {
            return this.name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public int getAge() {
            return this.age;
        }
        public void setAge(int age) {
            this.age = age;
        }
        public String getGrades() {
            return this.grades;
        }
        public void setGrades(String grades) {
            this.grades = grades;
        }
        /**
         * To-many relationship, resolved on first access (and after reset).
         * Changes to to-many relations are not persisted, make changes to the target entity.
         */
        @Generated(hash = 838351874)
        public List<Course> getCourseList() {
            if (courseList == null) {
                final DaoSession daoSession = this.daoSession;
                if (daoSession == null) {
                    throw new DaoException("Entity is detached from DAO context");
                }
                CourseDao targetDao = daoSession.getCourseDao();
                List<Course> courseListNew = targetDao._queryStudent_CourseList(id);
                synchronized (this) {
                    if (courseList == null) {
                        courseList = courseListNew;
                    }
                }
            }
            return courseList;
        }
        /** Resets a to-many relationship, making the next get call to query for a fresh result. */
        @Generated(hash = 829241409)
        public synchronized void resetCourseList() {
            courseList = null;
        }
        /**
         * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
         * Entity must attached to an entity context.
         */
        @Generated(hash = 128553479)
        public void delete() {
            if (myDao == null) {
                throw new DaoException("Entity is detached from DAO context");
            }
            myDao.delete(this);
        }
        /**
         * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
         * Entity must attached to an entity context.
         */
        @Generated(hash = 1942392019)
        public void refresh() {
            if (myDao == null) {
                throw new DaoException("Entity is detached from DAO context");
            }
            myDao.refresh(this);
        }
        /**
         * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}.
         * Entity must attached to an entity context.
         */
        @Generated(hash = 713229351)
        public void update() {
            if (myDao == null) {
                throw new DaoException("Entity is detached from DAO context");
            }
            myDao.update(this);
        }
        /** called by internal mechanisms, do not call yourself. */
        @Generated(hash = 1701634981)
        public void __setDaoSession(DaoSession daoSession) {
            this.daoSession = daoSession;
            myDao = daoSession != null ? daoSession.getStudentDao() : null;
        }
        
    }
    

    可以看到自动生成了很多代码,包括构造函数、get、set、增删改查方法等。这些代码就是GreenDAO根据我们写的那几个注解生成的。
    @Entity表示这个类会生成一个对应的表,这个注解还有一些属性可以设置:

    @Entity(
            schema = "schemaName",    //表示实体类对应的表
            active = true,                         //表示实体类是“活的”,具有增删改查
            nameInDb = "AWESOME_USERS",   //数据库中使用的别名
            indexes = {                                             //定义索引
                    @Index(value = "name DESC", unique = true)
            },
            createInDb = true,                 //是否生成表,默认true
            generateConstructors = true,    //是否生成构造方法
            generateGettersSetters = true  //是否生成get、set方法
    )
    

    @Id(autoincrement = true) 表示表的主键,并且自动增加
    @ToMany(referencedJoinProperty = "studentId") 表示一个一对多的关系,并通过studentId关联。
    这类注解还有很多,比如
    @Property(nameInDb="name") 可以配置一个非字段名的列,不配置默认使用字段名
    @NotNull 表示这列不能为空
    @Transient 标记的字段不会生成表的一列
    @Unique 表示这列只能有唯一值
    @ToOne 表示一对一关系 ,如@ToOne(joinProperty = "name")
    @OrderBy 表示这列如何排序,当get这列数据的时候会按照这个排序给出

    再看一下之前配置的路径src/main/java之下的com.example.lenovo.dao包下,也生成了几个类:



    这个几个类就是我们会用到的数据库操作类。
    其中的DaoMaster类有两个内部类,OpenHelper和DevOpenHelper,如下

    
        /**
         * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
         */
        public static abstract class OpenHelper extends DatabaseOpenHelper {
            public OpenHelper(Context context, String name) {
                super(context, name, SCHEMA_VERSION);
            }
    
            public OpenHelper(Context context, String name, CursorFactory factory) {
                super(context, name, factory, SCHEMA_VERSION);
            }
    
            @Override
            public void onCreate(Database db) {
                Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
                createAllTables(db, false);
            }
        }
    
        /** WARNING: Drops all table on Upgrade! Use only during development. */
        public static class DevOpenHelper extends OpenHelper {
            public DevOpenHelper(Context context, String name) {
                super(context, name);
            }
    
            public DevOpenHelper(Context context, String name, CursorFactory factory) {
                super(context, name, factory);
            }
    
            @Override
            public void onUpgrade(Database db, int oldVersion, int newVersion) {
                Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");
                dropAllTables(db, true);
                onCreate(db);
            }
        }
    
    

    后者是前者子类,在OpenHelper的onCreate方法中进行了数据库创建,而DevOpenHelper的onUpdate方法中是删除数据库并重新创建,每次升级数据库就会调用这个方法,所以实际开发中我们可以自己实现一个OpenHelper,并在onUpdate方法中处理数据库升级。

    • 初始化
      有了上面生成的几个类后,就可以进行初始化工作了,一般是在Application的onCreate方法中进行,如下代码:
    public class MyApplication extends Application {
        private DaoSession daoSession;
        @Override
        public void onCreate() {
            super.onCreate();
            DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(this, "test.db");
            SQLiteDatabase database = devOpenHelper.getWritableDatabase();
            DaoMaster daoMaster = new DaoMaster(database);
            daoSession = daoMaster.newSession();
        }
    
        public DaoSession getDaoSession(){
            return daoSession;
        }
    }
    

    这里通过OpenHelper生成了一个名为test的数据库,并最终通过DaoMaster获得了一个DaoSession保存在了Application中。

    • 增删改查
      我们使用数据表对应的dao类对表进行操作,dao类可以通过Application中的DaoSession获得。
    StudentDao studentDao = ((MyApplication)getApplication()).getDaoSession().getStudentDao();
    
    //增
    studentDao.insert(student);   //插入一条数据
    studentDao.insertOrReplace(student);   //增加一条数据,如果有这条数据则更新
    
    //删
    studentDao.delete(student);      //通过对象删除
    studentDao.deleteByKey(1L);   //通过id删除
    
    //改
    studentDao.update(student);
    
    //查
    List<Student> studentList = studentDao.loadAll();   //获取所有数据
    studentDao.queryRaw("where age>?","18"); //直接写查询条件
    studentDao.loadByRowId(1);  //根据ID查询
    QueryBuilder builder = userDao.queryBuilder();
    return  builder.where(StudentDao.Properties.Age.gt(18)).build().list();  //通过QueryBuilder拼查询条件
    

    Realm

    Realm和GreenDAO有很大的不同,Realm本身就是一种数据库,而且是可以支持跨平台的数据库,比SQLite更轻量级,速度也更快,支持JSON格式、数据变更通知、数据同步等等特性。下面介绍它的简单使用流程。

    • 引入依赖
      Realm同样需要在工程的build.gradle文件和Module的build.gradle文件配置依赖。
      工程的build.gradle:
    buildscript {
        repositories {
            google()
            jcenter()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:3.0.1'
            classpath "io.realm:realm-gradle-plugin:5.0.0"     //realm插件路径
        }
    }
    
    

    Module中的build.gradle只需引入插件即可:

    apply plugin: 'realm-android'
    
    • 初始化
      Realm的初始化也推荐在Application中进行,例子如下:
    public class MyApplication extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
            Realm.init(this);
            RealmConfiguration configuration = new RealmConfiguration
                    .Builder()
                    .name("test.realm")    //设置数据库名称
                    .migration(new RealmMigration() {            //设置升级策略
                        @Override
                        public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
    
                            RealmSchema schema = realm.getSchema();
    
                            switch ((int) oldVersion){
                                case 0:
                                    schema.create("Person")
                                            .addField("name", String.class)
                                            .addField("age", int.class);
                                    oldVersion++;
                                case 1:
                                    schema.get("Person")
                                            .addField("id", long.class, FieldAttribute.PRIMARY_KEY)
                                            .addRealmObjectField("favoriteDog", schema.get("Dog"))
                                            .addRealmListField("dogs", schema.get("Dog"));
                                    oldVersion++;
                            }
                        }
                    })
                    .deleteRealmIfMigrationNeeded()   //合并需要删除可以删除
                    .schemaVersion(4)                //设置版本号
                    .inMemory()                     //设置为内存数据库
                    .readOnly()                     //设置数据库只读
                    .build();
            Realm.setDefaultConfiguration(configuration);
        }
    }
    
    

    首先调用Realm.init(this),如果需要设置一些初始化功能,可以创建一个RealmConfiguration,代码中可以看到,RealmConfiguration可以指定很多功能,比如数据库名称、升级策略、版本号、以及设置只读、内存数据库等等。然后把这个RealmConfiguration设置为Realm的默认配置即可。

    • 编写Entity类
      Realm的实体类需要继承RealmObject,比GreenDAO侵入性要高,也有一些功能性的注解可以使用,但没有GreenDAO的注解强大,如下是一个实体类:
    public class Student extends RealmObject{
        @PrimaryKey
        long id;
        String name;
        int age;
        String grades;
    
        RealmList<Course> courses;
    
        public RealmList<Course> getCourses() {
            return courses;
        }
    
        public void setCourses(RealmList<Course> courses) {
            this.courses = courses;
        }
    
        public long getId() {
            return id;
        }
    
        public void setId(long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getGrades() {
            return grades;
        }
    
        public void setGrades(String grades) {
            this.grades = grades;
        }
    }
    

    其中@PrimaryKey表示主键,但是没有自动增加的功能,需要自己实现。学生和课程关系是用RealmList表示的,这种关系和SQLite中的一对多、多对多有可能是不太一样的,但还需要进一步研究才能确定。Course类也是类似的,不再贴代码了。

    • 增删改查
      Realm的增删改查是通过Realm类的实例来实现的,并且必须在事务中进行而且不能跨线程操作,先看代码:
    Realm realm = Realm.getDefaultInstance();    //获取默认配置的Realm实例
    
    //或者这样随时用一个RealmConfiguration参数生成一个Realm
    //realm = Realm.getInstance(new RealmConfiguration.Builder().name("othor.realm").build()); 
    
    //增
    //增加3个课程
            realm.beginTransaction();      //必须先开事务
            List<Course> courseList = new ArrayList<>();
            Course course1 = realm.createObject(Course.class);    //使用createObject方法创建对象
            course1.setName("数学");
            course1.setId(0);
            courseList.add(course1);
            Course course2 = new Course();  //用new创建对象,这个对象对于Realm是非托管的
            course2.setName("语文");
            course2.setId(1);
            realm.copyToRealm(course2); //new 出的对象必须使用copyToRealm方法“拷贝”给Realm,不能直接插入,这也是个值得研究的问题
            courseList.add(course2);
            Course course3 = realm.createObject(Course.class);
            course3.setName("英语");
            course3.setId(2);
            courseList.add(course3);
            realm.insert(courseList);              //批量插入
            realm.commitTransaction();       //关闭事务
    
           //或者用回调的方式操作,可以代替事务开关
            realm.executeTransaction(new Realm.Transaction() {
                @Override
                public void execute(Realm realm) {
                    Student student = new Student();
                    student.setName("小明");
                    student.setAge(18);
                    student.setGrades("一年级");
                    realm.copyToRealm(student);
                }
            });
            
    //删
             realm.beginTransaction();
             RealmResults<Student> students = realm.where(Student.class).findAll();
             students.get(0).deleteFromRealm();    //通过实体类自己的方法删除
             students.deleteFirstFromRealm();       //通过RealmResults删除
             students.deleteLastFromRealm();    //通过RealmResults删除
             students.deleteFromRealm(1);   //通过RealmResults删除
             realm.commitTransaction();
    
    //改
              realm.beginTransaction();
              RealmResults<Student> students = realm.where(Student.class).findAll();
              students.get(0).setName("更新名字");     //直接查出一个数据修改就行
              realm.commitTransaction();
             
    //查
              //查的方式很多,比如上面的findAll方法,还有下面的equalTo、between,查询某个字段等于某个值或在某个区间等等,这种方法有很多很多
              Student student = realm.where(Student.class).equalTo("age",18).findFirst();
              RealmQuery<Student> studentList = realm.where(Student.class).between("age",10,18);
    
    

    如上就是GreenDAO和Realm的简单对比及使用流程,更深入的分析,可以关注后续文章。

    相关文章

      网友评论

        本文标题:GreenDAO系列(一) GreenDAO和Realm的简单使

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