前言
Room相比GreenDao而言是官方推荐的一个关于数据库的依赖库,Room更需要开发人员有较专业的SQL数据库知识,它涉及到SQL的语法编写和SQL数据库的升级,如果对SQL语法不懂的开发者来说,使用起来是很有难度的,但对于熟悉SQL语法的开发者来说,用起来比GreenDao好用许多
Room的简介
Room是Google提供的一个ORM库。Room提供了三个主要的组件:
- @Database:@Database用来注解类,该类必须是继承自RoomDatabase的抽象类。该类主要作用是创建数据库和创建Dao
- @Entity:@Entity用来注解实体类,@Database通过entities属性引用被@Entity注解的类,并利用该类所有字段作为表的结构
- @Dao:@Dao用来注解一个接口或者抽象方法,该类的作用是提供访问数据库的方法
以上各部分的依赖关系如下图所示:
Room的配置
配置比较简单,但是这里要注意的是,用的是kapt,如果用annotationProcess会报生成的类找不到,因为我这里用的是kotlin语言。由于很多项目用的是多Module的依赖形式,如果使用room需要跨module的话,需要使用api
去替代implementation
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation "android.arch.persistence.room:runtime:1.1.1"
implementation "android.arch.persistence.room:rxjava2:1.1.1"
kapt "android.arch.persistence.room:compiler:1.1.1"
Room的使用
下面就以学生信息的实战来使用room,这里的学生信息展示图如下
一、创建Bean对象(表名和字段名)
room的创建通过注解去生成表的结构,主要由下面几个注解形成
- @Entity:表示需要持久化的实体,后面参数表示表名
- @PrimaryKey:表示表中的主键
- @ColumnInfo:表示表中的字段
- @Embedded:表示表中需要嵌套的对象实体
- @Ignore:表示表中不需要持久化的字段
特別需要注意的是:在定义的时候,记得对实体对象加上
PrimaryKey
,否则程序会报错
@Entity(tableName = "tb_student")
public class Student {
@PrimaryKey(autoGenerate = true)
public long id;
@ColumnInfo(name = "name")
public String name;
@ColumnInfo(name = "sex")
public int sex;
@Embedded
public StudentExtendInfo extendInfo;
@Ignore
public String phone;
public static class StudentExtendInfo {
@ColumnInfo(name = "father_name")
public String father;
@ColumnInfo(name = "mother_name")
public String mother;
}
}
二、定义数据库的增删改查
数据库的增删改查都通过注解表示,可以对具体的操作书写具体的SQL语句。由于room可以和Rxjava一起使用,所以在查询的时候可以返回Flowable
- @Dao:表示当前接口为数据库的操作接口
- @Query:表示查询操作,需要书写具体的SQL语句
- @Insert:表示插入操作,需要在参数中填写插入时发生冲突时的策略
- @Delete:表示删除操作
- @Update:表示修改操作
@Dao
public interface StudentDaoApi {
@Query("SELECT * FROM TB_STUDENT")
Flowable<List<Student>> query();
@Query("SELECT * FROM TB_STUDENT WHERE id IN (:ids)")
Flowable<List<Student>> queryByIds(long[] ids);
@Query("SELECT * FROM TB_STUDENT WHERE id = (:id) LIMIT 1")
Flowable<Student> queryById(long id);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Student... entities);
@Delete
void delete(Student entity);
@Update
void update(Student entity);
}
三、创建数据库
数据库的创建也是通过注解@Database
生成,在注解中填写需要操作的表结构和版本。在这里我们通过kt的语言,用单例的方式去实现当前的数据库,并且要继承RoomDatabase
。
@Database(entities = [Student::class], version = 1)
abstract class AppDatabaseBuilder : RoomDatabase() {
abstract val studentDao: StudentDaoApi
companion object {
private var INSTANCE: AppDatabaseBuilder? = null
fun getInstance(context: Context): AppDatabaseBuilder {
if (INSTANCE == null) {
synchronized(AppDatabaseBuilder::class.java) {
// 生成数据库文件
val builder = Room.databaseBuilder(context.applicationContext,
AppDatabaseBuilder::class.java, "db_common.db")
if (!BuildConfig.DEBUG) {
// 迁移数据库如果发生错误,将会重新创建数据库,而不是发生崩溃
builder.fallbackToDestructiveMigration()
}
INSTANCE = builder.build()
}
}
return INSTANCE!!
}
}
}
四、使用数据库
在使用的时候,只需要获取对应的Dao接口就行操作即可
AppDatabaseBuilder.Companion.getInstance(context).getStudentDao().query();
AppDatabaseBuilder.Companion.getInstance(context).getStudentDao().queryByIds(ids);
AppDatabaseBuilder.Companion.getInstance(context).getStudentDao().insert(student);
AppDatabaseBuilder.Companion.getInstance(context).getStudentDao().delete(student);
AppDatabaseBuilder.Companion.getInstance(context).getStudentDao().update(student);
但是我们发现query
操作有返回Rxjava的特性,而插入、删除、修改
并没有,我们可以通过再增加一层封装,让插入、删除、修改
也支持Rxjava的特性
public class StudentDao {
private StudentDao() {
}
public static StudentDao getInstance() {
return SingletonHolder.sInstance;
}
private static class SingletonHolder {
private static final StudentDao sInstance = new StudentDao();
}
public Flowable<List<Student>> query(Context context) {
return AppDatabaseBuilder.Companion.getInstance(context).getStudentDao().query();
}
public Flowable<List<Student>> queryByIds(Context context, long[] ids) {
return AppDatabaseBuilder.Companion.getInstance(context).getStudentDao().queryByIds(ids);
}
public Flowable<Student> queryById(Context context, long id) {
return AppDatabaseBuilder.Companion.getInstance(context).getStudentDao().queryById(id);
}
public Observable<Boolean> insert(final Context context, final Student student) {
return Observable.fromCallable(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
AppDatabaseBuilder.Companion.getInstance(context).getStudentDao().insert(student);
return true;
}
});
}
public Observable<Boolean> delete(final Context context, final Student student) {
return Observable.fromCallable(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
AppDatabaseBuilder.Companion.getInstance(context).getStudentDao().delete(student);
return true;
}
});
}
public Observable<Boolean> update(final Context context, final Student student) {
return Observable.fromCallable(new Callable<Boolean>() {
@Override
public Boolean call() throws Exception {
AppDatabaseBuilder.Companion.getInstance(context).getStudentDao().update(student);
return true;
}
});
}
}
封装过后的使用就贴近Rxjava的特性,需要注意的是对数据库的操作是需要异步操作的,这在Rxjava也是特别简单
StudentDao.getInstance().insert(this, student)
.subscribeOn(Schedulers.io())
.subscribe(
{ Log.e("TAG", it.toString()) },
{ Log.e("TAG", it.toString()) }
)
五、升级数据库
在使用的过程中,经历了一次发版后,发现需要对原来的数据库表的结构进行修改,这个时候就需要我们掌握升级的SQL语法,room也提供了比较人性化的升级方式Migration
,当然也逃不过SQL语法的编写
在升级的时候,不要忘记将版本号进行更新到新的版本号
@Database(entities = [Student::class], version = 2)
abstract class AppDatabaseBuilder : RoomDatabase() {
abstract val studentDao: StudentDaoApi
companion object {
private var INSTANCE: AppDatabaseBuilder? = null
fun getInstance(context: Context): AppDatabaseBuilder {
if (INSTANCE == null) {
synchronized(AppDatabaseBuilder::class.java) {
// 生成数据库文件
val builder = Room.databaseBuilder(context.applicationContext,
AppDatabaseBuilder::class.java, "db_common.db")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3) // 升级数据库
if (!BuildConfig.DEBUG) {
//迁移数据库如果发生错误,将会重新创建数据库,而不是发生崩溃
builder.fallbackToDestructiveMigration()
}
INSTANCE = builder.build()
}
}
return INSTANCE!!
}
/**
* 版本1升级到2的SQL语句
*/
private val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE 'tb_student' ADD COLUMN 'Sid' INTEGER NOT NULL DEFAULT 0")
}
}
/**
* 版本2升级到3的SQL语句
*/
private val MIGRATION_2_3: Migration = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE 'tb_student' ADD COLUMN 'Stext' TEXT NOT NULL DEFAULT ''")
}
}
}
}
结语
在Room的使用中更结合了新技术Rxjava的使用,可见Rxjava也越来越得到重视,不仅如此,SQL语法也给大家提个醒要去掌握基础的操作,否则room使用起来是很困难的。毫无疑问,google已经将Rxjava和SQL语法当做Android程序员必备的知识,所以不懂得这方面的同学,要加紧补回来哦
Github:RoomDbDemo