RoomDatabase的使用方法

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();
    }
}
  1. allowMainThreadQueries表示允許在主線程操作數據庫,一般不推薦;設置這個後主線程調用增刪改查不會報錯,否則會報錯
  2. 可以通過調用 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.可以指定插入和查詢的衝突策略,主要是REPLACEABORTIGNORE三種常用方式

4.查詢操作需要指定sql語句,方法指定值查詢需要加 ==:==標記

5.配合LiveData等使用時,需要在通知回調中處理

6.聲明數據庫,使用 @Database 標記,指定數據庫包含的表,以及數據庫版本號

7.可以使用 addMigrations 指定更新數據庫版本的操作

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章