Android 开源库分析——greenDAO

本文链接:https://rainmonth.github.io/posts/A211027.html
Android greenDAO使用

参考内容:官方网站,打开官网发现官网极力推荐ObjectBox使用,后面会对ObjectBox页做一个简单的说明。

摘要

greenDAO是一款开源高效的ORM数据库,极大的降低开发者编写sql查询的时间(使用greenDAO后会自动生成对应的dao,可以进行CRUD操作)。而greenDAO的使用也极其简单。greenDAO具有以下特点:

  • 性能好,特别是在Android平台上(相对于其他的ORM框架来说)表现尤其出色;
  • 易用,提供了强大的API;
  • 内存消耗小;
  • 库体积小;
  • 支持数据库加密,greenDAO支持SQLChiper
  • 社区强大

ORM

ORM,即对象关系映射(Object-Relationship Mapping),具有以下特点:

  • 简单,ORM可以将SQLite(或MySql)的一张表映射成一个Java对象;
  • 精确,所有的数据表都按统一的标准映射成Java对象;
  • 易懂,ORM是数据库结构文档化,一张张表对Java程序员来说就是一个个实体类;
  • 易用,ORM包含了对生成Java持久化数据对象的一些列CRUD操作,风格统一,规范,便于维护;

参考:ORM框架简介

基本使用

配置greedDAO环境

  1. 根目录的build.gradle添加如下代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    buildscript {
    repositories {
    jcenter()
    mavenCentral() // 添加仓库地址
    }
    dependencies {
    classpath 'com.android.tools.build:gradle:3.5.3'
    classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0' // 添加插件
    }
    }
  2. 应用module的build.gradle文件添加如下代码

    1
    2
    apply plugin: 'com.android.application'
    apply plugin: 'org.greenrobot.greendao' // apply plugin

dependencies {
implementation ‘org.greenrobot:greendao:3.3.0’ // add library
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
3. greenDAO的一些配置
通过上面两步就可以在项目中使用greenDAO,在app module中添加greendao闭包处理,如下:
```groovy
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin
greendao {
// 数据库版本,很重要,后面数据库的升级需要用到它
schemaVersion 1
// 指定dao文件所在的包名
daoPackage 'com.rainmonth.notebook.dao'
// 指定插件自动生成java文件存放的目录
targetGenDir 'src/main/java'
// 是否自动生成单元测试
generateTests true
}

dependencies {
implementation 'org.greenrobot:greendao:3.3.0' // add library
}

基础注解说明

  • @Entity,加在类上,声明表的实体类型,加上给注解后会根据类的属性生成必要的表,字段就是类的属性;该注解有如下属性

    • nameInDb, 表在数据库中的别名,不指定默认就是实体类名;
    • Index[],索引,可以跨列;
    • createInDb,默认true,标记在数据库中刚创建表;
    • schema,表的schema;
    • active,默认为true,实体(表)是否处于活跃状态,活跃状态的有更新删除和刷新的方法;
    • generateConstructors,默认true,是否生成构造函数;
    • generateGettersSetters,默认true,标记是否生成相应的get/set方法;
    • protobuf,默认为void.class,为实体类提供一个额外的class以生成一个特殊的DAO
  • @Id,加载实体类的属性上,定义自增id,有一个autoincrement属性,默认为false;

  • @Index,加载实体类属性上,表示索引,有value、name、unique三个属性;

  • @Generated,表明是属性、方法或构造方法是由greenDAO自己生成的代码;

  • @JoinEntity,用于建立多对多关系

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    @Entity
    public class Product {
    @Id private Long id;

    @ToMany
    @JoinEntity(
    entity = JoinProductsWithOrders.class,
    sourceProperty = "productId",
    targetProperty = "orderId"
    )
    private List ordersWithThisProduct;
    }

    @Entity
    public class JoinProductsWithOrders {
    @Id private Long id;
    private Long productId;
    private Long orderId;
    }

    @Entity
    public class Order {
    @Id private Long id;
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- @JoinProperty,用来定义Join的的两个属性分别是name和referenceName
- @Keep,表明下次greenDAO再次生成代码是要不能修改被@Keep修饰的属性、方法或类型;
- @NotNull,非空注解;
- @OrderBy,跟排序相关,某字段加上了该注解,就根据该字段排序;
- @Property,定义@Entity实体类某个字段在数据库表中对应的名字;
- @ToMany,一对多或多对多;
- @ToOne,一对一;
```java
@Entity
public class Order {

@Id private Long id;

private long customerId;

@ToOne(joinProperty = "customerId")
private Customer customer;
}

@Entity
public class Customer {
@Id
private Long id;
}
  • @Transient,不会被greenDAO持久化到数据库中;
  • @Unique,创建表的时候被@Unique注释的属性需遵循唯一性约束;

实体类支持的字段类型

greenDAO默认支持的字段类型有如下:

1
2
3
4
5
6
7
8
9
10
boolean, Boolean
int, Integer
short, Short
long, Long
float, Float
double, Double
byte, Byte
byte[]
String
Date

核心类

greenDAO 使用是否简单,在使用@Entity注解定义好实体后,编译就可以自动生成一些用来进行数据库操作的处理。正常的主要类有DaoMasterDaoSessionXxxEntity

DaoMaster

负责管理数据库对象和EntityDAO对象的关系,可以通过其内部类OpenHelper、DevOpenHelper、SQLiteOpenHelper创建不同模式的数据库。负责表的创建、更新,负责数据库的升级,负责DaoSession的创建

DaoSession

管理者所有的EntityDAO,提供了公用的实体CRUD方法(实际上是在AbstractDAOSession中实现的)

XxxEntity

根据定义的java类(@Entity注解修饰的类)生成的类文件,会根据@Entity注解的属性按需生成类方法(如是否需要生成构造函数,是否需要生成getter/setter方法等。

原理

由于greenDAO是一个ORM框架,干的就是对象关系映射这件事,所以要搞清楚对象到关系的映射是如何生成的。这里原理分析部分主要分析代码自动生成,这样以后自己也可以写插件自动生成代码了。

  1. greenDAO的api就是其定义的那些注解,开发者在原始的类中根据要在数据库中建立的表的关系,将api(即注解应用上去),这样开发者的工作就完成了,箱单简单;
  2. 前面在介绍基本使用时,在项目的根目录下的build.gradle引用了gradle插件org.greenrobot:greendao-gradle-plugin:3.3.0,并在app Module中的build.gradle文件作用应用了插件apply plugin: 'org.greenrobot.greendao'。注意应用这个插件的目的就是为了让greenDAO在编译能找到合适的时机来进行相关的代码生成。这说明两点:
    1. greenDAO的gradle插件只是为greenDAO生成代码找一个切口,跟greenDAO代码生成是没有直接联系的;
    2. 生成代码是在编译期进行的,所以greenDAO并不会影响app运行时的效率,但是会影响apk的编译速度。(实际项目中可以采用demo工程生成相关代码,然后将代码拷贝到实际的工程中来,这样就避免了这一影响)
  3. 有一个控制模块:greendao-code-modifier,它来控制greenDAO的代码生成,具体包括:
    1. 收集注解信息并判断注解信息是否要更新(收集注解信息采用的greendao-jdt,由此可见greenDAO采用的注解处理器是JDT,与常见的注解处理方法(APT、反射不同)
    2. 调用greenDAO的code-generator来进行代码,code-generator使用的代码生成模板为FreeMarker;
  4. 然后就得到了开发者实际运行时用到的几个核心类:DaoMaster、DaoSession、XxxEntity;
  5. greenDAO-core通过4中生成的三中核心类来跟SQLite进行交互。

用到的技术

  1. gradle插件技术,gradle插件开发介绍:https://www.jianshu.com/p/d53399cd507b;
  2. Android注解相关技术
  3. JDT注解处理与收集,JDT介绍:http://www.eclipse.org/jdt/overview.php
  4. 代码生成框架FreeMaker,FreeMarker快速入门:https://segmentfault.com/a/1190000011768799

greeDAO 数据库升级

为什么会要单独拎一节来讲数据库升级呢?首先,面试中经常会被问到,哈哈;其次这个是开发中很容易遇到的问题,下面来介绍下。

greenDAO的数据库升级处理主要是在DaoMaster中进行的,主要是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 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);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
super.onUpgrade(db, oldVersion, newVersion);
//todo 这里进行的时数据库升级操作
}

}

当我们数据表发生更改的时候,我们需要在app的build.gradle下的greendao{}升级schemaVersion版本,版本改变后,最终就会走到onUpgrade(...)里面。
粗暴的策略:删掉所有老的表,然后创建新的,然后所有的数据都丢了,然后该滚蛋了…….

通常的升级策略

  1. 针对有索引的表,先要删除相应的索引,否则数据备份是会存在冲突;
  2. 针对有变动的表,将原来的老表重命名为相应的临时表(改变表明);
  3. 创建新表
  4. 数据恢复,将临时表的数据恢复到新表中(新表临时表都包含的字段需要恢复)
  5. 删除临时表;
  6. 保底措施:如果表没出创建成功,重新创建

greenDAO降级处理

降级处理比较简单,删除表然后重新创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
LogHelper.i(TAG, "主数据库降级 [" + mDbName + "] from version " + oldVersion + " to " + newVersion);

try {
DaoMaster daoMaster = new DaoMaster(db);
Database database = daoMaster.getDatabase();

// 数据库版本号降级时,先删除所有表,然后重新创建
DaoMaster.dropAllTables(database, true);

DaoMaster.createAllTables(database, true);
} catch (Throwable e) {
LogHelper.e(TAG, mDbName + "onDowngrade from version " + oldVersion + " to " + newVersion + " err: " + e.toString());
}
}

疑问

  1. 不定义主键会发生什么?

  2. SqlLite数据库操作需要加锁吗?

  3. SqlLite数据库不同字段的限制条件是什么?

小结

了解了greenDAO的基本使用,特点及性能情况,同时对greenDAO整体的处理流程做了一个梳理,至于具体每个流程,如插件编写、注解处理、FreeMarker的具体使用细节,后面有机会单独介绍,这里不做赘述。

本文知识greenDAO的基本使用及原理介绍,高级使用请移步Android ORM 框架:GreenDao 使用详解(进阶篇)