Search code examples
androidkotlinmvvmandroid-roomviewmodel

Cant get data from Room database


I have a problem with getting data from check_list_table. insert method executes normally and there is no error or warning, but allItems property doesn't work.

Item, ItemDao and ViewModel class are shown below:

@Entity(tableName = "check_list_table")
data class Item(
    @PrimaryKey(autoGenerate = true)
    var id: Int = 0,
    var title: String,
    var priority: Int=0
)
interface ItemDao {

    @Insert
    suspend fun insert(item: Item)

    @Delete
    suspend fun delete(item: Item)

    @Query("SELECT * FROM check_list_table ORDER BY id DESC")
    fun getAllItems():LiveData<List<Item>>

}
class DailyCheckListItemViewModel(private val itemDao: ItemDao, application: Application) :
    AndroidViewModel(application) {

    private val viewModelJob = Job()
    private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)

    val allItems = itemDao.getAllItems().value


    override fun onCleared() {
        super.onCleared()
        viewModelJob.cancel()
    }

    fun onAdd(item: Item) {
        uiScope.launch {
            insertItem(item)
        }
    }

    suspend fun insertItem(item: Item) {
        withContext(Dispatchers.IO) {
            itemDao.insert(item)
        }
    }

}
  • insert method called.
  • insert method called with a proper feed.
  • insert method is called from a non-ui thread

Solution

  • Why I cant getData from database [using allItems Property]?

    You've defined an observable read operation:

    @Query("SELECT * FROM check_list_table ORDER BY id DESC")
    fun getAllItems():LiveData<List<Item>>
    

    But are reading it in a direct manner:

    val allItems = itemDao.getAllItems().value
    

    This will not work because the LiveData object must be observed to actually trigger the database read.

    You can either ...

    Make the read operation a "one-shot" (non-observable) operation (if you only need to read the data once)

    Change the DAO to just return a list of items, not a LiveData:

    @Query("SELECT * FROM check_list_table ORDER BY id DESC")
    suspend fun getAllItems():List<Item>
    

    And change the ViewModel to fetch that data all at once:

    viewModelScope.launch(Dispatchers.IO){ allItems = itemDao.getAllItems() }
    

    Or make the ViewModel variable a LiveData as well and observe it in the UI (if the data can change and you want to refresh the UI as it does)

    The DAO stays the same:

    @Query("SELECT * FROM check_list_table ORDER BY id DESC")
    fun getAllItems():LiveData<List<Item>>
    

    The ViewModel just references the LiveData:

    val allItems = itemDao.getAllItems()
    

    And your Activity/Fragment observes the changes. Now that the LiveData is being observed, the read operation will be triggered and the data returned:

    viewModel.allItems.observe(/*lifecycle*/) { items ->
        // Do something with the list of items that was read
    }