Android ORM 框架之 greenDAO应用基础

时间:2022-02-06 15:58:42

greenDAO是时下Android最流行的一款ORM框架,其性能高,可加密,使用简洁,做android开发,如果会使用它,工作量会大大减小。其性能与其他ORM框架之比较可以查阅其官网。
目前greenDAO版本为3.1.1,greenDAO3相较于greenDAO2发生了较大的改变:可以使用注解声明schemas和实体。有两种方式使用greenDAO3,一种是使用java库的形式(greenDAO2的使用方法),一种是基于注解的形式(greenDAO3新增的)。

方式一,java库形式

1>在 src/main 目录下新建一个与 java 同层级的java-gen目录,用于存放由 greendao生成的 Bean类、DAO类、DaoMaster、DaoSession 等类:
Android ORM 框架之 greenDAO应用基础
2> 在build.gradle(app),添加 sourceSets 与依赖:

sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/java-gen']
}
}
dependencies {
compile 'org.greenrobot:greendao:3.1.0'
// This is only needed if you want to use encrypted databases
compile 'net.zetetic:android-database-sqlcipher:3.5.1'
}

3>新建java库模块:File -> New -> New Module -> Java Library
Android ORM 框架之 greenDAO应用基础
Android ORM 框架之 greenDAO应用基础
4>在java库模块build.gradledependencies中添加依赖:

dependencies {
compile 'org.greenrobot:greendao-generator:3.1.0'
}

5>编写java库模块生成的类,创建实体(表):

public class Generator {
public static void main(String[] args) throws Exception {
int version = 1;
String defaultPackage = "com.example.bean";
//创建模式对象,指定版本号和自动生成的bean对象的包名
Schema schema = new Schema(version, defaultPackage);
//指定自动生成的dao对象的包名,不指定则都DAO类生成在"com.example.bean"包中
schema.setDefaultJavaPackageDao("com.example.dao");

//添加实体
addEntity(schema);
//java-gen绝对路径
String outDir = "H:/Persion/app/src/main/java-gen";
//调用DaoGenerator().generateAll方法自动生成代码到之前创建的java-gen目录下
new DaoGenerator().generateAll(schema, outDir);

}

private static void addEntity(Schema schema) {
//添加一个实体,会自动生成实体类Persion
Entity persion = schema.addEntity("Persion");
//指定表名,如不指定,表名则为 Persion(即实体类名)
persion.setTableName("persion");
//给实体类中添加成员(即给表中添加列,并设置列的属性)
persion.addIdProperty().autoincrement();//添加Id,自增长
persion.addStringProperty("name").notNull();//添加String类型的name,不能为空
persion.addIntProperty("age");//添加Int类型的age
persion.addDoubleProperty("high");//添加Double类型的high
...
}
}

6>运行此java类,会自动在Android项目的java-gen目录下生成bean类和DAO类:
Android ORM 框架之 greenDAO应用基础
Android ORM 框架之 greenDAO应用基础
7>转到下方 操作数据库 部分

代码说明

点击阅读greenrobot

方式二,使用注解

1>在build.gradle(app)中添加插件和依赖:

buildscript {
repositories {
mavenCentral()
}

dependencies {
classpath 'org.greenrobot:greendao-gradle-plugin:3.1.0'
}

}

apply plugin: 'org.greenrobot.greendao'

dependencies {
compile 'org.greenrobot:greendao:3.1.0'
// This is only needed if you want to use encrypted databases
compile 'net.zetetic:android-database-sqlcipher:3.5.1'
compile 'org.greenrobot:greendao-generator:3.1.0'
}

2>在build.gradle(app)中插件配置:
如果不对 greenDAO进行属性配置,在下面当”Build -> Make Project”时, greendao生成的源代码路径会是: build/generated/source/greendao。对greenDAO进行相关配置,则可以指定其路径:

greendao {
schemaVersion 1
daoPackage 'com.example.greendao'
targetGenDir 'src/main/java'
}

这个greendao 可以配置的属性:

schemaVersion: 数据库schema版本号,默认为1。
daoPackage: greendao生成文件 DAOs,DaoMaster,DaoSession所在的包名,默认为源码实体所在的包名。
targetGenDir: 上面包所在的路径目录,默认为:build/generated/source/greendao。
generateTests: 设置为true可以自动生成单元测试。
targetGenDirTests:生成单元测试存储在的目录, 默认为:src/androidTest/java。

3>使用注解创建Bean类:

@Entity
public class Persion {
@Id
private Long id;
@NotNul
private String name;
private int age;
}

4>Build -> Make Project:
Make Project后,greenDAO自动为Bean类生成构造方法和每个成员的get set方法,以及前面设置的com.example.greendao包及其中的文件:
Android ORM 框架之 greenDAO应用基础
5>转到下方 操作数据库 部分

类注解

@Entity:注解实体类,标识一个表,添加的属性可以查阅依赖库中Entity注解类:

public @interface Entity {

/**
* 指定数据库中表明,默认为注解的实体类的类名
*/

String nameInDb() default "";

/**
* 对实体的索引
* <p/>
* 注意: 如果创建单列索引使用 {@link Index}
*/

Index[] indexes() default {};

/**
* 如果设置为false,表示这个实体类不在数据库中创建表。 仅仅是一个实体类。
* 注意,greendao不会在缓存中同步多个实体。
*/

boolean createInDb() default true;

/**
* 指定schema名字。
*/

String schema() default "default";

/**
* 是否可以生成update/delete/refresh方法。
*如果实体被定义成{@link ToMany} 或者 {@link ToOne} 关系,这个属性是active
*/

boolean active() default false;

/**
* 构造器是否可以生成.
*/

boolean generateConstructors() default true;

}

用法为:

@Entity(nameInDb="persion ",active=false)

注意:注解方式不支持多schema,但java库形式支持。

基础属性注解

@Id:对象id,通过设置@Id(autoincrement = true)表示自增,只有当Long/long时才有效

@Property:设置成员属性名(表的列名),如果不设置此属性表示默认是类成员名

@NotNull :表示此成员属性非空

@Transient:标识这个字段是自定义的,不会创建到数据库表中

自定义类型

@Convert:如果有自定义的成员类型,使用此注解。如果自定义类型或转换器是在此实体类外,需要设置她们为静态的。
使用实例:

@Entity
public class User {
@Id
private Long id;
//注解中converter指向类型类,columnType指向在此表中显示的字段类型
@Convert(converter = RoleConverter.class, columnType = String.class)
private Role role;

enum Role {
DEFAULT, AUTHOR, ADMIN
}

static class RoleConverter implements PropertyConverter<Role, String> {
@Override
public Role convertToEntityProperty(String databaseValue) {
return Role.valueOf(databaseValue);
}

@Override
public String convertToDatabaseValue(Role entityProperty) {
return entityProperty.name();
}
}
}

索引注解

@Index:为数据库中此列设置为索引列,通过其name参数指定此列的索引名,通过unique参数给索引添加约束 。
@Unique:给索引添加约束。

关系注解

@ToOne:声明与另一个实体的关系:一对一。参数joinProperty 指向另一个实体的 ID列,如果不设置,则默认为id主键:

@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;
}

@ToMany:声明与另外多个实体的关系:一对多。这种关系映射有三种情况:
referencedJoinProperty 参数:外键属性,指向另一实体的ID列:

@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;
}

joinProperties 参数:对于更复杂的关系,可以使用多个@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;
}

@JoinEntity:多对多的关系:

@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;
}

@Generated代码

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

Error:Execution failed for task ':app:greendao'.
> Constructor (see ExampleEntity:21) has been changed after generation.
Please either mark it with @Keep annotation instead of @Generated to keep it untouched,
or use @Generated (without hash) to allow to replace it.

当这个错误出现时,通常有两种方法解决:
1>恢复@Generated注解的代码,可以删除此段代码,下次build时,会再重新生成。
2>使用@Keep注解代替@Generated注解,它会告诉greenDAO不去触碰@Keep注解的代码。

操作数据库

在Android项目中自定义一个Application:

public class App extends Application {
/** 数据库是否加密的标识 */
public static final boolean ENCRYPTED = true;

private DaoSession daoSession;

@Override
public void onCreate() {
super.onCreate();
//创建数据库
DevOpenHelper helper = new DevOpenHelper(this, ENCRYPTED ? "notes-db-encrypted" : "notes-db");
//获取数据库读写的权限,如果进行加密调用helper.getEncryptedWritableDb("super-secret"),参数为设置的密码
Database db = ENCRYPTED ? helper.getEncryptedWritableDb("super-secret") : helper.getWritableDb();
daoSession = new DaoMaster(db).newSession();
}

public DaoSession getDaoSession() {
return daoSession;
}
}

在Manifest文件中指向自定义的Application:

    <application
android:name=".App"
...
</application>

对数据库加密

在build.gradle中添加依赖:

dependencies {
compile 'net.zetetic:android-database-sqlcipher:3.5.1'
}

如上App类,对数据库进行加密读写:

//创建数据库(上下文,数据库名)
DevOpenHelper helper = new DevOpenHelper(this,"notes-db-encrypted");
//获取读写(密码)
Database db = helper.getEncryptedWritableDb("super-secret")

如果只要求对加密的数据库进行读的操作:

//获取读(密码)
Database db = helper.getEncryptedReadableDb("super-secret")

DaoMaster和DaoSession

DaoMaster:是使用greenDAO的入口点,DaoMaster为指定的schema保存数据库对象(SQLiteDatabase)并且管理DAO类(不是对象类)。它内部的静态方法可以用来创建表或删除表,它的内部类OpenHelper 和 DevOpenHelper 是 SQLiteOpenHelper在数据库中创建schema的实现。
DaoSession:也是使用greenDAO的重要类,为指定的schema管理所有可用的DAO对象,可以使用getter方法获得DAO对象。DaoSession还提供了一些通用的持久性的方法,如插入,装载,更新,刷新和删除实体。
如果要对数据库进行操作,需要先获取相关实体类Dao对象:

daoMaster = new DaoMaster(db);
daoSession = daoMaster.newSession();
persionDao = daoSession.getPersionDao();

请注意,该数据库连接属于DaoMaster,所以多个Session指向的是同一个数据库。新的Session可以相当迅速的创建。每个Session都分配内存,通常为实体的一个Session“缓存”。
如果有对同一个数据库对象的多个请求,会有几个java对象来处理?这取决于作用域,默认(如果不对他进行配置)是这多个请求会返回同一个java对象,比如,查询USER 表的 ID 42 获取User对象,会为每一个请求返回相同的结果。并且会将其缓存在内存中。如果一个实体仍然存在内存中(greenDAO对它有弱引用),该实体将不会是从数据库值再次获得,例如,如果通过其ID加载实体,但是之前已经加载过这个实体,greenDAO就不会再次询问数据库了,它会从session缓存“马上”返回此对象,这样更快一个或两个数量级。

操作

1>获取DAO

        // get the note DAO
DaoSession daoSession = ((App) getApplication()).getDaoSession();
persionDao = daoSession.getPersionDao();

2>插入数据:id设为null,数据库会自动为其分配自增的id:

        Persion persion = new Persion(null, "liHua", 5);
persionDao.insert(persion);

3>保存数据:如果key属性不为null,会更新这个对象;如果为null,会插入这个对象:

        Persion persion = new Persion(2, "liHua", 5);
persionDao.save(persion);

4>查询数据:

Persion persion = persionDao.queryBuilder().where(PersionDao.Properties.Name.eq("LiHua")).build().unique();

unique()表示查询结果为一条数据,若数据不存在,persion为null。如果多个获取多个查询结果:

List<Persion> persionList = persionDao.queryBuilder()  
.where(PersionDao.Properties.Id.notEq(10)) //查询条件
.orderAsc(PersionDao.Properties.Id) //按首字母排列
.limit(10) //限制查询结果个数
.build().list(); //结果放进list中

5>删除数据:

persionDao.delete(persion);
//或
persionDao.deleteByKey(persion.getId());

6>事务:当执行的数据较多时,可以使用事务处理,事务的方法有insertInTx(~),saveInTx(~)等后缀为-InTx的方法,其参数为多个对象:

        Persion persion = new Persion(2, "liHua", 5);
Persion persion1 = new Persion(2, "liHua", 5);
Persion persion2 = new Persion(2, "liHua", 5);
persionDao.saveInTx(persion,persion1,persion2);

7>更多使用方法参考greendao库中AbstractDao类。

更多参考greenDAO: Android ORM for your SQLite database