Room是谷歌官方推出的数据库框架,是替代GreenDao的终极杀手。
工欲善其事,必先利其器
使用room首先需要添加配置
1,使用插件
在app的gradle中添加
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
2,添加依赖
api "androidx.room:room-runtime:$rootProject.roomVersion"
kapt "androidx.room:room-compiler:$rootProject.roomVersion"
ext {
roomVersion = '2.1.0'
}
3,指定room.schemaLocation生成的文件路径
android {
......
defaultConfig {
......
//指定room.schemaLocation生成的文件路径
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
}
}
在编译的时候,Room将database的schema信息导出到一个JSON文件中。为此,要在build.gradle 文件中设置room.schemaLocation注解处理器属性。
它代表了你的数据库的schema历史-保存到你的版本管理系统中,这样就可以让Room创建旧版本的数据库来测试。
room使用中通常分为四个层:entity,dao,database,repository
1,entity
实体数据层,就是一个实体类。
(1)在类上添加@Entity注解,就成为了room的一张表,类名默认是表的名字,可通过在注解中加tableName = "user"来设置表的名字。
(2)一张表至少需要一个主键,可以在类成员上加@PrimaryKey注解来设置表的主键。
(3)类成员对应一个字段,字段名默认是类成员的名字,可以通过@ColumnInfo(name = "id")添加注解来设置字段的名字。
(4)一般这个实体类也会用来进行网络请求、界面展示等,所以实体类中可能会有属性是不需要保存到数据库中的,这时可以添加@Ignore注解来忽略这个字段。
PS:@Ignore也可以忽略自己添加的构造方法。
@Entity(tableName = "user")
public class User {
@PrimaryKey
@ColumnInfo(name = "id")
private Long id;
@ColumnInfo(name = "name")
private String name;
private int sex;
// 忽略的字段
@Ignore
private int age;
}
2,dao
dao是提供了查询数据库的常用方法。通过注解+sql语句实现对数据库的操作。
@Dao
interface UserDao {
// 查询
@Query("SELECT * FROM user")
fun getUsers():LiveData>
// 删除
@Delete
fun delUser(user:User)
// 添加
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertUser(user:User)
}
dao的具体用法参看:Android Jetpack--Room之Dao详解
3,database
database是一个单例的抽象类,实现了对数据库的维护,包括版本号,数据库名,数据库升级,以及关联数据库中的表。
@Database(entities = [User::class], version = 2, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
companion object {
val DATABASE_NAME = "user-db.db"
// For Singleton instantiation
@Volatile
private var instance: AppDatabase? = null
fun getInstance(context: Context): AppDatabase {
return instance ?: synchronized(this) {
instance ?: buildDatabase(context).also { instance = it }
}
}
private fun buildDatabase(context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
.addMigrations(MIGRATION1_2)
.build()
}
}
// 增加字段
object MIGRATION1_2 : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE user ADD COLUMN age INTEGER NOT NULL DEFAULT 18")
}
}
}
(1)、数据迁移
一种全能的更新方法,思路在代码中注释的很清楚了。
val MIGRATION_2_3: Migration = object : Migration(2, 3) {
override fun migrate(@NonNull database: SupportSQLiteDatabase) {
//1.创建一个新的符合Entity字段的新表user_new
database.execSQL(
"CREATE TABLE user_new (id INTEGER NOT NULL,"
+ "name TEXT NOT NULL," //关注点1
+ "age INTEGER NOT NULL," //关注点2
+ "weight TEXT," //关注点3
+ "sex TEXT,"
+ "PRIMARY KEY(id))"
)
//2.将旧表user中的数据拷贝到新表user_new中
database.execSQL(("INSERT INTO user_new(id,name,age,weight,sex) " + "SELECT id,name,age,weight,sex FROM user"))
//3.删除旧表user
database.execSQL("DROP TABLE user")
//4.将新表user_new重命名为user,升级完毕
database.execSQL("ALTER TABLE user_new RENAME TO user")
}
}
(2)、数据库回调
Room通过Builder模式创建数据库,Builder提供了addCallback()方法来提供数据库的回调
RoomDatabase.Callback的代码如下:
/**
* Callback for {@link RoomDatabase}.
*/
public abstract static class Callback {
/**
* Called when the database is created for the first time. This is called after all the
* tables are created.
* @param db The database.
*/
public void onCreate(@NonNull SupportSQLiteDatabase db) {
}
/**
* Called when the database has been opened.
* @param db The database.
*/
public void onOpen(@NonNull SupportSQLiteDatabase db) {
}
}
因此可以实现数据库第一次创建时的回调和数据库每次打开时的回调,代码如下:
Room.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
.addCallback(dbCallback)
.build()
object dbCallback :RoomDatabase.Callback(){
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
// do something when database created
}
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
// do something when database open
}
}
4,repository
repository可以是一个单例类,是VM层访问数据的接口,这里可以控制,数据从网络获取还是从本地数据库获取。
class UserRepository {
private val userDao:UserDao = AppDatabase.getInstance(BaseApp.getContext()).userDao()
fun getUsers() = userDao.getUsers()
fun addUser(user:User){
Thread(Runnable {
userDao.insertUser(user)
}).start()
}
companion object{
// For Singleton instantiation
@Volatile private var instance: UserRepository? = null
fun getInstance() =
instance ?: synchronized(this) {
instance ?: UserRepository().also { instance = it }
}
}
5,VM
VM也就是ViewModel层,这假设你对MVVM模式已经有所了解。
在VM中获取Repository实例,提供返回值为LiveData的方法供View层访问。
class MainViewModel : ViewModel() {
var mRepository: UserRepository? = null
init{
if (mRepository == null) {
mRepository = UserRepository.getInstance()
}
}
fun getUsers(): LiveData> {
return mRepository?.getUsers()!!
}
fun addUser(user: User) {
mRepository?.addUser(user)
}
}
6,V
V也就是MVVM模式中的View层。获取ViewModel,进行数据访问。
viewModel = ViewModelProviders.of(this).get(MainViewModel::class.java)
// 添加数据
btn1.setOnClickListener {
val user = User()
user.name = "小花2"
user.sex = 2
user.age = 27
viewModel.addUser(user)
}
// 查询数据
btn2.setOnClickListener {
val users = viewModel.getUsers()
users.observe(viewLifecycleOwner, Observer> {
it.forEach { item ->
Log.e("aaaa", item.toString())
}
})
}