前言
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