Android ORM框架学习-greenDAO的基本使用

Android内置了SQLite数据库,而且可以利用JDBC对数据库进行操作,还提供了一个辅助类SQLiteOpenHelper对SQLite进行了简单的封装,但是一遍一遍的重复调用不仅枯燥而且敲代码的效率也很难上去。所以聪明的人类又想到把数据库底层进一步封装起来,只把操作接口,比如增删改查这一类的暴露给开发者,这样一来,不仅提升了开发效率,而且开发者不需要再花心思去关注底层逻辑实现了。这一类封装了底层数据库的数据库框架又被称为ORM框架,greenDAO就是其中之一。ORM框架有很多,之所以选择学习greenDAO,是综合了广大网友的评估意见,greenDAO确实是杠杠的,其他的框架之后有时间再进行学习。

greenDAO-orm

greenDAO是什么

前面将greenDAO概括为封装了底层数据库的框架,确实是这样,官网都说了,它为关系数据库(也就是SQLite)提供了面向对象的接口,简单实现了很多对数据的重复操作。这么说可能有点抽象,要清晰的了解greenDAO的话,建议先自行补充精神食粮:

  • ORM:即Object Relation Mapping(关系对象映射),也就是把关系数据库中的关系(可以理解为表)映射成Java对象,通过对Java对象的处理来操作数据库中的数据,利用面向对象的思想解决了开发者需要面对复杂的SQL语句的问题
  • 关系数据库:ORM就是因为关系数据库而产生了,所以了解关系数据库是很有必要的
  • Annotation:是指Java中的注解。greenDAO3中一改以前的风格,利用注解(Annotation)来生成相应的项目,所也要对它有个初步认识

由上图可以看到,greenDAO是间隔于Java对象与数据库之间的一层,通过greenDAO,数据库table中的每一条记录被映射成一个Java对象(就是后面会说到的Entity),记录的字段对应着Java对象的属性,可以直接通过getter和setter方法对字段进行操作,这中间发生的SQL语句完全不需要担心,greenDAO会自动生成一些类来完成这些任务。所以选择一个ORM框架对于数据库开发而言,说有如神助都不算夸张。

为项目添加greenDAO的Gradle插件

新建一个项目,项目源码地址放在文末。现在正式开始使用greenDAO了,既然greenDAO是一个辅助工具,和其他工具一样,第一步就是往项目里添加插件和依赖,好让项目构建的时候自动添加相关的库。具体操作如下:

  1. 在build.gradle(Moudle:app)文件中添加相关插件和依赖:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.1'
}
}
apply plugin: 'org.greenrobot.greendao'
dependencies {
compile 'org.greenrobot:greendao:3.2.0'
}

插件和依赖库的最新版本可以关注官方github,添加之后在文件中的效果:

add-gradle-plugin add-greenDAO-configuration

  1. 在同一个文件中配置greenDAO Gradle插件。原则上讲这一步可以省略,但是为了便于管理,最好配置一下,而且官方建议最少设置版本号,其他的如果不设置则使用默认值:
1
2
3
4
5
greendao {
schemaVersion 1
daoPackage 'com.crazypudding.greendaodemo.greendao.dao'
targetGenDir 'src/main/java'
}

如下图:

configure-gradle-plugin

添加完成点击右上角的Sync now会自动加载插件,由于插件在墙外,这是一个痛苦的过程,要加载多久就得拼脸了。PS.使用AS的时候最好能让它在全局代理的状态,这样会比较畅通,虽然这些设置都不一定能干得过硬性条件。。。

好了,现在对这些配置表示懵逼不要紧,在一切准备工作就绪以后回过头来看会更清晰的。greenDAO插件所有可以配置的属性说明如下,:

  • schemaVersion:这是当前数据库模式的版本号(可以理解为数据库版本),在升级数据库的时候会用到。默认设置成1,每次更新数据库,版本号只能增加
  • daoPackage:这是greenDAO自动生成的类所在的包名,默认和映射的Java对象Entity(后期会新建)的包名相同。greenDAO会自动生成DAOs、DaoMaster和DaoSession,DAOs对应Entity,另外两个是辅助类。
  • targetGenDir:自动生成的所有资源会被保存到这个位置,默认是在build/generated/source/greendao中。
  • generateTests:如果设置成true会自动生成单元测试文件
  • targetGenDirTests:单元测试文件保存的位置,默认是src/androidTest/java
  1. 构建Entity模型。新建一个类,类名就是你希望的表名,我新建一个Company类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Entity
public class Company {
@Id(autoincrement = true)
private Long id;
@NotNull
@Property(nameInDb = "EMPLOYEE_NAME")
private String name;
private int salary;
@Transient
private int tempUsageCount;
}

没错,这样简单的步骤就完成了Entity的建模,这其实就是数据库表的结构,里面的属性就是表的字段名(也就是列名),剩下的工作交给GgreenDAO去做就行了。如果你不止一个数据库表的话,需要多个Entity模型。现在可以使用Ctrl+F9来重新构建项目,或者在Android Studio中使用Build>Make Project

这里先对这个类简单说明一下,如果你熟悉注解的话,大概就能明白了。前面说greenDAO3使用注解来产生相应的类,这样比之前的版本方便很多,不需要手动去用写一个generator(生成器)了。greenDAO的生成器会在重新构建项目的时候自动生成一个完整的Entity类,其中包括各个属性的getter和setter方法,所以你最终会看到的完整的Company类会是这样的:

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@Entity
public class Company {
@Id(autoincrement = true)
private Long id;
@NotNull
@Property(nameInDb = "EMPLOYEE_NAME")
private String name;
private int salary;
@Transient
private int tempUsageCount;
@Generated(hash = 2028949425)
public Company(Long id, @NotNull String name, int salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
@Generated(hash = 1096856789)
public Company() {
}
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 getSalary() {
return this.salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}

你可能会发现在类中并没有tempUsageCount的getter和setter方法,这就是注解的作用了。Generator会根据注解来生成,而这些注解的意思就对应着SQLite建表时的各项限制了,他们把数据库表中的个字段映射成Java对象。

greenDAO的基本注解

@Entity:它会把当前类映射成默认以类名为表名的数据库表,每一个Entity对象对应着数据库表中的一行记录。当然像表名这些关于数据库表的设置也可以在 @Entity中进行设置。具体可以配置的信息官方描述如下:

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
26
27
28
29
30
31
32
33
@Entity(
// If you have more than one schema, you can tell greenDAO
// to which schema an entity belongs (pick any string as a name).
schema = "myschema",
// Flag to make an entity "active": Active entities have update,
// delete, and refresh methods.
active = true,
// Specifies the name of the table in the database.
// By default, the name is based on the entities class name.
nameInDb = "AWESOME_USERS",
// Define indexes spanning multiple columns here.
indexes = {
@Index(value = "name DESC", unique = true)
},
// Flag if the DAO should create the database table (default is true).
// Set this to false, if you have multiple entities mapping to one table,
// or the table creation is done outside of greenDAO.
createInDb = false,
// Whether an all properties constructor should be generated.
// A no-args constructor is always required.
generateConstructors = true,
// Whether getters and setters for properties should be generated if missing.
generateGettersSetters = true
)
public class Company {
...
}

@Id:被它标注的Java类属性被映射成数据库表中的id列,注意它一定是Long类型的,默认是主键,可以设置 autoincrement标识它是自增的
@Property:通过这个注解可以设置列名,默认是将属性名的写法转换成数据库中全大写的写法,如本例中通过设置的name列在表中的名字是EMPLOYEE_NAME,如果不设置property属性列名就是NAME,如果有多个单词的话,每个单词中间会有下划线分隔开,前提是属性名是标准的驼峰命名法,这样才能识别每个单词。
@NotNull:这个就简单了,就是限制这个列的插入不能为空
@Transient:这个注解的意思就是只是一个临时数据,不会被写入到数据库中,所以上面的Company类中不会出现tempUsageCount属性的getter和setter方法了。

当然还有像标识索引的 @Index、表示数据库表之间关系的 @ToOne等一些注解,又需要可以去官网了解。

grenDAO生成的类

Make Project之后,不仅会自动把你的Company类补全,还会自动生成三个类用来管理数据库,这三个类就是CompanyDao、DaoMaster和DaoSession,它们是greenDAO的核心类。生成的位置就是我们之前在Gradle插件中配置的路径:

make-project

DaoMaster:DaoMaster类是greenDao的入口点,它的内部类继承了DatabaseOpenHelper,可以生成database对象并且管理所有的DAO类
DaoSession:DaoSession管理所有可用的Dao对象,为开发者提供操作数据的权限,相对Dao类而言,它还提供了更完整的增删改查的操作方法
CompanyDao:通过这个类可以操作Entity(本例中是Company),每一个Entity会生成一个相对应的Dao类。

greenDAO的使用

通过以上的配置,基本上完成了项目的初步构建,最后再初始化数据库和greenDAO核心类就可以开始使用greenDAO相应的功能了。

初始化green DAO

官方推荐在Application类中对整个App进行一次初始化操作,所以我们新建一个类MyApplication:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyApplication extends Application {
private DaoSession daoSession;
@Override
public void onCreate() {
super.onCreate();
DevOpenHelper helper = new DevOpenHelper(this, "company-db");
Database db = helper.getWritableDb();
daoSession = new DaoMaster(db).newSession();
}
public DaoSession getDaoSession() {
return daoSession;
}
}

原理和使用SQLiteOpenHelper类似,都是通过获得Database对象来对数据库进行操作,不过greenDAO之后获取了DaoSession对象,这样就进行完了初始化操作,之后要在Activity或者Fragment中操作数据库的话只需要调用getDaoSession()方法来取得操作CompanyDao的权限就行了。

但是别忘了在AndroidManifest.xml文件中为MyApplication.java<application>加入name属性,加入之后MyApplication类会在所有组件之前完成初始化,然后就可以在任何位置通过getApplication()访问greenDAO了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.crazypudding.greendaodemo">
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
...
</application>
</manifest>

现在我们在MainActivity.java的onCreate()方法中获取CompanyDao的实例:

1
2
3
//get the company DAO
DaoSession daoSession = ((MyApplication)getApplication()).getDaoSession();
mCompanyDao = daoSession.getCompanyDao();

这一系列初始化完成,CompanyDao对象也获取到了终于可以开始我们熟悉的增删改查操作了。

这一系列方法都很熟悉了,但是greenDao为我们做了封装,所以操作起来更简单,通过CompanyDao的构造方法初始化数值传入insert()方法:

1
2
Company employee = new Company(null, mName, mSalary);
mCompanyDao.insert(employee);

案例中将这个方法封装在addEmployee()方法中,从编辑文本框获取输入的值传入Company的构造方法,然后清空编辑框,刷新ListView显示新的数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
//add a employee by a name and the salary
private void addEmployee() {
getValue();
if (mName.equals("")) {
mNameEditor.setError(getString(R.string.name_required));
return;
}
Company employee = new Company(null, mName, mSalary);
mCompanyDao.insert(employee);
cleanEditText();
refreshList();
}

效果如下:

add

这里通过点击List中的每一个item获取其id,通过id来删除数据:

1
mCompanyDao.deleteByKey(id);

效果如下:

delete

通过greenDAO的封装的update()方法,通过对应的NAME更改数据:

1
2
3
4
5
6
7
Company employee = mCompanyDao.queryBuilder().where(CompanyDao.Properties.Name.eq(mName)).build().unique();
if (employee != null) {
employee.setSalary(mSalary);
mCompanyDao.update(employee);
cleanEditText();
refreshList();
}

其实也是先通过了QueryBuilder()来找到对应的Entity,更换数据后在进行更新。

效果如下:

update

greenDAO中的查找方法是queryBuilder(),可以多次追加查找条件来准确定位数据位置:

1
Company employee = mCompanyDao.queryBuilder().where(CompanyDao.Properties.Name.eq(mName)).build().unique();

案例中使用了Android的SearchView来完成查找功能。效果如下:

query

像增删改查这一类操作其实都很简单,底层无非也就是封装了原生的SQL语句,所以具体的操作要根据需要来调用相应greenDAO的API,这些都可以在官网找到,就不一一赘述了。

总结

需要注意的一点是,在DaoMaster类中,greenDAO对于数据库的升级并没有提供很友好的操作,注释也提出了警告:

1
2
3
4
5
6
7
8
9
10
/** WARNING: Drops all table on Upgrade! Use only during development. */
public static class DevOpenHelper extends OpenHelper {
...
@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);
}
}

WARNING: Drops all table on Upgrade! Use only during development.
意思就是,在升级数据库的时候,greenDAO会删除所有的数据。从onUpgrade方法中也可以看出来,执行升级操作是先删除所以table再新建新的同名table,所以建议只在开发过程中调用DevOpenHelper。

可以试着更换一下build.gradle文件中的schemaVersion,再次运行会发现数据都不见了。如果不涉及数据库升级的话,倒没有什么问题,一旦涉及到数据库需要升级的App,都需要自己重写一个MyOenHelper类重载onUpgrade方法。常见的方法是在重载的onUpgrade方法中利用数据迁移的方式,先把旧数据迁移到临时表中,新建新的表之后再移回到新的表中,最后删除临时表达到升级数据库并且保存数据的目的。

一路实践下来,发现greenDAO对数据库的操作确实简化了很多,当然还有很多非常优秀的ORM框架,有时间再研究其他的吧。本文中案例源码地址传送门

呼啦啦...