Room数据库使用与踩坑(最新)
目录
一、介绍
关于Room数据库的介绍可以看Android官方的(使用 Room 将数据保存到本地数据库 | Android 开发者 | Android Developers),这里主要介绍一些官方缺少的和一些踩坑。
二、引入
如需在应用中使用 Room,请将以下依赖项添加到应用的 build.gradle
文件:
注意:Room的版本号与kotlin的版本号需要对应
dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:1.3.72" implementation 'androidx.core:core-ktx:1.3.2' def latest_version = "2.2.5" //room implementation "androidx.room:room-runtime:$latest_version" implementation "androidx.room:room-ktx:$latest_version" kapt "androidx.room:room-compiler:$latest_version" // optional - 支持Rxjava2与Room implementation "androidx.room:room-rxjava2:$latest_version" // optional - 支持协程与Room implementation("androidx.room:room-ktx:$latest_version")}
三、使用(增删改查)
使用前需要先准备数据实体,数据访问对象(DAO),数据库
数据实体
@Entityclass RunRecord { @PrimaryKey(autoGenerate = true) var id: Long? = null @ColumnInfo(name = "userId") var userId: String? = null @ColumnInfo(name = "pathLine") var pathLine: String? = null @ColumnInfo(name = "totalStep") var totalStep:Int? = 0}
数据访问对象
注意:返回主键ID类型与数据实体中的主键类型对应。或者实体中的类型是Int,插入成功返回Long/Int,不能相反。
@Daointerface RunDao { / * * 插入一条信息,id不用传,会自动增长.建议不传 * @return Long 返回插入的主键id */ @Insert fun insert(info: RunRecord?): Long @Query("INSERT into RunRecord(userId,pathLine) values(:userId,:pathLine) ") fun insert(userId: String?, pathLine: String?): Long / * @return Int 1 成功 0失败 */ @Delete fun delete(info: RunRecord?): Int @Query("DELETE FROM RunRecord WHERE userId = :userId") fun delete(userId: String?): Int / * @return Int 返回删除的条数 */ @Query("DELETE FROM RunRecord") fun deleteAll(): Int / * @return Int 1 成功 0失败 */ @Update fun update(vararg info: RunRecord?): Int @Query("UPDATE RunRecord SET pathLine =:info WHERE userId = :userId") fun updateRunInfo(userId: String, info: String): Int / * 根据userId获取最后一条 */ @Query("SELECT * FROM RunRecord WHERE userId LIKE :userId order by userId desc LIMIT 1") fun findLastByUserId(userId: String): RunRecord? / * 查询全部数据 */ @get:Query("SELECT * FROM RunRecord") val all: List?}
数据库
@Database(entities = [ RunRecord::class], version = 1)abstract class YzDatabase : RoomDatabase() { abstract fun runDao(): RunDao? companion object { private const val DB_NAME = "yzWill.db" @Volatile private var instance: YzDatabase? = null fun getInstance(context: Context) = instance ?: synchronized(this) { instance ?: Room.databaseBuilder( context, YzDatabase::class.java, DB_NAME ).build() } }}
注意:Room的操作都需要在子线程执行,如果需要在主线程执行需要设置allowMainThreadQueries()方法
如下设置:
fun getInstance(context: Context) = instance ?: synchronized(this) { instance ?: Room.databaseBuilder( context, YzDatabase::class.java, DB_NAME ).allowMainThreadQueries().build() }
3.1 增
插入一条数据,返回主键ID,初始也就是1
@Insert用法
val db = YzDatabase.getInstance(this).runDao()Thread() { //插入一条数据,返回主键id val id = db?.insert(RunRecord().apply { pathLine = "插入一条信息" userId = "小小虫" }) Log.e(tag, "id:$id")}.start()
@Query("INSERT into RunRecord(userId,pathLine) values(:userId,:pathLine) ") 用法
val db = YzDatabase.getInstance(this).runDao()Thread() { //插入一条数据pathLine = "插入一条信息1",userId = "小小虫1",返回主键id val id = db?.insert(pathLine = "插入一条信息1", userId = "小小虫1") Log.e(tag, "id:$id")}.start()
3.2 删
删除一条信息,返回0或1。注意:这里删除必须传ID,也就是主键,因为 @Delete 注解只根据主键识别,后面的 @Update 也是如此
@Delete 用法
val db = YzDatabase.getInstance(this).runDao()Thread() { //删除ID为1的数据,返回 0:删除失败 1:删除成功 val success = db?.delete(RunRecord().apply { id = 1 }) Log.e(tag, "success:$success")}.start()
@Query("DELETE FROM RunRecord WHERE userId = :userId") 用法
val db = YzDatabase.getInstance(this).runDao()Thread() { //删除userId = "小小虫"的数据,返回删除的条数 val count = db?.delete( userId = "小小虫") Log.e(tag, "count:$count")}.start()
@Query("DELETE FROM RunRecord") 用法
val db = YzDatabase.getInstance(this).runDao()//删除所有数据,返回删除的条数Thread() { val count = db?.deleteAll() Log.e(tag, "count:$count")}.start()
3.3 改
@Query("UPDATE RunRecord SET pathLine =:info WHERE userId = :userId")用法
val db = YzDatabase.getInstance(this).runDao()//更新 userId = "小小虫" 的一数据,修改info = ="修改一条信息"// 返回 0:更新失败 1:更新成功Thread() { val success = db?.updateRunInfo(userId = "小小虫", info = "修改一条信息") Log.e(tag, "success:$success")}.start()
@Update 用法也是根据主键ID查找对应数据修改
val db = YzDatabase.getInstance(this).runDao()//更新 id = 1 的一条数据,,返回 0:更新失败 1:更新成功Thread() { val success = db?.update(RunRecord().apply { id = 1 pathLine = "修改一条信息" userId = "小小虫1" }) Log.e(tag, "success:$success")}.start()
3.4 查
@Query("SELECT * FROM RunRecord WHERE userId LIKE :userId order by userId desc LIMIT 1")
val db = YzDatabase.getInstance(this).runDao()Thread() { //根据userId获取最后一条 val info = db?.findLastByUserId(userId = "小小虫") Log.e(tag, "info:$info")}.start()
@get:Query("SELECT * FROM RunRecord")
val db = YzDatabase.getInstance(this).runDao()//查询所有数据,返回表所有数据Thread() { val info = db?.all info?.forEach { Log.e(tag, "it:$it") }}.start()
四、更新数据库
当我们需要在原有的数据库里添加表或者在原有的表里添加新的字段时,需要更新数据库才能使我们新加的内容得以应用。
4.1自动迁移
注意:Room 在 2.4.0-alpha01 及更高版本中支持自动迁移。如果您的应用使用的是较低版本的 Room,则必须手动定义迁移。
不建议使用这种方式,因为高版本的Room需要更新Kotlin 版本,Kotlin插件版本,Gradle版本等,而且还要升级Android Studio,我放弃了!!
关于这些具体的版本号查看这里:https://github.com/googlecodelabs/android-room-with-a-view/archive/kotlin.zip
使用
旧版本
@Database(entities = [ RunRecord::class], version = 1)abstract class YzDatabase : RoomDatabase() {
新版本
@Database(entities = [ RunRecord::class], version = 2, autoMigrations = [AutoMigration (from = 1, to = 2)])abstract class YzDatabase : RoomDatabase() {
4.2手动迁移
在RunRecord表中加一个userName字段和std字段
@ColumnInfo(name = "userName")var userName: String? = null@ColumnInfo(name = "std")var std: Int? = null
步骤:
1.将version+1
@Database(entities = [ RunRecord::class], version = 2)
2.创建Migration(int startVersion,int endVersion)类,startVersion原始版本号(例如1),endVersion升级后的版本号(例如2)
提示:由于SQLite好像不支持像数据库一样多个字段添加,所以只能分开添加
val MIGRATION_1_2: Migration = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL( "ALTER TABLE RunRecord"+ " ADD COLUMN userName TEXT" ) database.execSQL( "ALTER TABLE RunRecord"+ " ADD COLUMN std INTEGER" ) } }
3.将Mrgration添加到数据库构建中
fun getInstance(context: Context) = instance ?: synchronized(this) { instance ?: Room.databaseBuilder( context, YzDatabase::class.java, DB_NAME ).addMigrations(MIGRATION_1_2).build() }
完整代码
@Database(entities = [ RunRecord::class], version = 2)abstract class YzDatabase : RoomDatabase() { abstract fun runDao(): RunDao? companion object { private const val DB_NAME = "yzWill.db" val MIGRATION_1_2: Migration = object : Migration(1, 2) { override fun migrate(database: SupportSQLiteDatabase) { database.execSQL( "ALTER TABLE RunRecord"+ " ADD COLUMN userName TEXT" ) database.execSQL( "ALTER TABLE RunRecord"+ " ADD COLUMN std INTEGER" ) } } @Volatile private var instance: YzDatabase? = null fun getInstance(context: Context) = instance ?: synchronized(this) { instance ?: Room.databaseBuilder( context, YzDatabase::class.java, DB_NAME ).addMigrations(MIGRATION_1_2).build() } }}
注意:每次更新的MIGRATION_1_2,MIGRATION_2_3等都要加到构建中去,不能只单单 addMigrations(MIGRATION_2_3),如果此时手机安装的数据库版本是2,那就没问题。如果是1,那么app会闪退