android - How to get simple list from db using Room? - android

I am using Component libraries in my android app. in some case it is needed to use Livedata and observe its data but sometimes I just want to get some ordinary list not Livedata , How can I do that? query DB in simple way
p.s : I use getValue() but it returns null

Use query like this in DAO:
#Query("SELECT * FROM TABLE_NAME")
fun getListOfData(): List<Data>?
this will provide you list of data from your table, just like the select query passed in #Query parameter.
Edit:
When calling from main thread, you can use handler to do your job in background like below:
//Method from where you want your data from Db.
fun getMyList() {
Thread {
(your db object).(your dao).getListOfData()
}.start()
}
or you can allow your db to execute on main thread when building your room db like below (Though i wouldn't recommend this) :
Room.databaseBuilder(
...
)
.allowMainThreadQueries()
.build()

You can simply write query in your Dao which has return type as List and call from your ViewModel where you need those data.
Example :
//YourDao
#Query("SELECT * FROM YourTable")
List<YourModel> getAllYourTableData();
//YourRepo
public static List<YourModel> getAllData(){
return getYourModelDao.getAllYourTableData();
}
//Your ViewModel
public void someFunctionWhereYouNeedNormalData(){
//assign to list
YourRepo.getAllData();
}
Assuming you have knowledge about repo pattern in android arch components.

Related

Room returning blank object

I'm posting my room query result using the below code
_data.postValue(databaseImpl.findRepoById(id).value)
But it seems the returned object is empty. I'm guessing we need to observe databaseImpl.findRepoById(id), not access it directly but since it's in a viewmodel I don't have a lifecycleOwner to assign to the room query livedata.
databaseImpl.observe(this, Observer { //!!this is modelview and not activity
_data.postValue()
})
What is the right way to use Room query and update a mediator live data?
You could try use coroutines to query your data so you can have something like this for your DAO:
#Query("SELECT * FROM users")
fun findRepoById(id): Flow<User>
In your DatabaseImpl class return have it as:
fun findRepoById(id): Flow<User> = userDao.findRepoById(id)
Then in your ViewModel you could do this:
viewModelScope.launch {
databaseImpl.findRepoById(id).map { _data.postValue(it) }
}
Have a look at this article Room and Coroutines by Florina Muntenescu
on how you could set up coroutines and use it with room.

Android - Kotlin Coroutines for handling IllegalStateException: Cannot access database on the main thread

In my Android App, I use Room as local database to store the Account information of a user. When I make a simple Room request to retrieve the Account object stored in the database, I get the following error message :
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
Here, is the Fragment code from which I make the local database request:
// AccountInformationFragment.kt
accountDataFragmentViewModel.retrieveAccountData(accountId).observe(viewLifecycleOwner, Observer {
// do some stuff
})
In the ViewModel class I have implemented retrieveAccountData() like this:
// AccountInformationFragmentViewModel.kt
// used to get the account from the local datasource
fun retrieveAccountData(id:Long): LiveData<Account>{
val result = MutableLiveData<Account>()
viewModelScope.launch {
val account = authRepository.retrieveAccountData(id)
result.postValue(account)
}
return result
}
In the Repository class, I have implemented retrieveAccountData() like this:
// AccountRepository.kt
suspend fun retrieveAccountData(accId:Long): Account =
accountDao.retrieveAccountData(accId)
I understand that I have to use some sort of asnyc operation because the local database operation may take a long time when its performed on the main thread.
But in the ViewModel class I launched the coroutine inside the viewModelScope. Is that not enough? Based on the exception, it seems not. So, is there someone who could tell me how to do this correctly.
EDIT:
Here is the Dao class :
#Query("SELECT * FROM account_table WHERE id = :id")
fun retrieveAccountData(id: Long) : Account
Thanks in advance
As per the Room documentation, if you want Room to automatically move to a background thread to run your #Query, you can make your method a suspend method:
#Query("SELECT * FROM account_table WHERE id = :id")
suspend fun retrieveAccountData(id: Long) : Account
RoomDB supports LiveData. You could return the query result as a livedata which is by default does the operation in the background thread and observe it in your UI layer. I have modified your query below which will return LiveData instead of Account.
#Query("SELECT * FROM account_table WHERE id = :id")
fun retrieveAccountData(id: Long) : LiveData<Account>

Android RoomDatabase get SupportSQLiteDatabase exception

I'm using Room and I need to perform a database migration. I've migrated the data but I have a problem in one of the columns. When the migration is performed, the data for that column may still be unavailable.
When the user enters the data needed for that column, I have to get all rows that match a value in that column, update these values by the one provided by the user and drop all other rows that do not match.
I can have a method in my UserDao but the problem is that this does not seem correct because it's a one time only thing and I don't what to expose the method so my idea was to get the database instance and try to do the change myself.
When I use
var myDatabase = Room.databaseBuilder(....)
.addMigrations(... .build()
I keep a reference to it but then, when I do myDatabase.openHelper.writableDatabase I'm always getting an exception
getDatabase called recursively
Any idea how to handle this?
Your issue is that you are trying to use the MyDatabase's openHelper to try to get the database when building the instance of MyDatabase which is in the process of getting the database, so while getting the database you are then trying to get the database.
Instead you need to use the SupportSQLiteDatabase that is passed to the Migration.
As ean example :-
#Database(
version = 1,
entities = [
MyTableEntity::class
]
)
abstract class AppDatabase : RoomDatabase() {
abstract fun MyTableEntityDao(): MyTableEntityDao
companion object {
val MIGRATION_V1_V2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
//........ code using the already opened database
database.execSQL(????????); //<<<<<<<<<< USES the support database
}
}
}
}
This would then be invoked using something similar to :-
var myDatabase = Room.databaseBuilder(applicationContext,AppDatabase::class.java,"mydatabase")
.allowMainThreadQueries()
.addMigrations(AppDatabase.MIGRATION_V1_V2)
.build()

Function returning void with liveData

I'm using Room to handle my local database, and LiveData to handle the DAOs.
So I'm using LiveData as thread handler for my transactions.The question is how can I do insert and updates with LiveData? Or generally how can void functions return LiveData in the Room?
#Query("select * from table")
fun getAll(): LiveData<List<T>>
#Insert
fun insert(T data): LiveData<?> // What should be the generic, since it's void?
In RxJava we have something like this:
#Insert
fun insert(T data);
The question is how can I do insert and updates with LiveData? Or generally how can void functions return LiveData in the Room?
You can't use LiveData object itself to insert or update data in your database (db). LiveData is a wrapper around the object loaded from db which can be observed.
In order to perform insert/update operations you need to pass the object annotated with #Entityannotation to your DAO method parameter. You can do it either by calling LiveData#getValue(), or observe the LiveData in Activity, or Fragment and update a local variable via the observer and pass said variable.

is this the correct way of LiveData usage?

I am currently applying Room + ViewModel + LiveData to my project.
In my app, there is "obviously" observe data that is needed, but not all.
The code below is example code for category data. In my situation, category data does not change and always maintains the same value state (13 categories and content does not change). Categories are data that is loaded from the Database through the CategoryItemDao class.
Does category data need to be wrapped with livedata?
Or is there a reason enough to use LiveData in addition to its observerable feature?
I've read the guide to LiveData several times, but I do not understand the exact concept.
CategoryItemDao
#Dao
interface CategoryItemDao {
#Query("SELECT * FROM CategoryItem")
fun getAllCategoryItems(): LiveData<MutableList<CategoryItem>>
}
CategoryRepository
class CategoryRepository(application: Application) {
private val categoryItemDao: CategoryItemDao
private val allCategories: LiveData<MutableList<CategoryItem>>
init {
val db = AppDatabase.getDatabase(application)
categoryItemDao = db.categoryItemDao()
allCategories = categoryItemDao.getAllCategoryItems()
}
fun getAllCategories() = allCategories
}
CategoryViewModel
class CategoryViewModel(application: Application) : AndroidViewModel(application) {
private val repository = CategoryRepository(application)
private val allCategories: LiveData<MutableList<CategoryItem>>
init {
allCategories = repository.getAllCategories()
}
fun getAllCategories() = allCategories
}
This is fine, but you can make a few changes:
Change LiveData<MutableList<CategoryItem>> to LiveData<List<CategoryItem>>. Don't use a MutableList unless you really have to. In your case, List would work fine.
In your CategoryRepository instead of fetching in init, do it during the getAllCategories() call. So change your code like this: fun getAllCategories() = categoryItemDao.getAllCategoryItems()
Similarly do the same in CategoryViewModel as well. Change you code to: fun getAllCategories() = repository.getAllCategories()
A common misconception is to use LiveData only when the data changes. But that's not true. Your 13 categories may not change, but that's in a database. So if you were to accomplish this without a LiveData you have to query the DB and populate the view in the main thread, or you need to wrap this around in a background thread. But if you do this via LiveData, you get the Asynchronous Reactive way of coding for free. Whenever possible, try to make your view observe a LiveData.

Categories

Resources