MU'men Ahmed
MU'men Ahmed

Reputation: 159

Android Room Select query returns null or empty strings even the database is not empty

I'm new to android and room. I'm trying to make a local db but I'm struggling at this point where the database is not empty as the image below proves. But when I try to select any data it returns an empty list or null values. Note that the insert query works fine. enter image description here

Code: Entity:

@Entity(tableName = "product_table")
@Parcelize
data class Product(

    @PrimaryKey
    @SerializedName("id")
    val id : String,
    @SerializedName("title")
    val title : String,
    @SerializedName("price")
    val price : String,
    @SerializedName("category")
    val category : String,
    @SerializedName("description")
    val description : String,
    @SerializedName("image")
    val image : String,

    val color : String,
    val size : String
): Parcelable

Dao:

@Dao
interface CartDao {

    @Query("SELECT * FROM product_table")
    fun get_all_carts(): LiveData<List<Product>>

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert_item_to_cart(product: Product) : Long

    @Delete
    suspend fun delete_item_from_cart(product: Product)


    @Query("Delete FROM product_table")
    fun delete_all_cart()

}

Database:

@Database(entities = [Product::class], version = 1)
abstract class ProductDatabase : RoomDatabase() {

    abstract fun productDao(): CartDao

    companion object{
        @Volatile
        private var INSTANCE: ProductDatabase? = null

        fun getDataseClient(context: Context) : ProductDatabase {

            if (INSTANCE != null) return INSTANCE!!

            synchronized(this) {

                INSTANCE = Room
                    .databaseBuilder(context, ProductDatabase::class.java, "product_database")
                    .fallbackToDestructiveMigration()
                    .build()

                return INSTANCE!!

            }
        }
    }
}

Repository:


class CartRepository {

    companion object {

        var productDatabase: ProductDatabase? = null

        var product: LiveData<Product>? = null

        fun initializeDB(context: Context) : ProductDatabase {
            return ProductDatabase.getDataseClient(context)
        }

        fun get_all_data(context: Context) : List<Product> {

            productDatabase = initializeDB(context)
            var temp_list = emptyList<Product>()

            CoroutineScope(Dispatchers.IO).launch {
                temp_list = productDatabase!!.productDao().get_all_carts()
            }
            return temp_list
        }

        fun get_first_item(context: Context, input_id : String) : Product {

            productDatabase = initializeDB(context)
            var temp_item : Product = Product("","","","","","","","")

            CoroutineScope(Dispatchers.IO).launch {
                temp_item = productDatabase!!.productDao().get_item(input_id)
            }
            return temp_item
        }



    }
}

View Model:


@HiltViewModel
class CartFragmentViewModel @Inject constructor(
    private val productDao : CartDao
) : ViewModel() {

    var products_list : MutableLiveData<List<Product>>

    init {
        products_list = MutableLiveData()
    }

    fun get_all_products(context: Context) : List<Product>{

        return CartRepository.get_all_data(context)

    }
    fun get_first_item(context: Context, input_id : String) : Product{

        return CartRepository.get_first_item(context, input_id)

    }
}

Fragment:


class CartFragment @Inject constructor(

) : Fragment(R.layout.cart_fragment_layout) {


    lateinit var cart_list : List<Product>

    val cart_adapter = CartRecyclerViewAdapter()

    val viewModel by activityViewModels<CartFragmentViewModel>()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        rv_cart.adapter = cart_adapter
        rv_cart.layoutManager = LinearLayoutManager(
            activity?.applicationContext,
            LinearLayoutManager.VERTICAL,
            false
        )
        rv_cart.setHasFixedSize(true)

        /*cart_list = viewModel.get_all_products(activity?.applicationContext!!)
        cart_adapter.submitList(
            cart_list
        )
        cart_adapter.notifyDataSetChanged()
        Log.d(TAG, "Fetched Data: {${cart_list.get(1).title}}")*/

        var p = viewModel.get_first_item(activity?.applicationContext!!, "1")
        cart_adapter.submitList(
            listOf(
                p
            )
        )
        cart_adapter.notifyDataSetChanged()
        var s = p.title
        Log.d(TAG, "Fetched Data: {$s}")

        /*viewModel.get_first_item(activity?.applicationContext!!).observe(viewLifecycleOwner, Observer {
            cart_adapter.submitList(listOf(it))
            cart_adapter.notifyDataSetChanged()
        })*/

        //viewModel.get_first_item(activity?.applicationContext!!)
    }
}

There are many comments and logs in the Fragment Class for the sake of trying to figure what the problem is. I can't really know what is happening and when I use LiveData as return type of dao get functions the app crashes. Hope someone can help me out and thanks for your attention.

Upvotes: 4

Views: 2453

Answers (2)

Fayaz Rafeek
Fayaz Rafeek

Reputation: 11

To use a LiveData with room, firstly, you should attach an observer to the live data.

So instead of returning the value of LiveData from the Repository methods, you should return the Live Data object itself, And then observe that Livedata in your Viewmodel class.

Upvotes: 1

Nitin Prakash
Nitin Prakash

Reputation: 977

The problem with your CartRepository methods.

 fun get_first_item(context: Context, input_id : String) : Product {

        productDatabase = initializeDB(context)
        var temp_item : Product = Product("","","","","","","","")

        CoroutineScope(Dispatchers.IO).launch {
            temp_item = productDatabase!!.productDao().get_item(input_id)
        }
        return temp_item
    }

In the above method, you are fetching an item in a background thread which means it goes into another thread and allows the return temp_item to always returns immediately, without blocking the code to wait for a result so that's why you are getting null and or empty list.

Solution is :

Make all database operation methods in CartRepository as suspended, see below: Note: I use an object instead of class

object CartRepository {


    var productDatabase: ProductDatabase? = null


    fun initializeDB(context: Context) : ProductDatabase {
        return ProductDatabase.getDataseClient(context)
    }

   suspend fun get_all_data(context: Context) : List<Product> {

        productDatabase = initializeDB(context)
        
     return productDatabase!!.productDao().get_all_carts()
    }

  suspend fun get_first_item(context: Context, input_id : String) : Product {

        productDatabase = initializeDB(context)
        return productDatabase!!.productDao().get_item(input_id)
    }

}

And in your viewModel call this suspend function in viewModelScope like below:

@HiltViewModel
class CartFragmentViewModel @Inject constructor(
    private val productDao : CartDao
) : ViewModel() {



 .....
       var productData = MutableLiveData<Product>()

fun get_first_item(context: Context, input_id: String) {

    viewModelScope.lauch(Dispatchers.IO){
        val data = CartRepository.get_first_item(context, input_id)
        withContext(Dispatchers.Main){
            productData.value = data
        }

    }

}

....

In your fragment call get_first_item first then observe you data productData and you can do the same things for other database operations also by following all steps.

I hope this will help you if you dont understand any code just let me know in the comments and please ignore the typos

Upvotes: 1

Related Questions