ORM 表示全称为对象关系映射(Object Relational Mapping)。
O 可以理解为java对象
R 可以理解为关系型数据库(sqllite)
M 可以理解为从java对象到关系型数据库建立映射关系的过程
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框架的性能对比数据。 详情地址
通过数据我们看出GreenDAO, Room框架的整体性能是很出色的。所以最终我们选择这两个框架中进行选择。
使用方式对比
对于现有的ORM框架在使用方式上其实大同小异,我们在table映射,数据库初始化,增删改查操作,数据库升级和与Rxjava配合使用,这5点进行对比。
GreenDao框架
GreenDao框架目前存在两种使用方式。
- 一种是通过引入GreenDao的Gradle插件来生成所需要的类,
- 一种是新建一个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;
}
增删改查操作
- 添加数据
daoSession.insert(user); //插入数据
daoSession.insertOrReplace(user);//插入或替换
- 删除数据
daoSession.delete(user);
daoSession.deleteAll(User.class);
- 修改数据
daoSession.update(user);
- 查询数据
方式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的升级思路:
- 创建临时表TMP_,复制原来的数据库到临时表中;
- 删除之前的原表;
- 创建新表;
- 将临时表中的数据复制到新表中,最后将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框架,主要存在如下几点:
- Room是基于APT(Annotation Processing Tool),而GreenDao是基于插件来实现自动生成代码。每次数据库的变动都会重新生成代码,而Room框架基于APT生成的代码在build目录中的apt目录下,对开发者来说是无感知的。
- 对应增删改查的操作,Room框架更加灵活,只要我们熟练的掌握sql语法即可,而GreenDao对于复杂的查询操作则需要使用GreenDao包装的函数
- 数据库升级方面Room框架更加灵活,效率更加高效
- Room对多数据库模式的支持更加友好。app内一般都会存在多个数据库。而GreenDao需要新建个java工程才可以支持多数据库模式,然后拷贝代码到app工程当中。Room则只需要多添加Database注解即可。
应用
大道至简,知易行难
基于以上,我们最终选择了Room框架,但是接入的过程中还是遇到了一些问题,在这里记录一下,以防后来的人在踩不必要的坑。
- 不能忽视的@Ignore注解。
迁移的过程中我们大多会在原有的bean对象的基础上去建立映射关系,也许之前我们定义的bean并不能与数据库的结构一一对应,所以需要我们对于那些不能对应的字段属性上添加@Ignore注解 - 对于多个合并在一起的操作,考虑是否使用事务
- 混淆配置问题
Room框架的官方文档和demo中并没有混淆配置的说明,需要注意开启混淆的话需要对集成RoomDatabase的类不要进行混淆。
-keep class * extends android.arch.persistence.room.RoomDatabase { *; }
- 跨进程问题。GreenDao与Room框架都是不支持跨进程的,如果需要支持跨进程操作建议使用ContentProvider。
参考
http://greenrobot.org/greendao/
https://developer.android.com/training/data-storage/room/index.html