GreenDao框架简析

GreenDao是一款开源的面向Android的轻便、快捷的ORM(对象映射)框架,将Java对象映射到SQLite数据库中,避免编写复杂的SQL语句。具有高性能、低开销,且支持数据库加密等功能。本文只做GreenDao核心API简析,详细使用请参考GreenDao官方文档译文 ,ORM数据库框架greenDAO SQLite MD。

相关概念

核心类

  • DaoMaster:负责管理数据库对象(SQLiteDatabase)和DAO类(对象),通过OpenHelper、DevOpenHelper和SQLiteOpenHelper创建不同模式的SQLite数据库;
  • DaoSession:管理指定模式下的所有DAO对象,提供了基本的实体类操作;
  • XxxDAO:针对每个实体类生成的数据访问对象;
  • Entities:可持久化对象,一个JavaBean实例对应数据库表的一行;

注解

  • @Entity:表明这个实体类会在数据库中生成一个与之对应的表;注:Kotlin不支持。
  • @Id:对应数据库表中的id字段,是一条数据的唯一标识,必须是Long类型;@Id(autoincrement = true)表示自增型主键。
  • @Property(nameInDb = "name"):表明这个属性对应数据库表中的name字段;
  • @NotNull:表明该属性值不能为空;
  • @Transient:表明该属性值不被存储在数据库中;
  • @Unique:表明该属性在数据库中只能有唯一值, 也可作为一条数据的唯一标识;优先级高于主键Id。
    • 多个@Unique注解的数据,满足其中一个就认定是同一条数据。
    • 自增型主键时,以@Unique注解的值为唯一性标识;
  • @OrderBy:增加某一字段的排序,如@OrderBy("data ASC") 正序;

常用API

  • QueryBuilder:SQL的语法错误只有在runtime时才会提示,但QueryBuilder可以在编译期就检测到错误。
  • where:配置查询条件,可传递多个查询条件。where语句里的条件用“且”连接,whereOr语句里的条件用"或"连接。
  • limit(int):从前面限制获取的条数。
  • offset(int):从某位置开始获取数据,即数据返回的偏移量。需要结合limit使用。
  • eq:等于;notEq:不等于;like:模糊查询;gt:大于;ge:大于等于;
  • insert:插入数据,如果已存在则插入失败,insertInTx是在事务中操作;
  • insertOrReplace:插入数据,如果已存在则替换(判断一条数据是否存在,由主键id或@Unique(高优先级)作为唯一标识),insertOrReplaceInTx是在事务中操作。

环境配置

  • module级别的build.gradle里:
apply plugin: 'org.greenrobot.greendao'
 
greendao {
       schemaVersion 1 //数据库版本号
       daoPackage 'com.example.myapplication.db'// 设置DaoMaster、DaoSession、Dao 包名
       targetGenDir 'src/main/java'//设置DaoMaster、DaoSession、Dao目录
}
dependencies {
    implementation 'org.greenrobot:greendao:3.2.2'//数据库
    implementation 'net.zetetic:android-database-sqlcipher:4.2.0'//数据库加密
    implementation 'io.github.yuweiguocn:GreenDaoUpgradeHelper:v2.2.1'//数据库升级
}
  • Project级别的build.gradle里:
dependencies {
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
}
allprojects {
    repositories {
        maven { url "https://jitpack.io" }
    }
}

核心功能

加密

SQLCipher是在SQLite基础上自定义256位的AES加密开源数据库,加密性能高,开销低。

public class App extends Application {
    public static DaoSession daoSession;
    private static String dbPassword = "test";//推荐使用设备唯一标识类UUID的字段加密值。
 
    @Override
    public void onCreate() {
        super.onCreate();
        initGreenDao();
    }
 
    private void initGreenDao() {
        DaoMaster.DevOpenHelper helper = new DbOpenHelper(this, "vivo_test.db", null);
        daoSession = new DaoMaster(helper.getEncryptedWritableDb(dbPassword)).newSession();
 
        //要在debug模式下开启,输出带有具体数值的SQL日志
        QueryBuilder.LOG_SQL = true;
        QueryBuilder.LOG_VALUES = true;
    }
}

SQLCipher是应用级别的数据库加密,通过命令查询等操作数据库,会被提示数据库加密,操作失败,进一步保证了数据的安全性。
SQLCipher的so包会增加apk的大小,约1.4M,不过平衡程序的安全性,影响不大。

升级

GreenDao在数据库版本升级时,默认会删除低版本数据。目前主流的方案是在数据库升级时将低版本数据迁移到临时表中,成功插入新版本的表后,再删除低版本数据。

新建 DbOpenHelper类继承自 DaoMaster.DevOpenHelper,并重写 onUpgrade方法,通过 GreenDaoUpgradeHelper库内的MigrationHelper类配置好所有DAO类,如PersonBeanDao.class、BankCardDao.class类,即可实现数据的迁移。

之后更改build.gradle中GreenDao的数据库版本号,clean Project -> makeProject。

public class DbOpenHelper extends DaoMaster.DevOpenHelper {
 
    public DbOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
        super(context, name, factory);
    }
 
    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
//        super.onUpgrade(db, oldVersion, newVersion);
        MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() {
            @Override
            public void onCreateAllTables(Database db, boolean ifNotExists) {
                DaoMaster.createAllTables(db, ifNotExists);
            }
 
            @Override
            public void onDropAllTables(Database db, boolean ifExists) {
                DaoMaster.dropAllTables(db, ifExists);
            }
        }, PersonBeanDao.class,BankCardDao.class);
    }
}

在 onUpgrade中处理数据迁移时,要注释掉 super.onUpgrade(db, oldVersion, newVersion); 并将现有所有的DAO类都加到 MigrationHelper.migrate()的参数内。

多表关联

@ToOne 定义了一个entities与另一个entities的1:1对应关系。通过joinProperty参数定义外键。
@ToMany 定义了一个entities与另一个entities的1:N对应关系。通过referencedJoinProperty参数定义外键。

  • 1:1对应关系示例
//源实体
@Entity
puiblic class PersonBean {
    @Id
    puiblic Long id;
    public String name;
    public Long bankCardId;
 
    @ToOne(joinProperty = "bankCardId")
    public BankCardBean bankCardBeans;
...
}
 
//目标实体
@Entity
public class BankCardBean {
    @Id
    public long id;
    public float money;
}

一对一表格关联,源实体与目标实体通过@ToOne(joinProperty= "bankCardId")外键关联,源实体中的bankCardId值关联目标实体的主键id。

  • 1:N对应关系示例
//源实体
@Entity
puiblic class PersonBean {
    @Id
    puiblic Long id;
    public String name;
 
    @ToMany(referencedJoinProperty = "personId")
    @OrderBy("money ASC")
    public List bankCardBeans;
...
}
 
//目标实体
@Entity
public class BankCardBean {
    @Id
    public Long id;
    public float money;
    public long personId;
}

在目标实体中定义与源实体关联的外键,即BankCardBean的personId,在源实体中由referencedJoinProperty 指定目标实体的外键。

目前GreenDao的多表关联方案较原始,不支持级联删除。不过目前项目中此类业务不多,可分别处理关联表的数据操作。

操作示例

private void asyncInsert() {
        final AsyncSession asyncSession = CommonInit.daoSession.startAsyncSession();
        asyncSession.runInTx(new Runnable() {
            @Override
            public void run() {
                asyncSession.setListenerMainThread(new AsyncOperationListener() {
                    @Override
                    public void onAsyncOperationCompleted(AsyncOperation operation) {
                        if (operation.isCompletedSucessfully()) {
                           //主线程 判断数据库操作完毕 更新UI
                        }
                    }
                });
                //todo 子线程 在此处开启事务操作数据库
                App.daoSession.getPersonBeanDao().insertOrReplaceInTx(personBeans);
            }
        });
    }

为了性能最优化,宜采用异步操作数据库。事务除了可以确保数据处理的动作的完整性,还能提升大量插入数据的性能。

注意:以下数据库操作相比示例只是替换了具体操作。

CommonInit.daoSession.getPersonBeanDao().insertOrReplaceInTx(personBeans);
CommonInit.daoSession.getPersonBeanDao().updateInTx(personBeans);

示例:查询该账号下年龄大于29岁的人,并按照年龄正序输出从第五个开始的前20个人。多个检索条件,多个where。

final List personBeans =
                       CommonInit.daoSession.getPersonBeanDao().queryBuilder()
                               .where(PersonBeanDao.Properties.Age.gt(29))
                               .offset(5)
                               .limit(20)
                               .orderAsc(PersonBeanDao.Properties.Age)
                               .list();

等同于

final List personBeans =
                       CommonInit.daoSession.getPersonBeanDao().queryRaw(
                       "WHERE AGE>? ORDER BY AGE ASC LIMIT ? OFFSET ?", "221239", "20", "5");
CommonInit.daoSession.getPersonBeanDao().deleteInTx(personBeans);
  • 实体类
@Entity
public class PersonBean implements Serializable {
    public static final long serialVersionUID = 1234134312341234L;
 
    @Id(autoincrement = true)
    public Long id;//一般不参与业务,自增主键
    @Unique
    public String identifyId;
    public String firstname;
    public String secondname;
    public String age;
    @Transient
    public String address;
...
}

你可能感兴趣的:(GreenDao框架简析)