kotlin Android ROOM插入行时获取自动生成的主键ID

wooyq4lh  于 2023-11-21  发布在  Kotlin
关注(0)|答案(2)|浏览(362)

我需要获取'id'值,每当我使用以下函数在数据库中添加新行时,该值会自动生成。

TaskItem.kt

  1. @Entity(tableName = "task_item_table")
  2. class TaskItem(
  3. // Date Info
  4. @ColumnInfo(name = "hour") var hour: Int, // Hours
  5. @ColumnInfo(name = "minutes") var minutes: Int, // Minutes
  6. @ColumnInfo(name = "totalMinutes") var totalMinutes: Int, // HoursId (total of minutes, for sorting)
  7. // Sound Info
  8. @ColumnInfo(name = "withMusic") var withMusic: Boolean, // With Music at the end
  9. @ColumnInfo(name = "isActive") var isActive: Boolean, // If it will ring
  10. // Sound Volume
  11. // @ColumnInfo(name = "volume") var volume: Int, // If it will ring
  12. @PrimaryKey(autoGenerate = true) var id: Int = 0
  13. )

字符串
正如你所看到的,主键总是有一个唯一的整数。

TaskItemDao.kt

  1. @Dao
  2. interface TaskItemDao
  3. {
  4. @Query("SELECT * FROM task_item_table ORDER BY id ASC")
  5. fun allTaskItems(): Flow<MutableList<TaskItem>>
  6. @Query("UPDATE task_item_table SET isActive=:newActiveValue WHERE isActive=:isActive")
  7. suspend fun setAllAlarmsTaskItem(isActive: Boolean, newActiveValue: Boolean)
  8. @Insert(onConflict = OnConflictStrategy.REPLACE)
  9. suspend fun insertTaskItem(taskItem: TaskItem)
  10. @Update
  11. suspend fun updateTaskItem(taskItem: TaskItem)
  12. @Delete
  13. suspend fun deleteTaskItem(taskItem: TaskItem)
  14. }

TaskItemRepository.kt

  1. class TaskItemRepository(private val taskItemDao: TaskItemDao)
  2. {
  3. val allTaskItems: Flow<MutableList<TaskItem>> = taskItemDao.allTaskItems()
  4. @WorkerThread
  5. suspend fun insertTaskItem(taskItem: TaskItem)
  6. {
  7. taskItemDao.insertTaskItem(taskItem)
  8. }
  9. @WorkerThread
  10. suspend fun updateTaskItem(taskItem: TaskItem)
  11. {
  12. taskItemDao.updateTaskItem(taskItem)
  13. }
  14. @WorkerThread
  15. suspend fun deleteTaskItem(taskItem: TaskItem)
  16. {
  17. taskItemDao.deleteTaskItem(taskItem)
  18. }
  19. @WorkerThread
  20. suspend fun setAllAlarmsTaskItem(isActive: Boolean, newActiveValue: Boolean)
  21. {
  22. taskItemDao.setAllAlarmsTaskItem(isActive, newActiveValue)
  23. }
  24. }

TaskViewModel.kt

  1. class TaskViewModel(private val repository: TaskItemRepository): ViewModel()
  2. {
  3. val taskItems: LiveData<MutableList<TaskItem>> = repository.allTaskItems.asLiveData()
  4. private lateinit var taskViewModel: TaskViewModel
  5. fun addTaskItem(taskItem: TaskItem) = viewModelScope.launch {
  6. repository.insertTaskItem(taskItem)
  7. }
  8. fun updateTaskItem(taskItem: TaskItem) = viewModelScope.launch {
  9. repository.updateTaskItem(taskItem)
  10. }
  11. fun deleteTaskItem(taskItem: TaskItem) = viewModelScope.launch {
  12. repository.deleteTaskItem(taskItem)
  13. }
  14. fun setAllAlarmsTaskItem(isActive: Boolean, newActiveValue: Boolean) = viewModelScope.launch {
  15. repository.setAllAlarmsTaskItem(isActive, newActiveValue)
  16. }
  17. fun setCompleted(taskItem: TaskItem) = viewModelScope.launch {
  18. repository.updateTaskItem(taskItem)
  19. }
  20. }
  21. class TaskItemModelFactory(private val repository: TaskItemRepository) : ViewModelProvider.Factory
  22. {
  23. override fun <T : ViewModel> create(modelClass: Class<T>): T
  24. {
  25. if (modelClass.isAssignableFrom(TaskViewModel::class.java))
  26. return TaskViewModel(repository) as T
  27. throw IllegalArgumentException("Unknown ViewModel class")
  28. }
  29. }

NewTaskSheet.kt

  1. override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  2. super.onViewCreated(view, savedInstanceState)
  3. val activity = requireActivity()
  4. binding.tvGuardar.setOnClickListener {
  5. saveAction()
  6. }
  7. }
  8. private fun saveAction(){
  9. // ... some more code
  10. if (taskItem == null)
  11. {
  12. val newTask = TaskItem(hour, minutes, totalMinutes, withMusic, isActive)
  13. taskViewModel.addTaskItem(newTask)
  14. // Here is where i need to get the id value, so i can passed to my alarm service
  15. }
  16. // ... some more code
  17. alarmService.setRepetitiveAlarm(timeInMilis, musicHour, musicMinutes, withMusic)
  18. }


我试过使用以下函数,但没有成功:所以基本上我只是在每个函数中返回。

TaskViewModel.kt

这是我认为导致问题的函数,因为它使程序崩溃:

  1. fun addTaskItem(taskItem: TaskItem): Long {
  2. // return@async repository.insertTaskItem(taskItem)
  3. return runBlocking(viewModelScope.coroutineContext) {
  4. return@runBlocking repository.insertTaskItem(taskItem)
  5. }
  6. }
  7. // Also tried with this function.
  8. // This one didn't crashed it just return some sort of string that
  9. // didn't contained the id that I was looking for
  10. fun addTaskItem(taskItem: TaskItem): Deferred<Long> = viewModelScope.async {
  11. return@async repository.insertTaskItem(taskItem)
  12. }

TaskItemRepository.kt

  1. @WorkerThread
  2. suspend fun insertTaskItem(taskItem: TaskItem): Long
  3. {
  4. return taskItemDao.insertTaskItem(taskItem)
  5. }

TaskItemDao.kt

我读到,通过返回Long,它会自动返回主键的值。

  1. @Insert(onConflict = OnConflictStrategy.REPLACE)
  2. suspend fun insertTaskItem(taskItem: TaskItem): Long


我希望能够有'id'值(primarykey),它被赋予了相应的创建行,这样我就可以使用它作为我的pendingIntents的requestCode。

  1. private fun getPendingIntent(intent: Intent) =
  2. PendingIntent.getBroadcast(
  3. context,
  4. 0, // Here is where the id should be
  5. intent,
  6. //PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE
  7. PendingIntent.FLAG_UPDATE_CURRENT
  8. )


或者有没有另一种正确处理“请求代码”的策略?
我的应用程序是一个闹钟,所以每个闹钟都必须有一个唯一的代码,将被保存在数据库中,以便以后能够删除创建的闹钟。

2hh7jdfx

2hh7jdfx1#

在TaskItemDao中,从插入返回ID

  1. @Insert(onConflict = OnConflictStrategy.REPLACE)
  2. suspend fun insertTaskItem(taskItem: TaskItem): Long

字符串
在TaskItemRepository中,再次返回ID

  1. suspend fun insertTaskItem(taskItem: TaskItem): Long
  2. {
  3. return taskItemDao.insertTaskItem(taskItem)
  4. }


在TaskViewModel中,你可以添加项目,获取ID,然后返回它。注意,要从协程作用域返回,你必须将函数标记为suspend,并使用调用者上下文。这强制要求从协程作用域调用函数。

  1. suspend fun addTaskItem(taskItem: TaskItem): Long = withContext(Dispatchers.IO) {
  2. return@withContext repository.insertTaskItem(taskItem)
  3. }


注意Dispatchers.IO,当你对一个DAO方法进行Room调用时,你总是想指定它,而这个DAO方法不返回Flow。你看到的崩溃很可能是因为在主线程上使用了Room,这是不允许的(IO也会阻止)。
在UI层,您可以使用Fragment的生命周期范围调用新的ViewModel函数

  1. lifecycleScope.launch {
  2. val newTaskID = taskViewModel.addTaskItem(newTask)
  3. }


需要newTaskID的代码将进入协程作用域。
我建议阅读关于Coroutines的文章,并查看additional resources。在使用Room时,对apec概念有很强的理解是很重要的。

展开查看全部
afdcj2ne

afdcj2ne2#

viewmodelScope中调用你的仓库方法insertTaskItem,并将返回的id保存到liveData或stateFlow中,然后你就可以从你的视图中观察liveData了。这就是我从问题中理解的,如果你不需要视图中的id,那么就在viewModel中处理它

  1. class TaskViewModel(private val repository: TaskItemRepository): ViewModel()
  2. {
  3. private val _myId = MutableLiveData<Long>()
  4. val myId : LiveData<Long> get() = _myId
  5. fun addTaskItem(taskItem: TaskItem) {
  6. viewModelScope.launch {
  7. _myId.value = repository.insertTaskItem(taskItem)
  8. // or you can save the id in a variable here and do your work
  9. }
  10. }
  11. }

字符串
注意,你不需要使用withContext来将线程更改为IO,因为Room会自动处理这个问题,并且所有的dao suspend函数都可以安全地从主线程调用。

展开查看全部

相关问题