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 指定更新数据库版本的操作

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