GreenDao 3.2使用简介

前言

Android开发中,经常用到的三种本地数据持久化的方式为:

  • SharedPreference
  • 文件存储的方式
  • 数据库(SQLite)
    我们在开发过程中,经常会需要存储相对大量的数据,因此数据库常常会用到。Android使用数据库一般有两种方案:
  1. 使用原生数据库SQLite直接操作;
  2. 使用第三方数据库,如GreenDao。

GreenDao使用简单,加上其本身存取快、体积小、支持缓存、支持加密等优点,使得它成为了一个受欢迎的ORM解决方案,今天我们就简要介绍一下GreenDao的用法和使用过程中一些坑。

基本使用

github地址

GreenDao

项目中引入

  • 项目的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' // 添加GreenDao依赖
    }
}
  • module的build.gradle中引入
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // 添加GreenDao Plugin

dependencies {
    implementation 'org.greenrobot:greendao:3.2.2' // 添加GreenDao依赖
}
  • 数据库版本号与路径设置
    GreenDao核心类共有三个分别为DaoMaster.java、DaoSession.java以及根据你创建的持久化数据结构而生成的XXDao.java。这三个类都可以自动生成,无需手动编写代码。
    生成以上三个类需要在module的build.gradle文件中进行如下配置:
greendao{
    schemaVersion 1  //数据库版本号
    daoPackage 'com.example.greendaotest.dao'//DaoMaster、DaoSession、XXDao的包名
    targetGenDir 'src/main/java' //DaoMaster、DaoSession、XXDao的路径
    generateTests false //设置为true以自动生成单元测试。
    targetGenDirTests 'src/androidTest/java' //应存储生成的单元测试的基本目录。
}

其中,schemaVersion为数据库版本号必填;若不需要单元测试,generateTests和targetGenDirTests可以删除;daoPackage和targetGenDir选填,如果填写会根据填写的路径生成相关的代码,否则会在build/generated/source/greendao中自动生成相关的类。
到此,GreenDao的引入工作已经完成,可以再代码中使用GreenDao进行数据库相关开发了。

构建实体类

  • 注意:实体类中的属性即为数据库中对应的字段
@Entity
public class UserBean {
    @Id(autoincrement = true)
    private Long id;
    @NotNull
    private String name;
    private int age;
}
GreenDao注解
  • @Entity:告诉GreenDao该对象为实体,只有被@Entity注释的Bean类才能被dao类操作
  • @Id:对象的Id,使用Long类型作为EntityId,否则会报错。(autoincrement = true)表示主键会自增,如果false就会使用旧值
  • @Property:可以自定义字段名,注意外键不能使用该属性
  • @NotNull:属性不能为空
  • @Transient:使用该注释的属性不会被存入数据库的字段中
  • @Unique:该属性值必须在数据库中是唯一值
  • @Generated:编译后自动生成的构造函数、方法等的注释,提示构造函数、方法等不能被修改

实体类创建后,Build --> Make Module 'app', 会自动生成相应的类,build后刚刚创建的实体类如下:

@Entity
public class UserBean {
    @Id(autoincrement = true)
    private Long id;
    @NotNull
    private String name;
    private int age;
    @Generated(hash = 1420883130)
    public UserBean(Long id, @NotNull String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    @Generated(hash = 1203313951)
    public UserBean() {
    }
    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;
    }
}

并且,在刚刚build.gradle 中配置的daoPackage目录下生成了三个java文件,分别为:

  • DaoMaster.java
  • DaoSession.java
  • UserBeanDao.java
    这三个类是自动生成的,不需要手动做修改。自此,实体类构建完成。

初始化数据库

  1. 创建数据库管理类DbManager,也可以不创建,直接在Application中初始化:
public class DbManager {

    private static final String DB_NAME = "search";
    private static DbManager mDbManager;
    private static DaoMaster.DevOpenHelper mDevOpenHelper;
    private static DaoMaster mDaoMaster;
    private static DaoSession mDaoSession;

    private Context mContext;

    private DbManager(Context context) {
        this.mContext = context;
        // 初始化数据库信息
        mDevOpenHelper = new DaoMaster.DevOpenHelper(context, DB_NAME);
        getDaoMaster(context);
        getDaoSession(context);
    }

    public static DbManager getInstance(Context context) {
        if (null == mDbManager) {
            synchronized (DbManager.class) {
                if (null == mDbManager) {
                    mDbManager = new DbManager(context);
                }
            }
        }
        return mDbManager;
    }

    /**
     * 获取可读数据库
     *
     * @param context
     * @return
     */
    public static SQLiteDatabase getReadableDatabase(Context context) {
        if (null == mDevOpenHelper) {
            getInstance(context);
        }
        return mDevOpenHelper.getReadableDatabase();
    }

    /**
     * 获取可写数据库
     *
     * @param context
     * @return
     */
    public static SQLiteDatabase getWritableDatabase(Context context) {
        if (null == mDevOpenHelper) {
            getInstance(context);
        }

        return mDevOpenHelper.getWritableDatabase();
    }

    /**
     * 获取DaoMaster
     *
     * @param context
     * @return
     */
    public static DaoMaster getDaoMaster(Context context) {
        if (null == mDaoMaster) {
            synchronized (DbManager.class) {
                if (null == mDaoMaster) {
                    mDaoMaster = new DaoMaster(getWritableDatabase(context));
                }
            }
        }
        return mDaoMaster;
    }

    /**
     * 获取DaoSession
     *
     * @param context
     * @return
     */
    public static DaoSession getDaoSession(Context context) {
        if (null == mDaoSession) {
            synchronized (DbManager.class) {
                mDaoSession = getDaoMaster(context).newSession();
            }
        }
        return mDaoSession;
    }
}

2.初始化GreenDao:

public class GreenDaoApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        //初始化GreenDao
        DbManager.getInstance(this);
    }
}

增删改查

为了方便介绍,单独建立一个单例类,创建UserBeanDaoManager,如下:

package com.example.greendaotest;

import android.content.Context;

import com.example.greendaotest.bean.UserBean;
import com.example.greendaotest.dao.UserBeanDao;

import java.util.List;

/**
 * 1 * FileName: UserBeanDaoManager
 * 2 * Author: WangDongDong
 * 3 * Date: 2019/1/3 4:34 PM
 * 4 * Description:
 * 5 * History:
 * 6 *  

以上是GreenDao基本的增删改查操作,GreenDao提供了更加丰富的API,这些API可以通过java doc直接看到,再此不做更多介绍。

联合查询

创建班级类,ClassBean.java:

@Entity
public class ClassBean {
    @Id(autoincrement = true)
    private Long id;
    private String className;
    @Generated(hash = 2143102101)
    public ClassBean(Long id, String className) {
        this.id = id;
        this.className = className;
    }
    @Generated(hash = 1395092832)
    public ClassBean() {
    }
    public Long getId() {
        return this.id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getClassName() {
        return this.className;
    }
    public void setClassName(String className) {
        this.className = className;
    }
}

在UserBeanDaoManager中新增方法:

/**
     * 联合查询,查询班级为一班的学生
     * @param name
     * @param age
     * @return
     */
    public List queryByNameAndAge(String name, int age) {
        QueryBuilder builder = mUserBeanDao.queryBuilder();
        builder.join(ClassBean.class, ClassBeanDao.Properties.Id)
                .where(ClassBeanDao.Properties.ClassName.eq("一班"));
        List list = builder.list();
        return list;
    }
查询条件
  • distinct() 过滤字段
  • limit() 限制数量
  • offset() 忽略查询出的前n条结果
  • orderAsc() 升序排列
  • orderDesc() 降序排列
  • eq() 等于
  • notEq() 不等于
  • in() 在范围之内
  • notIn() 不再范围之内
  • or() 或者
  • like() 就是sql语句的 LIKE "%" +string+ "%"
  • between() 也就是BETWEEN ? AND ? 可以取两个值的区间
  • gt() 相当于 >
  • ge() 相当于 >=
  • lt() 相当于 <
  • le() 相当于 <=
  • isNull 为空
  • notIsNull 不为空

数据库升级

build.gradle 的greendao配置中,有schemaVersion字段:

greendao{
    schemaVersion 1  //数据库版本号
    daoPackage 'com.example.greendaotest.dao'//DaoMaster、DaoSession、XXDao的包名
    targetGenDir 'src/main/java' //DaoMaster、DaoSession、XXDao的路径
    generateTests false //设置为true以自动生成单元测试。
//    targetGenDirTests 'src/androidTest/java' //应存储生成的单元测试的基本目录。
}

升级数据库需要将该字段值加1。
但是!但是!!但是!!!如果只是版本+1 ,数据库字段确实添加成功了,但所有的数据都没了,这在实际应用中,是一个很严重的问题。
原因可见自动生成的DaoMaster类中,onUpgrade处的注释:

/** 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);
        }
    }

原来,GreenDao在升级时,首先会删除所有table,然后重新创建,因此数据在升级是会丢。
因此,我们可以通过以下的方式进行升级:

  • 方案一:每次升级时,在onUpgrade中,执行升级数据库的sql:
@Override
public void onUpgrade(Database db, int oldVersion, int newVersion) {
            if (oldVersion < 2) {
                //添加性别字段
                db.execSQL(String.format("ALTER TABLE %s ADD %s varchar", DbManager.DB_NAME, "gender"));
            }
            if (oldVersion < 3) {
                //添加成绩字段
                db.execSQL(String.format("ALTER TABLE %s ADD %s integer default 0", DbManager.DB_NAME, "score"));
            }
        }
  • 方案二:
    1.首先创建临时表。
    2.把当前表的数据插入到临时表中去。
    3.删除掉原表,创建新表。
    4.把临时表数据插入到新表中去,然后删除临时表。

数据库升级方案可以参考GreenDaoUpgradeHelper,这里提供了完整的数据库升级解决方案,在此处就不搬运了。

混淆

#greendao3.2.0,此是针对3.2.0,如果是之前的,可能需要更换下包名
-keep class org.greenrobot.greendao.**{*;}
-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
public static java.lang.String TABLENAME;
}
-keep class **$Properties

一些坑

  1. 自动生成的类(DaoMaster.java、DaoSession.java,XXDao.java)是动态编译的,因此在多人协作开发时,会出现在Git更改记录中,总是需要提交。因此,该部分应放在gitignore中。
  2. JavaBean在编译之后,会生成@Generated(hash = xxxxxxxxx)样式的注解,如:
 @Generated(hash = 1420883130)
    public UserBean(Long id, @NotNull String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

该hash值也是动态生成的,因此如果动态编译之后,hash值有可能会改变,会发生编译错误

java.lang.RuntimeException: Constructor (see UserBean:24) has been changed after generation.
Please either mark it with @Keep annotation instead of @Generated to keep it untouched,
or use @Generated (without hash) to allow to replace it.

因此,此部分需要在生成之后,使用@Keep注解替换:

@Keep
    public UserBean(Long id, @NotNull String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
  1. 数据库升级问题,如果需要升级数据库,一定记得重写DaoMaster中onUpgrade的方法,升级数据库的过程中,数据会直接丢失,造成严重后果。

你可能感兴趣的:(GreenDao 3.2使用简介)