大家好,我是一个热爱技术的Coder。对于android,我是-【不厉害但爱‘玩’ 】。今天记录的是数据存储这块的内容。
2018年 Room 就问世了,可是到现在有多少人正式用过它呢?这个问题,我也不知道。我问出来,只是因为我之前一直没用,对于本地数据的存储涉及不到。最近项目中有需要,就记录了一下吧。以后,要多多了解新出的技术,例如最近的Compose Beta版,与时代同步。
老规矩,是什么?为什么?怎么用?三步走。
对于一个东西是什么?最好的了解方式,就是上官网,看官网的定义。上才。。不对,上链接。传送门
Room 在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库。
那为什么我们直接用 SQLite 呢?这种问题,关于为什么要用一个新的技术,而不用原来的呢?其实官网也会提上一嘴。
处理大量结构化数据的应用可极大地受益于在本地保留这些数据。最常见的用例是缓存相关数据。这样,当设备无法访问网络时,用户仍可在离线状态下浏览相应内容。设备重新连接到网络后,用户发起的所有内容更改都会同步到服务器。
由于 Room 负责为您处理这些问题,因此我们强烈建议您使用 Room(而不是 SQLite)。
在刚开始使用的时候,我发现了一些注解,那么我们先来了解一下这些注解吧。
注解名 | 官方解释 | 自己理解 |
---|---|---|
@Database | Marks a class as a RoomDatabase. 注意: 这需要是一抽象类并且继承RoomDatabase; 如果您的应用在单个进程中运行,在实例化 AppDatabase 对象时应遵循单例设计模式。每个 RoomDataBase 实例的成本相当高,而您几乎不需要在单个进程中访问多个实例。 | 就是建立数据库 |
@Dao | Marks the class as a Data Access Object. 注意: 被这个标注的类需要是一个接口或者是抽象类, 建议有多少个表就有多少个 Dao | 就是操作数据库的(CRUD) |
@Entity | Marks a class as an entity. This class will have a mapping SQLite table in the database. 注意: 至少有一个主键 | 可以看作-建表,数据库里的一张表 |
那他们之间的关系是什么呢?
那么怎么用呢?主要是三个类的创建(Database、Dao、Entity)
不过首先要记得在gradle中配置依赖
dependencies {
def room_version = "2.2.6"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
// optional - Test helpers
testImplementation "androidx.room:room-testing:$room_version"
}
Database
@Database(entities = [RecordDataBean::class], version = 1, exportSchema = true)
abstract class RecordDatabase:RoomDatabase() {
abstract fun chatRecordDao(): ChatRecordDao
companion object {
@JvmStatic
@Volatile
private var INSTANCE: RecordDatabase? = null
@JvmStatic
fun getDatabase(context: Context): RecordDatabase {
val tempInstance = INSTANCE
if (tempInstance != null) {
return tempInstance
}
synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
RecordDatabase::class.java,
"record_database" //数据库名称
).build()
INSTANCE = instance
return instance
}
}
}
}
这里采用了官方推荐的单例模式来创建 Database
Dao
@Dao
interface ChatRecordDao {
//Insert 可以返回插入数据的id值
@Insert
suspend fun insert(recordDataBean: RecordDataBean):Long
@Delete
suspend fun delete(recordDataBean: RecordDataBean): Int
@Update
suspend fun update(recordDataBean: RecordDataBean): Int
@Query("select * from chat_record where user_account =:userAccount order by date asc")
suspend fun queryAllRecord(userAccount:String): List<RecordDataBean>
@Query("select * from chat_record order by id desc limit 1")
suspend fun queryLastRecord(): RecordDataBean
@Query("select * from chat_record where msg_id = :msgId")
suspend fun isExistInDatabase(msgId:Int): RecordDataBean?
}
根据自己的业务需求,编写操作数据库的方法。这里最关键的就是 SQL 语句的编写
Entity
@Entity(tableName = "chat_record")
data class RecordDataBean (
@PrimaryKey(autoGenerate = true) var id:Int?=0,
@ColumnInfo(name="user_account") var userAccount:String,
@ColumnInfo(name="user_type") var userType:Int,
@ColumnInfo(name="content") var content:String,
@ColumnInfo(name="content_type") var contentType:Int,
@ColumnInfo(name="date") var date:String,
@ColumnInfo(name="status") var status:Int,
@ColumnInfo(name="msg_id") var msgId:Int?=-1
)
最后就是 Entity 了。
在开发过程中,遇到一些疑问和Bug,这里分享出来,避免大家踩坑。
问题描述:
Execution failed for task ':app:kaptDebugKotlin'. > A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptExecution > java.lang.reflect.InvocationTargetException (no error message)
解决方案:因为在Dao 忘记用注解了@Dao,加上@Dao注解即可
问题描述:
UNIQUE constraint failed: chat_record.id (code 1555 SQLITE_CONSTRAINT_PRIMARYKEY)
解决方案:因为主键重复了,所以报错
问题描述:如何让主键自动增加
解决方案:
data class RecordDataBean ( @PrimaryKey(autoGenerate = true) var id:Int?=0 //省略其他字段 )
//在创建一条数据的时候,将id值为空即可,这样就可以自增了 val recordDataBean3 = RecordDataBean(null)
问题追加1:我填了null,我又怎么得到插进入的数据的id是多少呢?
解决方案:在插入后,可以将对应的主键值返回
//Insert 可以返回插入数据的id值
@Insert
suspend fun insert(recordDataBean: RecordDataBean):Long
问题描述:如何通过动态的值来查询数据,例如通过不同userId来获取有关数据
解决方案:
@Query("select * from chat_record where user_id is (:userId) order by date asc")
suspend fun queryAllRecord(userId: Int): List<RecordDataBean>
问题描述:如果查询结果为空,会返回什么?
测试结果:会返回空
如有疑问和指教,可评论区留言交流。