I was tinkering with SingleLiveEvent. Is it possible to use it with Room database? I tried using it and got a build error saying Not sure how to convert a Cursor to this method's return type. Are there any workarounds here? I have got an edge case where I would like to use it!
SingleLiveEvent is MutableLiveData which is LiveData. You can return List<LiveData<YourData>> from Room with select query which is invoked in worker thread. No need to work with cursors in Room. get List<LiveData<YourData>> and on observe method send List<YourData> to required class or RecyclerView. What is your edge case for needing cursor?
Caution: It's highly discouraged to work with the Cursor API because
it doesn't guarantee whether the rows exist or what values the rows
contain. Use this functionality only if you already have code that
expects a cursor and that you can't refactor easily.
However, you can get it with
#Dao
public interface MyDao {
#Query("SELECT * FROM user WHERE age > :minAge LIMIT 5")
public Cursor loadRawUsersOlderThan(int minAge);
}
Source
Related
in my Dao I've defined a Query like this to check whether the database is empty or not:
#Query("SELECT * from meal_table LIMIT 1")
suspend fun getAnyMeal(): LiveData<Array<Meal>>
Within my populateDatabse function I would like to check, whether any item is inside my database with something like this:
suspend fun populateDatabase(mealDao: MealDao) {
if ((mealDao.getAnyMeal()).size < 1)
...
}
Unforunately size doesnt work in this context unless I am doing something wrong.
If someone has a tipp on how to solve this I would apreciate it! Thank you!
Unforunately size doesnt work in this context
It is because getAnyMeal returns you LiveData which has no property named size. LiveData is an observable object. It means that LiveData object you get by calling this method will return to its observers (only the ones who "subscribed" to updates) an array of Meal objects when this array will be available.
First of all, when you are using LiveData with Room you sort of giving Room a signal that you are not requesting a response immediately. LiveData is used when you want to get updates in future that will happen on change of any object in DB. Also you may want to use LiveData when you want to execute the SELECT query asynchronously. It means you call getAnyMeal method and it does not block UI thread.
When you are using suspend keyword you can remove LiveData from return type. When suspend function is executed it will synchronously return you the result.
Solution
Update getAnyMeal() to the next form or create the new method as it is declared below:
#Query("SELECT * from meal_table LIMIT 1")
suspend fun getAnyMeal(): Array<Meal>
If you declare getAnyMeal method like this you will be able to call size property on the return type as it directly returns you an array.
On LiveData with Room:
Room doesn't support database access on the main thread unless you've called allowMainThreadQueries() on the builder because it might lock the UI for a long period of time. Asynchronous queries—queries that return instances of LiveData or Flowable—are exempt from this rule because they asynchronously run the query on a background thread when needed.
Observable queries with LiveData for more info.
I have a social networking app which displays a list of users, and am looking to have an efficient way of being able to retrieve an object from my LiveData using its primary key.
Example: Retrieve a set of User POJOs from within my LiveData<List<User>> given a LIST of userId Integers (ie, users 12, 5, 7, and 1). I need to be able to look up these users by the userId for display in the appropriate order in the UI.
I believe I want something more like LiveData<Map<Integer, User>>, but how could I implement this using the Room database, without breaking the LiveData callbacks from my local DB -> Room -> LiveData -> UI?
PROPOSAL 1:
Change my Room implementation to somehow return a LiveData containing a HashMap of <userId,User>.
Current Room implementation:
#Query("SELECT * FROM users WHERE user_id in :userIds LIMIT 1")
LiveData<List<User>> getUsers(List<Integer> userIds);
Proposed Room implementation (no idea if something like this is possible or what it would even look like):
#Query("SELECT * FROM users WHERE user_id in :userIds LIMIT 1")
LiveData<**HashMap**<Integer,User>> getUsers(List<Integer> userIds);
PROPOSAL 2:
Have a list of many LiveData objects WITHIN a Map:
Map<Integer,LiveData<User>> liveDataUsers;
This might be something to look into, but I'm worried that having potentially hundreds/thousands of LiveData objects within a map is bad design and could also lead to performance issues / too many open LiveData internal callback threads.
PROPOSAL 3:
Something else??? I feel like I am missing something easy here. How are others looking up objects within their LiveData using only their primaryKey?
edit: this is something I'd like to achieve at the Repo / Model level and not at the activity level, as this LiveData will be re-used throughout the app.
Problem:
I am using Room Persistence Library and so far everything is working fine except that there is a data from select query which I need synchronously as I am calling it from a Periodic Job (Work Manager's Worker). I have defined the return type to be LiveData as I am also accessing it for display purposes in UI and so observers are great for that but now I also need the same data in Job.
Code Snippet
#Query("SELECT * from readings ORDER BY date, time ASC")
LiveData<List<Reading>> getAllReadings();
Tried
I have tried the getValue() method in LiveData but it returns null as the data is not loaded in LiveData while making the query.
readingDao().getAllReadings().getValue() // returns null
Possible Solution
There is only one solution that I can think of which is to duplicate the getAllReadings query with a different name and return type (without LiveData) but I don't think this is a clean approach as it increases duplication of code just to get a synchronous return type.
Please let me know if there is any other solution or perhaps some way to synchronously access data from LiveData variable.
You can allow main thread query when you initialize Room DB, but it's clearly not desirable. This will give you the synchronous behavior but will block user interface. Is there a specific reason you want this to be synchronous?
The reason why getValue() is returning null is because Room is querying data asynchronously. You can attach an observer or a callback function to get result when the query is finished. You can display the result to the UI or chain another call for sequential operation etc from there.
I use RxJava to wrap my query request for asynchronous query but I you can also use AsyncTask.
how do I VACUUM my RoomDatabase for my Application?
I build my entire Application around Room and at a certain point one big table gets deleted reguarly and later filled again.
I tried to create an additional DAO-Interface with the Query:
#Dao
public interface GenericDao {
#Query("VACUUM")
void vacuum();
}
but I get the Error:
Error:(13, 10) error: UNKNOWN query type is not supported yet. You can use:SELECT, UPDATE, DELETE
Is there a workaround?
Basically what I need is, that once my Table gets completely emptied, Autoincrement starts at 1 again.
I'm fairly new to Database Design, so please be understanding if this is against best Practice.
And yes: I exhausted Google and every other Platform I know.
Many Thanks in advance!
Dao can look as follows :
#Dao
public interface RawDao {
#RawQuery
int vacuumDb(SupportSQLiteQuery supportSQLiteQuery);
}
Run the query like this :
rawDao.vacuumDb(new SimpleSQLiteQuery("VACUUM"));
Is there a workaround?
Call getOpenHelper() on your RoomDatabase subclass, to get a SupportSQLiteOpenHelper. Call getWritableDatabase() on it to get a SupportSQLiteDatabase. Then, since I don't think that VACUUM returns a result set, call execSQL("VACUUM") on the SupportSQLiteDatabase.
Basically what I need is, that once my Table gets completely emptied, Autoincrement starts at 1 again.
IMHO, you should not be making any assumptions about how autoincrement fields get incremented.
I am trying to use RoomDatabase in my Android App. And I am using LiveData to be able to refresh my changes automatically inside my fragment.
The first time I am running my app I am getting the data from the API, creating my RoomDatabase and storing my data.
The second time I run my app I want to check if my DataBase is not empty. But while using LiveData: the following code is returning null.
AppDatabase.getInstance(getContext()).getRecipeDao().getAllRecipes().getValue();
I have read that "if the response is an observable data type, such as Flowable or LiveData, Room watches all tables referenced in the query for invalidation".
How to check if my RoomDatabase has data or is empty?
So after implementing myself I found that you need to do a few things:
Make sure you have an Observer for changes to the LiveData
You need to call observeForever(Observer<T> observer) unless you are using a LiveCyclerOwner then use that instead with: observe (LifecycleOwner owner, Observer<T> observer)
Finally, there is an interesting note on getValue():
Returns the current value. Note that calling this method on a
background thread does not guarantee that the latest value set will be
received
So to reiterate, I think your approach does not work.
You will need to create some type of separate check rather than use a method that returns a LiveData class as noted since it does not guarantee the latest value set is received by calling getValue().
I would recommend something super simple in the end such as adding a new method to your Dao
#Query("SELECT * FROM recipes LIMIT 1")
Recipe getAnyRecipe();
and do this check looking for null to see if anything exists in the recipes table.