Android端ORM框架调研与应用

ORM是什么

ORM 表示全称为对象关系映射(Object Relational Mapping)。

O 可以理解为java对象 

R 可以理解为关系型数据库(sqllite) 

M 可以理解为从java对象到关系型数据库建立映射关系的过程

是一种为了解决面向对象与关系型数据库存在不匹配现象的技术,orm通过描述对象和数据库之间映射的元数据,将程序中的对象自动持久化到关系型数据库中。

为什么使用ORM

存在即合理

ORM框架的存在大大提高了开发效率,ORM框架会自动为我们生成对象到数据库建立映射关系的逻辑,不需要我们在重复的进行代码编写。如下代码ORM框架会为我们自动生成,是不是很方便。

final Cursor _cursor = __db.query(_statement);

try {

final int _cursorIndexOfId = _cursor.getColumnIndexOrThrow("id");

final int _cursorIndexOfName = _cursor.getColumnIndexOrThrow("name");

final Bean _result;

if(_cursor.moveToFirst()) {

_result = new Bean();

_result.id = _cursor.getInt(_cursorIndexOfId);

_result.name = _cursor.getString(_cursorIndexOfName);

} else {

_result = null;

}

return _result;

ORM框架选型

没有最好,只有最合适

目前业内有很多ORM框架如ORMLite, Sugar ORM, Freezer, DBFlow, GreenDAO,Room等,如何在众多中的框架中进行抉择呢?我们主要从性能和使用方式进行对比,选择最合适的。

ORM框架性能对比

性能对比主要是测试ORM框架增删改查的耗时。这里github上的一个开源项目已经明确列出了各个ORM框架的性能对比数据。 详情地址:https://github.com/AlexeyZatsepin/Android-ORM-benchmark

通过数据我们看出GreenDAO, Room框架的整体性能是很出色的。所以最终我们选择这两个框架中进行选择。

使用方式对比

对于现有的ORM框架在使用方式上其实大同小异,我们在table映射,数据库初始化,增删改查操作,数据库升级和与Rxjava配合使用,这几点进行对比。

GreenDao框架

GreenDao框架目前存在两种使用方式。

  1. 一种是通过引入GreenDao的Gradle插件来生成所需要的类,

  2. 一种是新建一个java工程通过GreenDao的 generator 来构造所需要的类。这种方式适合如下场景:你的应用内有多个数据库( multiple schemas)的话,那只能使用第二种方式,新建个java工程,然后编写数据库结构代码,运行工程后生成我们需要的操作数据库代码,最后拷贝到项目工程中。

In most cases, we recommend to use the new Gradle plugin instead. However, some advanced features are currently only supported by the generator. For example multiple schemas.

本文主要讲解第一种使用方式。

框架引入

工程中引入GreenDao插件,同时我们还需要指定数据库版本号,生成代码的包名和对应的代码路径。

// In your root build.gradle file:

buildscript {

repositories {

jcenter()

mavenCentral()

// add repository

}

dependencies {

classpath 'com.android.tools.build:gradle:3.1.1'

classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'

// add plugin

}

}

// In your app projects build.gradle file:

apply plugin: 'com.android.application'

apply plugin: 'org.greenrobot.greendao'

// apply plugin

greendao {

schemaVersion 1

//数据库版本号 daoPackage 'com.speedystone.greendaodemo.db' // 设置DaoMaster、DaoSession、Dao 包名 targetGenDir 'src/main/java' //设置DaoMaster、DaoSession、Dao目录

}

dependencies {

implementation 'org.greenrobot:greendao:3.2.2'

// add library }

table映射

Java Bean对象与数据库中的table结构建立映射关系,需要我们在对应的bean上添加@Entity即可

@Entity

public class User {

@Id(autoincrement = true)

Long id;

@Property (nameInDb="age")

int age;

@Property (nameInDb="name")

String name;

@Property (nameInDb="address")

String address;

}

数据库初始化

我们可以构建一个DaoSession的全局单例,可以通过操作DaoSession获取每个表的对应的Dao操作对象,进行增删改查操作。

/**

* 初始化GreenDao,直接在Application中进行初始化操作

*/

private void initGreenDao() {

DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "test.db");

SQLiteDatabase db = helper.getWritableDatabase();

DaoMaster daoMaster = new DaoMaster(db);

daoSession = daoMaster.newSession();

}

private DaoSession daoSession;

public DaoSession getDaoSession() {

return daoSession;

}

增删改查操作

  1. 添加数据

daoSession.insert(user); //插入数据 daoSession.insertOrReplace(user); //插入或替换

2.删除数据

daoSession.delete(user);

daoSession.deleteAll(User.class);

3.修改数据

daoSession.update(user);

4.查询数据 

方式1:loadAll():查询所有数据。

方式2:queryRaw():根据条件查询。

方式3:queryBuilder() : 自定义复杂的查询。

List<User> users = daoSession.loadAll(User.class); //方式1 List<User> users = daoSession.queryRaw(User.class, " where id = ?", s); //方式2

QueryBuilder<User> qb = daoSession.queryBuilder(User.class);

List<User> list = qb.list();

// 方式3

方式3的使用方式比较繁琐,queryBuilder定义了很多sql语句对应的方法如 eq(),like(),between()等等。

数据库升级

GreenDao的升级思路:

  1. 创建临时表TMP_,复制原来的数据库到临时表中;

  2. 删除之前的原表;

  3. 创建新表;

  4. 将临时表中的数据复制到新表中,最后将TMP_表删除掉;GreenDao数据库升级步骤较为复杂繁琐,并且效率比较低。

与Rxjava配合使用

GreenDao支持与Rxjava一起使用每个Dao都有rx()方法返回Observable进行链式调用和线程切换操作。

GreenDaoUtil.getDaoSession(activity)

.getUserDao()

.queryBuilder()

.where(UserDao.Properties.Age.gt(30))

.rx()

.list()

.observeOn(AndroidSchedulers.mainThread())

.subscribe(

new Action1<List<User>>() {

@Override

public void call(List<User> peopleBeen) {

}

},

new Action1<Throwable>() {

@Override

public void call(Throwable throwable) {

throwable.printStackTrace();

}

}

);

ROOM框架

ROOM框架是Google在2017年Google IO大会上推出的官方数据库框架。ROOM框架基于APT(Annotation Processing Tool),我们通过使用注解然后重新build一下Moudle工程,就会自动生成我们需要的代码。

框架引入

直接在对应的Moudle添加依赖即可

dependencies {

api "android.arch.persistence.room:runtime:$1.1.1"

annotationProcessor "android.arch.persistence.room:compiler:$1.1.1"

}

table映射

使用Entity注解,在每个属性中需要通过ColumnInfo制定数据库中对应的字段名称

@Entity(indices = {@Index(value = {"first_name", "last_name"},

unique = true)})

class User {

@PrimaryKey

public int id;

@ColumnInfo(name = "first_name")

public String firstName;

@ColumnInfo(name = "last_name")

public String lastName;

}

数据库初始化

建立数据库映射关系使用Database注解,指定该数据库中含有的table和当前数据库版本号

@Database(entities = {User.class}, version = 1)

public abstract class AppDatabase extends RoomDatabase {

private static volatile AppDatabase INSTANCE;

public abstract UserDao userDao();

public static AppDatabase getInstance(Context context) {

if (INSTANCE == null) {

synchronized (AppDatabase.class) {

if (INSTANCE == null) {

INSTANCE = Room.databaseBuilder(context.getApplicationContext(),

AppDatabase.class, "sample.db")

.build();

}

}

}

return INSTANCE;

}

}

数据增删改查操作

对每个数据表的操作,我们均需要定义对应的Dao,并且支持响应式查询。添加方式如下。

@Dao

public interface UserDao {

@Insert(onConflict = OnConflictStrategy.REPLACE)

public void insertUsers(User... users);

@Query("select * from user")

List<User> getAll();

}

数据库升级

每次升级数据库添加Migration即可,实现migrate方法添加数据库对应的修改逻辑

Room.databaseBuilder(getApplicationContext(), MyDb.class, "database-name")

.addMigrations(MIGRATION_1_2, MIGRATION_2_3).build();

static final Migration MIGRATION_1_2 = new Migration(1, 2) {

@Override

public void migrate(SupportSQLiteDatabase database) {

database.execSQL("CREATE TABLE `Fruit` (`id` INTEGER, "

+ "`name` TEXT, PRIMARY KEY(`id`))");

}

};

static final Migration MIGRATION_2_3 = new Migration(2, 3) {

@Override

public void migrate(SupportSQLiteDatabase database) {

database.execSQL("ALTER TABLE Book "

+ " ADD COLUMN pub_year INTEGER");

}

};

与Rxjava2 配合使用

由于Room是不能在主线程进行数据库操作的,一在主线程操作,系统就会用java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time. 所以Room支持与Rxjava2配合使用,支持Completable,Single,Maybe,Observable和Flowable

@Query(“SELECT * FROM Users WHERE id = :userId”)

Single<User> getUserById(String userId);

对比总结

通过在使用方式对比,整体上GreenDao与Room在使用方式上差异并不是很大,但最终决定选择Room框架,主要原因如下:

  1. Room是基于APT(Annotation Processing Tool),而GreenDao是基于插件来实现自动生成代码。每次数据库的变动都会重新生成代码,而Room框架基于APT生成的代码在build目录中的apt目录下,对开发者来说是无感知的。

  2. 对应增删改查的操作,Room框架更加灵活,只要我们熟练的掌握sql语法即可,而GreenDao对于复杂的查询操作则需要使用GreenDao包装的函数

  3. 数据库升级方面Room框架更加灵活,效率更加高效

  4. Room对多数据库模式的支持更加友好。app内一般都会存在多个数据库。而GreenDao需要新建个java工程才可以支持多数据库模式,然后拷贝代码到app工程当中。Room则只需要多添加Database注解即可。

应用

大道至简,知易行难

基于以上,我们最终选择了Room框架,但是接入的过程中还是遇到了一些问题,在这里记录一下,以防后来的人在踩不必要的坑。

  1. 不能忽视的@Ignore注解。

    迁移的过程中我们大多会在原有的bean对象的基础上去建立映射关系,也许之前我们定义的bean并不能与数据库的结构一一对应,所以需要我们对于那些不能对应的字段属性上添加@Ignore注解

  2. 对于多个合并在一起的操作,考虑是否使用事务

  3. 混淆配置问题

    Room框架的官方文档和demo中并没有混淆配置的说明,需要注意开启混淆的话,需要对继承RoomDatabase的类不要进行混淆。

-keep class * extends android.arch.persistence.room.RoomDatabase { *; }

4. 跨进程问题。

GreenDao与Room框架都是不支持跨进程的,如果需要支持跨进程操作, 建议使用ContentProvider。

参考

http://greenrobot.org/greendao/

https://developer.android.com/training/data-storage/room/index.html

https://github.com/AlexeyZatsepin/Android-ORM-benchmark

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章