详情参照官方文档
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.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();
}
}
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更新
}
}
可以使用查询结果监听,不必每次都去调用查询方法,当有插入删除更新等操作时,会主动通知我们
上面的查询方法更换为
@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数据即可
先指定数据库更新的版本号,然后指定更新数据库的操作信息
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();
}
这样整体操作就完成了
1.声明数据表实体类,用==@Entity==标记,同时设置一个构造方法,其他构造方法用 Ignore 标记
2.声明Dao类,用 @Dao 标记,里面用 @Insert ,@Delete,@Update,@Query 分别标记操作的增删改成
3.可以指定插入和查询的冲突策略,主要是REPLACE,ABORT 和 IGNORE三种常用方式
4.查询操作需要指定sql语句,方法指定值查询需要加 ==:==标记
5.配合LiveData等使用时,需要在通知回调中处理
6.声明数据库,使用 @Database 标记,指定数据库包含的表,以及数据库版本号
7.可以使用 addMigrations 指定更新数据库版本的操作