Bruno Baccho
Bruno Baccho

Reputation: 171

How to implement multiple queries with room database?

Im creating an android app using room database with MVVM pattern, the problem is that i cant use multiple queries when fetching data. I can fetch data once, but then i cant do it anymore.

DAO interface:

@Dao
interface StockDao {

    @Insert
    suspend fun insert(stock:Stock)

    @Update
    suspend fun update(stock:Stock)

    @Delete
    suspend fun delete(stock:Stock)

    @Query("DELETE FROM stock_table")
    suspend fun deleteAll()

    @Query("SELECT * FROM stock_table")
    fun selectAll():Flow<List<Stock>>

    @Query("SELECT * FROM stock_table WHERE isFinished = 0")
    fun selectAllUnfinished(): Flow<List<Stock>>

    @Query("SELECT * FROM stock_table WHERE isFinished = 1")
    fun selectAllFinished():Flow<List<Stock>>

    @Query("SELECT * FROM stock_table ORDER BY totalSpent DESC")
    fun selectAllOrderByDesc():Flow<List<Stock>>

    @Query("SELECT * FROM stock_table ORDER BY totalSpent ASC")
    fun selectAllOrderByAsc():Flow<List<Stock>>

}

Repository:

class StockRepository(private val stockDao: StockDao) {

    private lateinit var allStock: Flow<List<Stock>>


    suspend fun insert(stock: Stock) {

        stockDao.insert(stock)

    }

    suspend fun update(stock: Stock) {

        stockDao.update(stock)

    }

    suspend fun delete(stock: Stock) {

        stockDao.delete(stock)

    }

    suspend fun deleteAll() {

        stockDao.deleteAll()

    }

    fun selectAll(): Flow<List<Stock>> {

        allStock = stockDao.selectAll()

        return allStock

    }

    fun selectAllOrderByDesc(): Flow<List<Stock>> {

        allStock = stockDao.selectAllOrderByAsc()

        return allStock

    }

    fun selectAllOrderByAsc(): Flow<List<Stock>> {

        allStock = stockDao.selectAllOrderByAsc()

        return allStock

    }

    fun selectAllFinished(): Flow<List<Stock>> {

        allStock = stockDao.selectAllFinished()

        return allStock

    }

    fun selectAllUnfinished(): Flow<List<Stock>> {

        allStock = stockDao.selectAllUnfinished()

        return allStock

    }


}

Viewmodel class:

class StockViewModel(private val repo: StockRepository) : ViewModel() {

    companion object {

        const val ALL = 0
        const val ORDER_BY_DESC = 1
        const val ORDER_BY_ASC = 2
        const val FINISHED = 3
        const val UNFINISHED = 4

    }

    var allStocks = repo.selectAll().asLiveData()


    fun insert(stock: Stock) = viewModelScope.launch {
        repo.insert(stock)
    }

    fun update(stock: Stock) = viewModelScope.launch {
        repo.update(stock)
    }

    fun delete(stock: Stock) = viewModelScope.launch {
        repo.delete(stock)
    }

    fun deleteAll() = viewModelScope.launch {
        repo.deleteAll()
    }

    fun selectAllStockWithFilter(filter: Int): LiveData<List<Stock>> {

        when (filter) {

            ALL -> allStocks = repo.selectAll().asLiveData()
            ORDER_BY_DESC -> allStocks = repo.selectAllOrderByDesc().asLiveData()
            ORDER_BY_ASC -> allStocks = repo.selectAllOrderByAsc().asLiveData()
            FINISHED -> allStocks = repo.selectAllFinished().asLiveData()
            UNFINISHED -> allStocks = repo.selectAllUnfinished().asLiveData()
        }

        return allStocks

    }


    class StockViewModelFactory(private val repo: StockRepository) : ViewModelProvider.Factory {

        override fun <T : ViewModel?> create(modelClass: Class<T>): T {
            if (modelClass.isAssignableFrom(StockViewModel::class.java)) {
                @Suppress("UNCHECKED_CAST")
                return StockViewModel(repo) as T
            }

            throw IllegalArgumentException("Unknown viewModel class")
        }

    }


}

Application class:

class FinanceApplication :Application(){

    private val database by lazy {  FinanceDatabase.getInstance(this)}

    val stockRepository by lazy { StockRepository(database.stockDao()) }
}


Activity using this viewmodel :

class StocksActivity : AppCompatActivity() {

    //Layout components
    private lateinit var binder: ActivityStocksBinding
    private lateinit var recyclerView: RecyclerView
    private lateinit var recyclerViewAdapter: StockAdapter

    //ViewModel
    private val viewModel: StockViewModel by viewModels {
        StockViewModel.StockViewModelFactory((application as FinanceApplication).stockRepository)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binder = ActivityStocksBinding.inflate(layoutInflater)
        setContentView(binder.root)
    
        fetchStocks()


    }

    private fun fetchStocks() {


        viewModel.allStocks.observe(this) {

            recyclerViewAdapter.submitList(it)

        }


    }

    private fun initRecyclerViewLayout() {


        val recyclerViewLayoutBinder = binder.includedLayout

        recyclerView = recyclerViewLayoutBinder.stocksRecyclerView
        recyclerViewAdapter = StockAdapter(this)
        recyclerView.adapter = recyclerViewAdapter
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.setHasFixedSize(true)


    }

    override fun onCreateOptionsMenu(menu: Menu?): Boolean {

        menuInflater.inflate(R.menu.menu_stock_toolbar, menu)

        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {

        when (item.itemId) {

            R.id.menu_stock_toolbar_filter_all -> viewModel.selectAllStockWithFilter(StockViewModel.ALL)
            R.id.menu_stock_toolbar_filter_maior_menor ->  viewModel.selectAllStockWithFilter(StockViewModel.ORDER_BY_DESC)
            R.id.menu_stock_toolbar_filter_menor_maior -> viewModel.selectAllStockWithFilter(StockViewModel.ORDER_BY_ASC)
            R.id.menu_stock_toolbar_filter_finalized -> viewModel.selectAllStockWithFilter(StockViewModel.FINISHED)
            R.id.menu_stock_toolbar_filter_opened -> viewModel.selectAllStockWithFilter(StockViewModel.UNFINISHED)

        }

        return true
        
        //NOTHIN HAPPENS AFTER CHOOSING ONE

    }

} 

When i enter the activity, all the data is fetched normally, but when i click on a menu item to apply some filter on it, nothing happens, the data doesnt change. How can i fix this?

Upvotes: 0

Views: 1508

Answers (1)

homerman
homerman

Reputation: 3569

allStocks may seem dynamic because it's LiveData, but remember that it's still a reference to an object in memory. When StocksActivity is created, it observes allStocks in it's initial state. For the sake of simplicity, let's say allStocks is pointing to an object in memory with the address of "A". When selectAllStockWithFilter() is eventually invoked, the allStocks handle is updated to point to a new instance of LiveData living in memory at address "B". The problem you're facing is that StocksActivity is still observing "A". Nothing communicated that the allStocks handle itself has been changed.

One way to resolve this would be to change allStocks into an instance of MutableLiveData. Subsequently, whenever the contents of this allStocks should be updated, instead of reassigning allStocks, you would update it's internal "value". This allows the ViewModel to pump new/updated values through the same LiveData object instance that StocksActivity is observing.

Something like this:

class StockViewModel(private val repo: StockRepository) : ViewModel() {

    ...

    val allStocks = MutableLiveData<List<Stock>>().apply { value = repo.selectAll() }


    ...

    fun selectAllStockWithFilter(filter: Int) {
        when (filter) {
            ALL -> allStocks.postValue(repo.selectAll())
            ORDER_BY_DESC -> allStocks.postValue(repo.selectAllOrderByDesc())
            ORDER_BY_ASC -> allStocks.postValue(repo.selectAllOrderByAsc())
            FINISHED -> allStocks.postValue(repo.selectAllFinished())
            UNFINISHED -> allStocks.postValue(repo.selectAllUnfinished())
        }
    }

    ...

}

Upvotes: 1

Related Questions