greenDAO

前言

一般文章中介绍数据存储有这么几种:

  • 文件

  • SharedPreferences

  • 数据库

  • 网络

  • ContentProvider

其实,在内存存储也可以算是一种存储,比如,有些时候我们用 static 变量存储一些共享数据,只不过与上面的数据不用,上面的是持久化数据存储,但是也是一种数据存储方式,需要根据需求来决定使用哪种方式。加上内存存储构成三级缓存策略,

内存—>本地—>网络,不同级别的数据一般情况下,使用频率也是逐级降低的,这也是一种规律,因为对于移动端来说,提高用户体验的其中一点也是加载速度快,那么当然从内存中拿出数据来效率更高,但是不可能所有数据都能放在内存中,毕竟内存有限,而且有些数据也是需要长久记录下来的,设备重启或者应用重启后,保存在进程中的数据也就释放掉了。

下面说一说持久化存储的特点:

文件

文件,这种类型的数据一般都是文件格式,比如图片,word,text等但是大小和数量也不能太多,毕竟移动设备的存储空间有限。另外这种数据一般官方的建议是存储在外部私有存储空间,因为内部存储空间有限,对于外部存储空间,如果存在公有空间,则删除应用时文件不会被删除,如果保存在外部私有空间,删除应用时数据也会一并被删除,这样也能够减少一些用户喷来的口水!

SP

对于 SP 来说,一般也比较简单与方便,常常我们都是拿 SP 工具类来进行数据存储的,对于 SP 存储的数据,也是少量的,但是同为本地数据存储,有哪些特点,换句话说,在哪些场景下使用 SP ?

  • 只支持存储Java基础数据类型(Boolean、Int、Float、String、Long等)不支持自定义数据类型。

  • 本质是一种Map,通过键值对的形式进行数据存储。

  • 不支持查找功能。

从这几个特点中可以看出,实际上用 SP 存储一些基本的数据类型,虽然用你也可以将数据放在本地文件中,但是没有 SP 方便,SP 可以根据 key 直接取出。同样,SP 是放在内存存储空间的,数据量也是相对较小的。

数据库

很明显,数据库就是要解决 SP 不能存储自定义对象的烦恼。SQL 数据库叫做“结构化查询语言”,也就是说是用来存储结构化数据的,这里可以理解为对象,所以数据库一般是关系型数据库。对于数据存储的数据也可以存储到文件中,只不过我们需要将对象数据序列化,存储到文件中,然后读取的时候在反序列化,这对于开发者来说,很麻烦数据库能够很好地解决这个问题。开发者只管存储就行,不需要关心序列化和反序列化的过程。

对于移动端的数据库,一般都是轻量级的,像安卓端的 Sqlite,毕竟是移动端,不能搞得像 PC 上那么大。对于移动端的数据库一般也有以下几个特点

  • 自定义数据类型,一般是对象。

  • 存储在本地的内部存储空间,数据也不会太大

  • 支持查找。

数据库现在也有很多优秀的开源框架可以使用,不同的框架也有不同的特点,那么对于开发者来说如何选择?其实无非也就这么几点:

  • 满足性能要求,比如速度快,数据量大时,稳定

  • 简单易操作,能不写代码最好~~

  • 轻量级,引入的包很大的话,也会对APK的大小有影响,以及初始化的速度等

greenDao 介绍

为啥本篇主要来说一说 greenDao 呢?原因很简答,之前没用过,当然来尝试一下了。

原生的 sqlite 就不说了,应用大家都不会用原生的数据库,如果原生的数据库好用的话,估计也不会出现各种开源框架了!

对于数据库,特别是移动端的,涉及的操作不会很多,也就是增删改查,不熟悉的可以在 w3cschool 上复习一下。

SELECT - 从数据库表中获取数据
UPDATE - 更新数据库表中的数据
DELETE - 从数据库表中删除数据
INSERT INTO - 向数据库表中插入数据

大神们喜欢使用原始的 SQL 语句,对于我这种小菜鸟,还是喜欢更简单的框架,所以对于我来说,好的数据库框架,一般是越无脑越好,性能越高越好,怎么平衡它?看你自己了。

之前使用 LiteOrm,这个框架比较简单,有兴趣的可以看看这篇文章-Android ORM框架 LiteOrm使用。至于性能和 greenDAO 对于,没有亲子测过(主要是不会测~),可以看看 Android数据库框架:greenDAO vs LiteOrm 这篇文章。

greenDAO,官网链接 http://greenrobot.org/greendao 。

LiteOrm,官网链接 http://litesuits.com 。

对月 greenDAO,官方是这么说它的特点的:

(1) Rock solid: greenDAO has been around since 2011 and is used by countless famous apps 

非常稳定,自从 2011 年开始就被无数有名气的 App 使用

(2) Super simple: concise and straight-forward API, in V3 with annotations
超级简单(倒是觉得没有 LiteOrm简单),有简单的使用 API,在 v3 版本中引入注解

(3) Small: The library is <150K and it's just plain Java jar (no CPU dependent native parts)
很小,库是于 150 K 的 jar 包(不含有依赖 CPU 的本地代码部分,指的是 so库)

(4) Fast: Probably the fastest ORM for Android, driven by intelligent code generation
速度快,可能是 android 上最快的 ORM,可以自动生成代码

(5) Safe and expressive query API: QueryBuilder uses property constants to avoid typos
安全和容易记忆的查询 API:QueryBuilder 使用合适的常量来避免拼写错误

(6) Powerful joins: query across entities and even chain joins for complex relations
有强大的联合查询:支持实体间的交叉查询,以及支持复杂的链式查询

(7) Flexible property types: use custom classes or enums to represent data in your entity
拥有更灵活的类型,可以使用类类型或者枚举,来表示你的实体类

(8) Encryption: supports SQLCipher encrypted databases
可以加密:支持 SQLCipher 加密数据库

好,看它吹完牛和我唠叨完,我们还是实际使用一下,来感受感受!

greenDAO 使用

官方也是给出了例子

配置

工程目录下 build.gradle 添加如下配置:

buildscript {

repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.0'
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2' // add plugin

}
}

在 app 的 build.gradle 下配置:

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

android {
...
}

dependencies {
...
implementation 'org.greenrobot:greendao:3.2.2'

}

新建实体类

@Entity
public class Note {
@Id
private Long id;
private String name;
private int age;

//下面省去了 setter/getter
}

常用注解:

  1. @Entity – 实体注解
public @interface Entity {

/**
* 在数据库中表的名称,默认为实体的类名 
*/
String nameInDb() default "";

/**
*  定义索引,可以跨越多个列(默认为实体类成员变量的个数) 
*/
Index[] indexes() default {};

/**
* 标记创建数据库表 
* 如果是有多个实体都关联这个表,可以把多余的实体里面设置为false避免重复创建(默认是true)
*/
boolean createInDb() default true;

/**
*  告知GreenDao当前实体属于哪个schema 
*/
String schema() default "default";

/**
*  实体活动状体标志位(默认为false) 
*  若设置为true,实体有更新、删除和刷新方法 
*/
boolean active() default false;
}

  1. @Generated 派生属性

@Generated 这个是build后greendao自动生成的,这个注解理解为防止重复,每一块代码生成后会加个hash作为标记。 官方不建议你去碰这些代码,改动会导致里面代码与hash值不符。

@Generated(hash = 1272611929)
public Note() {
}

public Note(Long id) {
this.id = id;
}

@Generated(hash = 1686394253)
public Note(Long id, @NotNull String text, String comment, java.util.Date date, NoteType type) {
this.id = id;
this.text = text;
this.comment = comment;
this.date = date;
this.type = type;
}
  1. @NotNull – 设置表中当前列的值不可为空

  2. @Convert– 指定自定义类型,将自定义的数据类型,转换为数据库中所存储列的值的类型

@Convert(converter = NoteTypeConverter.class, columnType = String.class)
private NoteType type;
  1. @Id– 主键 Long型,可以通过@Id(autoincrement = true)设置自增长。通过这个注解标记的字段必须是Long,数据库中表示它就是主键,并且默认是自增的。

@Id
private Long id;

6.索引属性-两种方法

(1)@Index– 使用@Index作为一个属性来创建一个索引;

@Index 通过这个字段建立索引

@Unique 添加唯一约束,上面的括号里unique=true作用相同

@Entity
public class User {
@Id private Long id;
@Index(unique = true)
private String name;
}

@Entity
public class User {
@Id private Long id;
@Unique private String name;
}

(2)定义多列索引(@link Entity#indexes())

通过逗号间隔创建表的属性索引,例如 “propertyA,propertyB,propertyC” , 若要指定排序, 需在列明以后添加 ASC(升序) 或者DESC(降序) , 例如 “propertyA DESC, propertyB ASC” ,只有实体类中使用 {@link Entity#indexes()} 才可设置


@Entity(indexes = {
@Index(value = "text, date DESC", unique = true)
})
public class Note {
...
}
  1. 关系注解 (1)一对一 ```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; }

@ToOne 是将自己的一个属性与另一个表建立关联,如果不设置`joinProperty`,在`Customer`表中会自动创建外键,值是自动增加的。

(2)一对多

```java
public @interface ToMany {  
/** 
* 目标实体持有源实体的名称 
* 如果没设置,否则有 {@link JoinProperty} or {@link JoinEntity} 指定 
*/  
String referencedJoinProperty() default "";  

/** 
* 将源表列索引->目标列 
* 如果没设置,否则有 {@link JoinProperty} or {@link JoinEntity} 指定 
*/  
JoinProperty[] joinProperties() default {};  
}

上面的可能不是很好理解,其中也涉及了其他注解,@JoinProperty@JoinEntity ,意思是如果设置了 referencedJoinProperty 作为另一个表的外键,可以不设置 @JoinProperty@JoinEntity,否则就要设置这两个注解。举个例子。

方式一:

@Entity
public class User {
@Id private Long id;

@ToMany(referencedJoinProperty = "ownerId")
private List<Site> ownedSites;
}

@Entity
public class Site {
@Id private Long id;
private long ownerId;
}

一个 User 中可能有多个 Site ,那么对于 Site 和 User 联系起来是通过 ownerId

方式二:

如果不指定 ownerId,通过 @JoinProperty

@Entity
public class User {
@Id private Long id;
@Unique private String authorTag;

@ToMany(joinProperties = {
@JoinProperty(name = "authorTag", referencedName = "ownerTag")
})
private List<Site> ownedSites;
}

@Entity
public class Site {
@Id private Long id;
@NotNull private String ownerTag;
}

从上面看到通过 User 中的 authorTag 和 Site 中的 ownerTag 联系起来。

方式三:

(3) 多对多


@Entity
public class Site {
@Id private Long id;

@ToMany
@JoinEntity(
entity = JoinSiteToUser.class,
sourceProperty = "siteId",
targetProperty = "userId"
)
private List<User> authors;
}

@Entity
public class JoinSiteToUser {
@Id private Long id;
private Long siteId;
private Long userId;
}

@Entity
public class User {
@Id private Long id;
}

单独定义一个关系类 JoinSiteToUser ,通过该类将 Site 和 User 联系起来。

  1. @OrderBy– 指定排序

一般也是使用在 @toMany 中.

public @interface OrderBy {  
/** 
* 通过逗号间隔创建表的属性索引,例如 “propertyA,propertyB,propertyC” 
* 若要指定排序, 需在列明以后添加 ASC(升序) 或者DESC(降序) ,  例如 "propertyA DESC, propertyB ASC" 
*  默认按升序排序 
*  若不设置默认根据主键排序 
*/  
String value() default "";  
}

9.@Property– 设置数据库中的列名,默认是的使用字段名

举例:@Property (nameInDb=”name”)

public @interface Property {  
/** 
* 默认是的使用字段名 
*/  
String nameInDb() default "";  
}
  1. @Keep– 注解的代码段在GreenDao下次运行时保持不变
(1)注解实体类:默认禁止修改此类
(2)注解其他代码段,默认禁止修改注解的代码段
  1. @Transient– 添加次标记之后不会生成数据库表的列

初始化

编写完实体类之后,进行编译,会生成 DaoMaster,DaoSession,和一个实体 DAO类,如实体类是 User,则对应的是 UseDao 类。 然后在 Application 中初始化,看到代码中加了加密的设置。greenDao 允许加密, 若要使用加密,需要在 build.gradle 中加入以下依赖。

// This is only needed if you want to use encrypted databases
implementation 'net.zetetic:android-database-sqlcipher:3.5.6'

Application 中初始化

public class MyApplication extends Application {

/** A flag to show how easily you can switch from standard SQLite to the encrypted SQLCipher. */
public static final boolean ENCRYPTED = false;

private DaoSession daoSession;

@Override
public void onCreate() {
super.onCreate();

DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, ENCRYPTED ? "notes-db-encrypted" : "notes-db");
Database db = ENCRYPTED ? helper.getEncryptedWritableDb("super-secret") : helper.getWritableDb();
daoSession = new DaoMaster(db).newSession();
}

public DaoSession getDaoSession() {
return daoSession;
}
}

DaoMaster:使用greenDAO的切入点。 DaoMaster保存数据库对象(SQLiteDatabase)并管理特定模式的DAO类(而不是对象)。 它有静态方法来创建表或删除它们。 它的内部类OpenHelper和DevOpenHelper都是SQLiteOpenHelper的实现,用来在SQLite数据库中创建模式。

DaoSession:管理特定模式的所有可用DAO对象,你可以使用其中一个的getter方法获取DAO对象。 DaoSession还为实体提供了一些通用的持久性方法,如插入,加载,更新,刷新和删除。 最后,DaoSession对象也跟踪identity scope。

DAO:数据访问对象(DAO),用于实体的持久化和查询。 对于每个实体,greenDAO会生成一个DAO。 它比DaoSession拥有更多的持久化方法,例如:count,loadAll和insertInTx。

greenDAO Gradle插件


greendao {
targetGenDir 'src/main/java'
daoPackage 'com.XXXX.dao.db'
}

schemaVersion:数据库模式的当前版本。 这由* OpenHelpers类用于在模式版本之间迁移。 如果更改实体/数据库模式,则必须增加此值。 默认值为1。

daoPackage:生成的DAO,DaoMaster和DaoSession的包名称。 默认为源实体的包名称。

targetGenDir:生成的源码应存储在的位置。 默认为生成目录(build / generated / source / greendao)中生成的源文件夹。

generateTests:设置为true以自动生成单元测试。

targetGenDirTests:生成的单元测试应存储在的基本目录。 默认为src / androidTest / java。

增删改查

  1. 查询
DaoSession daoSession = ((MyApplication) getApplication()).getDaoSession();
userDao = daoSession.getUserDao();

...

QueryBuilder<User> qb = userDao.queryBuilder();
qb.where(Properties.FirstName.eq("Joe"),// 查询条件
qb.or(Properties.YearOfBirth.gt(1970), // 或 条件
qb.and(Properties.YearOfBirth.eq(1970), Properties.MonthOfBirth.ge(10)))); // 与 条件
List<User> youngJoes = qb.list();
  1. 添加

User user = new User();
user.setName("Nick");
user.setAge(25);
user.setId(12);
userDao.insert(user);
  1. 更新
user = userLists.get(10);
user.setName(Ralf);
user.setAge(29);
userDao.update(user);
  1. 删除
Long userId = user.getId();
userDao.deleteByKey(userId);

以上就是 greenDao 的基本介绍,数据库也是很复杂的一部分,但是对于移动端来说,一般会一些基本的操作大部分场景都能够满足,如果需要更多操作可以参考官方的说明文档 API.

另外需要关注的是,greenrobot 又出了 ObjectBox,使用更加单,也是 greenrobot 大力推荐的,后面会做一下介绍。下一篇会对 greenDao 进行封装使用,然后自己设计稍微复杂的表进行实践一下,也不能总是对着一张表增删改查,要不有点那啥。。。

好,就到这里,敬请期待!

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦