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

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

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

TaskItem.kt

@Entity(tableName = "task_item_table")
class TaskItem(
    // Date Info
    @ColumnInfo(name = "hour") var hour: Int, // Hours
    @ColumnInfo(name = "minutes") var minutes: Int, // Minutes
    @ColumnInfo(name = "totalMinutes") var totalMinutes: Int, // HoursId (total of minutes, for sorting)
    // Sound Info
    @ColumnInfo(name = "withMusic") var withMusic: Boolean, // With Music at the end
    @ColumnInfo(name = "isActive") var isActive: Boolean, // If it will ring
    // Sound Volume
    // @ColumnInfo(name = "volume") var volume: Int, // If it will ring

    @PrimaryKey(autoGenerate = true) var id: Int = 0
)

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

TaskItemDao.kt

@Dao
interface TaskItemDao
{
    @Query("SELECT * FROM task_item_table ORDER BY id ASC")
    fun allTaskItems(): Flow<MutableList<TaskItem>>

    @Query("UPDATE task_item_table SET isActive=:newActiveValue WHERE isActive=:isActive")
    suspend fun setAllAlarmsTaskItem(isActive: Boolean, newActiveValue: Boolean)

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

    @Update
    suspend fun updateTaskItem(taskItem: TaskItem)

    @Delete
    suspend fun deleteTaskItem(taskItem: TaskItem)
}

TaskItemRepository.kt

class TaskItemRepository(private val taskItemDao: TaskItemDao)
{
    val allTaskItems: Flow<MutableList<TaskItem>> = taskItemDao.allTaskItems()

    @WorkerThread
    suspend fun insertTaskItem(taskItem: TaskItem)
    {
        taskItemDao.insertTaskItem(taskItem)
    }

    @WorkerThread
    suspend fun updateTaskItem(taskItem: TaskItem)
    {
        taskItemDao.updateTaskItem(taskItem)
    }

    @WorkerThread
    suspend fun deleteTaskItem(taskItem: TaskItem)
    {
        taskItemDao.deleteTaskItem(taskItem)
    }

    @WorkerThread
    suspend fun setAllAlarmsTaskItem(isActive: Boolean, newActiveValue: Boolean)
    {
        taskItemDao.setAllAlarmsTaskItem(isActive, newActiveValue)
    }
}

TaskViewModel.kt

class TaskViewModel(private val repository: TaskItemRepository): ViewModel()
{
    val taskItems: LiveData<MutableList<TaskItem>> = repository.allTaskItems.asLiveData()
    private lateinit var taskViewModel: TaskViewModel

    fun addTaskItem(taskItem: TaskItem) = viewModelScope.launch {
        repository.insertTaskItem(taskItem)
    }
    
    fun updateTaskItem(taskItem: TaskItem) = viewModelScope.launch {
        repository.updateTaskItem(taskItem)
    }

    fun deleteTaskItem(taskItem: TaskItem) = viewModelScope.launch {
        repository.deleteTaskItem(taskItem)
    }

    fun setAllAlarmsTaskItem(isActive: Boolean, newActiveValue: Boolean) = viewModelScope.launch {
        repository.setAllAlarmsTaskItem(isActive, newActiveValue)
    }

    fun setCompleted(taskItem: TaskItem) = viewModelScope.launch {

        repository.updateTaskItem(taskItem)
    }

}

class TaskItemModelFactory(private val repository: TaskItemRepository) : ViewModelProvider.Factory
{
    override fun <T : ViewModel> create(modelClass: Class<T>): T
    {
        if (modelClass.isAssignableFrom(TaskViewModel::class.java))
            return TaskViewModel(repository) as T

        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

NewTaskSheet.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    val activity = requireActivity()

    binding.tvGuardar.setOnClickListener {
        saveAction()
    }

}

private fun saveAction(){

    // ... some more code
    if (taskItem == null)
    {
        val newTask = TaskItem(hour, minutes, totalMinutes, withMusic, isActive)
        taskViewModel.addTaskItem(newTask)
        // Here is where i need to get the id value, so i can passed to my alarm service

    }
    // ... some more code
        alarmService.setRepetitiveAlarm(timeInMilis, musicHour, musicMinutes, withMusic)

}


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

TaskViewModel.kt

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

fun addTaskItem(taskItem: TaskItem): Long {
    // return@async repository.insertTaskItem(taskItem)
    return runBlocking(viewModelScope.coroutineContext) {
        return@runBlocking repository.insertTaskItem(taskItem)
    }
}

// Also tried with this function.
// This one didn't crashed it just return some sort of string that
// didn't contained the id that I was looking for
fun addTaskItem(taskItem: TaskItem): Deferred<Long> = viewModelScope.async {
    return@async repository.insertTaskItem(taskItem)
}

TaskItemRepository.kt

@WorkerThread
suspend fun insertTaskItem(taskItem: TaskItem): Long
{
    return taskItemDao.insertTaskItem(taskItem)
}

TaskItemDao.kt

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

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


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

private fun getPendingIntent(intent: Intent) =
    PendingIntent.getBroadcast(
        context,
        0, // Here is where the id should be
        intent,
        //PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE
        PendingIntent.FLAG_UPDATE_CURRENT

    )


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

2hh7jdfx

2hh7jdfx1#

在TaskItemDao中,从插入返回ID

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

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

suspend fun insertTaskItem(taskItem: TaskItem): Long
{
    return taskItemDao.insertTaskItem(taskItem)
}


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

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


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

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


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

afdcj2ne

afdcj2ne2#

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

class TaskViewModel(private val repository: TaskItemRepository): ViewModel()
{
    private val _myId = MutableLiveData<Long>()
    val myId : LiveData<Long> get() = _myId

    fun addTaskItem(taskItem: TaskItem) {
        viewModelScope.launch {
            _myId.value = repository.insertTaskItem(taskItem)
            // or you can save the id in a variable here and do your work
        }
    }

}

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

相关问题