文章目录
1.简介
上一篇博客中,我们介绍了集成和使用都比较方便的数据持久层框架——Litepal,而在这篇博客中,我将介绍另一个当前更为主流的框架,也就是greenDao。
事实上,之前学习Android的时候在做持久化这一块时,使用的更多是原生的Sqlite或者Litepal,对于greenDao,一直都是只听说过而没有深入使用,今天趁着框架学习的兴头,就简单学习了一下greenDao,并且将学习过程记录下来,供各位读者参考。
2.特性
为什么要使用greenDao?通过GreenDao,我们可以更快速的操作数据库,我们可以使用简单的面相对象的API来存储,更新,删除和查询Java对象。
事实上,greenDao有些类似于Litepal,不过集成和使用要相对比Litepal要复杂些,正因如此,它的功能也更加强大。它的主要特性如下:
-
高性能,下面是官方给出的关于GreenDao,OrmLite和ActiveAndroid三种ORM解决方案的数据统计图:
-
易于使用的强大API,涵盖关系和连接;
-
最小的内存消耗;
-
占用内存小(<100KB)以保持较低的构建时间并避免65k方法限制;
-
数据库加密:greenDAO支持SQLCipher,以确保用户的数据安全;
相对于Litepal来说,greenDao可能更适用于较正式的开发场景,所以我们同样需要认真地学习该框架爱。
3.核心
在正式使用greenDao之前,我们应该先简单了解一下greenDao这个框架内部的核心,这样对于我们使用greenDao大有裨益
GreenDao的核心类有三个:分别是DaoMaster,DaoSession,XyzDao,这三个类都会自动创建,无需自己编写创建!不要惊讶,在后面的演示中,你将会体验到这个神奇的功能。
- DaoMaster:DaoMaster保存数据库对象(SQLiteDatabase)并管理特定模式的DAO类(而不是对象)。它有静态方法来创建表或删除它们。它的内部类OpenHelper和DevOpenHelper是SQLiteOpenHelper实现,它们在SQLite数据库中创建模式。
- DaoSession:管理特定模式的所有可用DAO对象,您可以使用其中一个getter方法获取该对象。DaoSession还提供了一些通用的持久性方法,如实体的插入,加载,更新,刷新和删除。
- XyzDao:数据访问对象(DAO)持久存在并查询实体。对于每个实体,greenDAO生成DAO。它具有比DaoSession更多的持久性方法,例如:count,loadAll和insertInTx。
- XyzEntity :可持久化对象。通常,实体对象代表一个数据库行使用标准 Java 属性(如一个POJO 或 JavaBean )。
我们简单了解了一下核心,当然只是比较浅显的部分,不过对于我们的简单使用也已然足够。
4.演示
4.1 集成
greenDao的集成相较于Litepal要多出一个步骤,即集成插件,首先我们还是在module下的build.gradle先导入greenDao,代码如下:
buildscript {
repositories {
jcenter()
mavenCentral() // add repository
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0' // add plugin
}
}
依赖导入后,接下来在项目下的build.gradle(注意和导入依赖的同名文件作区分)导入插件相关,代码如下:
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin
dependencies {
implementation 'org.greenrobot:greendao:3.3.0' // add library
}
搞定这两项后,重新sync一下,greenDao就集成到你的项目里了。
4.2 配置
跟Litepal类似的,grennDao在集成完毕后,同样需要一些简单的配置,才能确保可以正常使用。
在module下的build.gradle先对greenDao进行配置,主要是在android闭包下配置grenndao闭包,这里贴出全部的代码,方便读者参考,代码如下:
apply plugin: 'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.androidframelearn.dao_greendao"
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
greendao {
schemaVersion 1 //当前数据库版本
daoPackage 'com.androidframelearn.dao_greendao' // dao的包名,包名默认是entity所在的包
targetGenDir 'src/main/java' // 生成数据库文件的目录
// generateTests false //设置为true以自动生成单元测试。
// targetGenDirTests 'src/main/java' //应存储生成的单元测试的基本目录。默认为 src/androidTest/java。
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'org.greenrobot:greendao:3.3.0' // add library
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
其中,对grenndao闭包中的属性做一些解释:
- schemaVersion:指定数据库schema版本号,迁移、更新等操作会用到;
- daoPackage :dao的包名,包名默认是entity所在的包;
- targetGenDir:生成数据库文件的目录;
- generateTests:设置是否开启单元测试;
- targetGenDirTests:生成单元测试文件的目录;
一般来说,我们只是想简单使用的话,配置前三项即可,后两项在这里注释掉了,读者可以根据需求进行配置。
与上一小节相同,sync一下,即将greenDao配置完成。
4.3 创建数据库
跟Litepal一样,greenDao同样采取了对象关系映射(ORM)的模式,即我们想使用greenDao,同样需要创建一个实体类。为了跟上个项目相对比,我们同样新建一个名为Book的实体类。之前有提到过,要想让greenDao为我们自动三个类,这里就要用greenDao要求的写法来编写实体类,即使用@Entity
将该实体类标识为存储的数据类。在该类中,我们只需要定义属性,以及相应的注解,不需要编写相应的get/set
方法,代码如下:
@Entity
public class Book {
@Id(autoincrement = true) //设置自增长
private long id;
private String author;
private double price;
private int pages;
@Unique//设置唯一性
private String name;
}
PS:用注解开发将类注入的形式有点像Java中的Spring系列框架,如果比较了解的读者应该能明白这个意思
在进行下一步之前,先来简单介绍一下greenDao中主要的注解:
@Entity
:表示这个实体类一会会在数据库中生成对应的表;@Id
:表示该字段是id,注意该字段的数据类型为包装类型Long;@Property
:则表示该属性将作为表的一个字段,其中nameInDb看名字就知道这个属性在数据库中对应的数据名称;@Transient
:该注解表示这个属性将不会作为数据表中的一个字段;@NotNull
:表示该字段不可以为空;@Unique
:表示该字段唯一;
有兴趣的读者可以自行尝试这些注解,这里只使用比较基础的注解作为演示。
当我们编写好了实体类之后,若使用的IDE为Android Studio,只需按下 Ctrl + F9或者选择菜单中Build -> Make Project,等待几秒钟,神奇的事情就发生了!刚刚我们编写好的Book实体类自动产生了get/set
方法,并且发生了一些变化,俨然变成了这样:
package com.androidframelearn.dao_greendao;
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Index;
import org.greenrobot.greendao.annotation.Generated;
@Entity
public class Book {
@Id(autoincrement = true) //设置自增长
private long id;
private String author;
private double price;
private int pages;
@Index(unique = true)//设置唯一性
private String name;
@Generated(hash = 368601420)
public Book(long id, String author, double price, int pages, String name) {
this.id = id;
this.author = author;
this.price = price;
this.pages = pages;
this.name = name;
}
@Generated(hash = 1839243756)
public Book() {
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public String getAuthor() {
return this.author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return this.price;
}
public void setPrice(double price) {
this.price = price;
}
public int getPages() {
return this.pages;
}
public void setPages(int pages) {
this.pages = pages;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
除此之外,greenDao也确确实实给我们自动生成了三个类,如下图中的红框标出:
如果做到这一步都没有什么问题的话,就说明Book这个实体类已经“托管”到greenDao中,我们可以对它进行相应的CRUD操作了。但在这之前,还需要进行以下初始化。
4.4 初始化
在使用greenDao对实体类Book进行CRUD操作之前,我们需要对greenDao进行一个简单的初始化。新建一个Myapplication集成Application,在应用被创建的同时执行相应的初始化逻辑,并且使用单例模式封装Myapplication,供外部调用,代码如下:
package com.androidframelearn.dao_greendao;
import android.app.Application;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
public class MyApplication extends Application {
private DaoMaster.DevOpenHelper mHelper;
private SQLiteDatabase db;
private DaoMaster mDaoMaster;
private DaoSession mDaoSession;
public static MyApplication instances;
@Override
public void onCreate() {
super.onCreate();
// 给单例赋值
instances = this;
// 初始化GreenDao
initDatabase();
}
/**
* 初始化greenDAO
*/
private void initDatabase() {
/**通过 DaoMaster 的内部类 DevOpenHelper,你可以得到一个便利的 SQLiteOpenHelper 对象。
可能你已经注意到了,你并不需要去编写「CREATE TABLE」这样的 SQL 语句,因为 greenDAO已经帮你做了。
注意:默认的 DaoMaster.DevOpenHelper 会在数据库升级时,删除所有的表,意味着这将导致数据的丢失。
所以,在正式的项目中,你还应该做一层封装,来实现数据库的安全升级。**/
mHelper = new DaoMaster.DevOpenHelper(this, "androidframelearn.db", null);
db = mHelper.getWritableDatabase();
// 注意:该数据库连接属于 DaoMaster,所以多个 Session 指的是相同的数据库连接。
mDaoMaster = new DaoMaster(db);
mDaoSession = mDaoMaster.newSession();
Log.i("GreenDao","数据库初始化成功!");
}
public DaoSession getDaoSession() {
return mDaoSession;
}
public static MyApplication getInstances(){
return instances;
}
}
当然,若自定义了Application,记得在清单文件里配置一下android:name
属性,不然默认不会调用,清单文件的代码如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.androidframelearn.dao_greendao">
<application
android:name=".MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
与此同时,跟Litepal一样的,我们在使用greenDao之前对一些必要的实例进行一些初始化,修改MainActivity,通过刚刚定义的MyApplicaiton获取到Daosession的实例,再使用Daosession对象获取到BookDao的实例,代码如下:
private void createDBbyGreenDao() {
btn_db_create.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 获取daosession对象
mDaoSession = MyApplication.getInstances().getDaoSession();
mBookDao = mDaoSession.getBookDao();
Log.i(TAG,"创建数据库成功");
}
});
}
接下来,我们正式开始相关数据操作的演示。
为了演示方便和有对比性,这里使用的布局和上一篇讲解Litepal中activity_main.xml的布局是一样的,布局代码这里就不再贴出了,读者可以参考上篇博客的布局,也可以自己编写,反正怎么方便怎么来吧。
4.5 升级数据库
在开始讲解CRUD操作之前,我们简单提及一下数据库相关的方法。
一般来说,如果只是简单地进行升级,步骤大抵如下:
- 在module的build.gradle文件中升级版本号,即
schemaVersion
- 修改实体类
- 重新编译项目即可
而稍微复杂一些的数据库升级情况,例如数据库迁移等,greenDao的OpenHelper
下有个 onUpgrade(Database db, int oldVersion, int newVersion)
方法,当设置的数据库版本改变时,在数据库初始化的时候就会回调到这个方法,我们可以通过继承OpenHelper重写onUpgrade
方法来实现数据库更新操作,步骤大抵如下:
- 创建临时表TMP_,复制原来的数据库到临时表中;
- 删除之前的原表;
- 创建新表;
- 将临时表中的数据复制到新表中,最后将TMP_表删除掉;
升级数据库的操作建议参看greenDao上的官方文档(作者本地被墙,没法上网查看),这里不再赘述。
4.6 加密数据库
开发中对于存储于数据库中的敏感数据,我们可以通过对数据库加密来进行保护。greenDao可以通过SQLCipher来进行加密处理。下面我们简单讲解下加密过程:
步骤:
- 导入加密库文件依赖:
implementation 'net.zetetic:android-database-sqlcipher:3.5.6'
- 修改DaoSession的生成方式:
// MyDaoMaster helper = new MyDaoMaster(this, "androidframelearn.db"); //数据库升级写法
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "androidframelearn.db");
//SQLiteDatabase db = helper.getWritableDatabase(); //不加密的写法
Database db = helper.getEncryptedWritableDb("aserbao"); //数据库加密密码为“aserbao"的写法
DaoMaster daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
加密数据库的相关api同样可以参看greenDao上的官方文档,这里不再赘述。
4.7 插入数据
插入数据的操作跟Litepal类似:获取实体类对象,设置其属性,然后再通过greenDao中xyzDao提供的insert()
方法即可完成数据插入,代码如下:
private void insertDatabyGreenDao() {
btn_db_insert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mDaoSession != null){
Book book = new Book();
book.setName("The Da Vinci Code");
book.setAuthor("Dan Brown");
book.setPages(454);
book.setPrice(16.96);
mBookDao.insert(book);
Log.i(TAG,"插入数据成功");
}else {
Log.i(TAG,"插入数据失败");
}
}
});
}
插入数据后,我们点击查询数据(查询业务将在下一节提到),然后查看控制台,就可以查看到刚刚插入的数据,如图所示:
4.8 查询数据
查询数据的操作同样跟Litepal类似,只不过获取数据的方法变成了xyzDao下的loadAll()
,代码如下:
private void queryDatabyGreenDao() {
btn_db_query.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mDaoSession != null){
List<Book> books = mBookDao.loadAll();
for (Book book : books){
Log.i(TAG,"查询数据成功");
Log.d("MainActivity","书名是"+book.getName());
Log.d("MainActivity","书的作者是"+book.getAuthor());
Log.d("MainActivity","书的页数是"+book.getPages());
Log.d("MainActivity","书的价格是"+book.getPrice());
}
}else {
Log.i(TAG,"查询数据失败");
}
}
});
}
相关的运行结果上一小节以贴出,这里不再重复。
4.9 更新数据
更新数据的操作同样和Litepal类似,只不过获取数据的方法变成了xyzDao下的update()
,代码如下:
btn_db_update.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mDaoSession != null){
Book book = new Book();
book.setId(0);
book.setName("The Da Vinci Code");
book.setAuthor("Dan Brown");
book.setPages(100);
book.setPrice(16.96);
mBookDao.update(book);
Log.i(TAG,"更新数据成功");
}else {
Log.i(TAG,"更新数据失败");
}
}
});
更新数据后,我们再对数据进行查询,发现数据已有更新,如图所示:
4.10 删除数据
为了方便演示,删除数据直接使用了将所有数据都删除的deleteAll
,代码如下:
private void deleteDatabyGreenDao() {
btn_db_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mBookDao.deleteAll();
Log.i(TAG,"删除数据失败");
}
});
}
当然,有关于CRUD的操作事实上还有很多的api,这里仅仅做一些简单的示范,详细的就如同前面所说的:参考官方文档,没有任何教程要比官方文档详细了。