1.配置信息
def room_version = "2.2.3"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version" // For Kotlin use kapt instead of annotationProcessor
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
// optional - RxJava support for Room
implementation "androidx.room:room-rxjava2:$room_version"
// optional - Guava support for Room, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"
// Test helpers
testImplementation "androidx.room:room-testing:$room_version"
2.使用方法
2.1 定義數據表結構
@Entity(tableName = "student")
public class Student {
@PrimaryKey
private int uid;
@ColumnInfo(name = "name")
private String name;
@ColumnInfo(name = "age")
private int age;
public Student() {
}
@Ignore
public Student(int uid, String name, int age) {
this.uid = uid;
this.name = name;
this.age = age;
}
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
1.@Entity是數據表的聲明,可以使用tableName指定表名,此外在這裏也可以指定外鍵等相關信息,比如這裏指定uid爲Score表的外鍵,對應的Score表的studentId
@Entity(tableName = "student",foreignKeys = @ForeignKey(entity =Score.class,parentColumns = "studentId",childColumns = "uid"))
2.數據表必須指明一個構造方法,但一般只保留一個,其他的構造方法用 @Ignore標示
3.所有的get和set方法必須寫出
4.可以用 @PrimaryKey(autoGenerate = true) 指定主鍵自增
2.2 定義查詢Dao結構
@Dao
public interface StudentDao {
@Update
void insert(Student student);
@Delete
void delete(Student student);
@Update(onConflict = OnConflictStrategy.REPLACE)
void update(Student student);
@Query("select * from student where uid = :uid")
Student queryByUid(int uid);
@Query("select * from student order by uid desc")
List<Student> queryAll();
}
1.Dao結構需要用 @Dao 聲明
2.四種操作符 @Insert ,@Delete,@Update,@Query
3.onConflict定義了當操作發生衝突時的處理策略,比如說我們插入數據時指定了主鍵id,那麼如果兩次插入相同id的數據,第一次插入是正常的,第二次插入的話就會發生衝突,就會走這個策略判斷;
目前只有 @Insert 和 @Update 有這個策略方式,策略分爲五種
3.1 REPLACE 替換,會以最新的操作爲準
3.2 ABORT 終止,會中斷操作,並拋出異常
3.3 IGNORE 忽略,會中斷操作,並且不會拋出異常
3.4 ROLLBACK 回滾 和 FAIL 對當前Room不支持,被 @Deprecated
4. Query操作需要自己制定操作sql語句,返回值支持原始表數據和LiveData以及ViewModel等
5. 當query查詢需要指定查詢的值時,用 : 標出
2.3 聲明數據庫
@Database(entities = {Student.class}, version = 1,exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {
public abstract StudentDao studentDao();
}
1.數據庫用 @Database聲明
2.entities指定數據庫包含的表,可以傳入多個
3.version指定版本,升級版本號時候會用到
4.exportSchema主要用來忽略警告,可以配置
5. 數據需要繼承 RoomDatabase
6. apt編譯會自動生成表相關的實現類,傻瓜式編程
2.4 創建管理類
public class DbManager {
private static DbManager instance;
private MyDatabase myDataBase;
public static DbManager getInstance() {
if (instance == null) {
synchronized (DbManager.class) {
if (instance == null) {
instance = new DbManager();
}
}
}
return instance;
}
public void init(Context context) {
myDataBase = Room.databaseBuilder(context, MyDatabase.class, "dbName")
.allowMainThreadQueries()
// .addMigrations(migration)
.build();
}
public StudentDao getStudentDao() {
return myDataBase.studentDao();
}
public ScoreDao getScoreDao() {
return myDataBase.scoreDao();
}
}
- allowMainThreadQueries表示允許在主線程操作數據庫,一般不推薦;設置這個後主線程調用增刪改查不會報錯,否則會報錯
- 可以通過調用 addMigrations 方法設置更新數據庫的操作,後面會講
2.5 具體使用操作
初始化
fun init(){
DbManager.getInstance().init(applicationContext)
studentDao = DbManager.getInstance().studentDao
}
操作
var index: Int = 0;
fun add() {
GlobalScope.launch(Dispatchers.IO) {
val next = index++
val student = Student(next, "Name $next", next)
studentDao.insert(student)
}
}
fun delete() {
GlobalScope.launch(Dispatchers.IO) {
val student = Student(index, "", 0)
studentDao.delete(student)
}
}
fun update() {
GlobalScope.launch(Dispatchers.IO) {
val student = Student(index, "Name $666", -1)
studentDao.update(student)
}
}
fun query() {
GlobalScope.launch(Dispatchers.Main) {
val task = GlobalScope.async(Dispatchers.IO) {
val queryList = studentDao.queryAll()
}
task.await()
//TODO ui更新
}
}
3.和LiveData配合使用
可以使用查詢結果監聽,不必每次都去調用查詢方法,當有插入刪除更新等操作時,會主動通知我們
上面的查詢方法更換爲
@Query("select * from student order by uid desc")
LiveData<List<Student>> queryAll();
查詢方法更新爲
lateinit var liveData: LiveData<List<Student>>
private fun query() {
GlobalScope.launch(Dispatchers.Main) {
val task = GlobalScope.async(Dispatchers.IO) {
if (!this@RoomActivity::liveData.isInitialized) {
liveData = studentDao.queryAll()
}
}
task.await()
liveData.observe(this@RoomActivity, this@RoomActivity)
Log.e(TAG, "queryStudent $students ")
}
}
private fun updateList(list:List<Student>?){
students.clear()
students.addAll(list!!)
dataBinding.recyclerview.adapter = studentAdapter
studentAdapter.notifyDataSetChanged()
}
內部方法使用上下文可以用 this@RoomActivity這種方式
liveData很快就會返回,這裏不用線程應該也可以;
但是這時候並沒有返回查詢結果的,具體查詢到後會在方法中通知我們
override fun onChanged(t: List<Student>?) {
updateList(t)
}
在具體的更新方法中去操作UI數據即可
4.數據庫更新
先指定數據庫更新的版本號,然後指定更新數據庫的操作信息
public Migration updateVersion() {
return new Migration(1, 2) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase database) {
Log.e("1234", "migrate exec " + database.getVersion());
database.execSQL("alter table student add column testAddColum text default 'hello world'");
}
};
}
這裏指定數據庫從1升級到2版本執行的操作,給student表增加一個額外字段並賦初始值
然後使用 addMigrations方法添加到數據庫配置中,下次操作相應數據庫時候就會去檢測更新
public void init(Context context) {
myDataBase = Room.databaseBuilder(context, MyDatabase.class, "dbName")
//允許主線程操作數據庫
.addMigrations(updateVersion())
.allowMainThreadQueries()
.build();
}
最後把當期的數據庫版本升級 ,從1改成2
@Database(entities = {Student.class}, version = 2,exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {
public abstract StudentDao studentDao();
}
這樣整體操作就完成了
5.總結
1.聲明數據表實體類,用==@Entity==標記,同時設置一個構造方法,其他構造方法用 Ignore 標記
2.聲明Dao類,用 @Dao 標記,裏面用 @Insert ,@Delete,@Update,@Query 分別標記操作的增刪改成
3.可以指定插入和查詢的衝突策略,主要是REPLACE,ABORT 和 IGNORE三種常用方式
4.查詢操作需要指定sql語句,方法指定值查詢需要加 ==:==標記
5.配合LiveData等使用時,需要在通知回調中處理
6.聲明數據庫,使用 @Database 標記,指定數據庫包含的表,以及數據庫版本號
7.可以使用 addMigrations 指定更新數據庫版本的操作