How to use parameter fields in Room #Query? - android

I have a User class with a field id, so I wanted to run the following query with Room:
#Query("SELECT * FROM ticket where user_id = :user.id")
LiveData<Ticket> loadFromUser(User user);
But I am getting error marks on Android Studio on user.id and all examples I find online only use the direct parameter of the #Query method, usually a String or an int.
Is it possible to use an object's field in a Room #Query? If positive, so what's the proper way of referencing it.

You can't pass parameters like that to room. It does not support a full expression language. You have to use primitive types to pass parameters. Like this,
#Query("SELECT * FROM ticket where user_id = :user_id")
LiveData<Ticket> loadFromUser(String user_id);

A simple solution is to create two other functions, one is for user_id and one is for user as follows:
#Query("SELECT * FROM ticket where user_id = :user_id")
LiveData<Ticket> loadFromUser(String user_id);
#Transaction
LiveData<Ticket> loadFromUser(User user){
return loadFromUser(user.id);
}

in my case i used #RawQuery
in DAO you can write
#RawQuery
LiveData<Ticket> loadFromUser(SupportSQLiteQuery query);
and create your query and pass to it.
SimpleSQLiteQuery query = new SimpleSQLiteQuery("SELECT * FROM ticket where user_id = ?") , new Object[]{user.id})
and pass this query to DAO method.
userDao.loadFromUser(query)

Related

Android SQLite dynamically insert sorting (ASD or DESC), is it possible?

I'm trying to use one method for dao (Room) to get items by a specific sorting dynamically, but compiler gives an error for the next SQL query
So it's not possible? Do I have to create duplicate methods with different sorting?
you can not perform if and else logic in the sqlite query
You should use #RawQuery like this:
#Dao
interface RawDao {
#RawQuery
fun getTestItems(SupportSQLiteQuery query): DataSource.Factory
}
// Usage of RawDao
// for example set: restOfQuery = sortBy + "ASC"
val query = SimpleSQLiteQuery(
"SELECT * FROM Items ORDER BY ?",
new Object[]{ restOfQuery });
val result = rawDao.getTestItems(query);
Or another way is that you use multiple functions for multiple orderings.

Return type of GROUP BY statement in Room Database

I would like to make the following query to my database:
SELECT type, COUNT(*) FROM offerings GROUP BY type
This query works well with an Sqlite browser. Now I want to use this query in my Dao:
#Query("SELECT type, COUNT(*) FROM offerings GROUP BY type")
LiveData<Map<String, Integer>> getOfferingsGroupedByType();
But I am getting the error: ... not sure how to convert a cursor to this method's return type
How can I query a table with 2 columns? --> that is, [type, count(type)] ?
Step #1: Give a name to the count: SELECT type, COUNT(*) AS count FROM offerings GROUP BY type
Step #2: Create a Java class with suitable fields:
public class Thingy {
public String type;
public int count;
}
Step #3: Have your return type from the DAO method use that class:
#Query("SELECT type, COUNT(*) FROM offerings GROUP BY type")
LiveData<List<Thingy>> getOfferingsGroupedByType();
I don't recall Room supporting returning a Map, so you will need to handle that aspect yourself, either in the observer or via a MediatorLiveData that wraps the LiveData you get from the DAO and does the conversion.

Android Room DB - use static variable from another class in query

When I write a query in DAO class can I use a static variable from another class (or enum)? If it's possible, please tell me HOW?
I mean something like this:
public enum MessageState {
NOTHING,
PENDING,
SEND
}
and in query statement in DAO class:
#Query("SELECT * FROM message_db WHERE state = :MessageState.PENDING.ordinal()")
Pass your enum to the function as a default parameter:
#Query("SELECT * FROM message_db WHERE state = :state")
fun getMessages(state: Int = MessageState.PENDING.ordinal()): List<Message>
This way you won't need to provide it on every call, but achieve the same result.
You can concatenate it just like a normal String, in Java:
#Query("SELECT * FROM message_db WHERE state = " + MessageState.PENDING.ordinal() + ")"
If you are using Kotlin:
#Query("SELECT * FROM message_db WHERE state = ${MessageState.PENDING.ordinal()}")

Return Map object by DAO method

I would like to have method like this in my dao object
#Query("SELECT c.name, sum(p.value) FROM payments p, paymentCategories c WHERE p.categoryId = c.id GROUP BY c.name")
fun getCategoryStats(): Map<String, Float>
but i get the error
error: Not sure how to convert a Cursor to this method's return type
public abstract java.util.Map
Is it possible to change it to working version?
So it can be different type to return but the main conditions are
It must be only one query in db
I would like to avoid extra code like creating additional data structure only for this method
I'm little late for a party but maybe someone will still search for it.
From Room 2.4 we are able to use multimap return type with annotation #MapInfo which allow us to define the mapping for key and value.
In your case it will be sth like this:
#MapInfo(keyColumn = "name", valueColumn = "sum")
#Query("SELECT c.name AS name, sum(p.value) AS sum FROM payments p, paymentCategories c WHERE p.categoryId = c.id GROUP BY c.name")
fun getCategoryStats(): Map<String, Float>
More info:
https://developer.android.com/training/data-storage/room/accessing-data#multimap
While I don't think this can be done in the Dao it can easily be done when querying the LiveData in the repository or the viewModel (or where ever you are querying the list) using a transformation. Since I don't know your datanames I'm using made up ones:
val categoryStatsMap: LiveData<Map<String, Float>> =
Transformations.map(
database.categoryStatsDao.getCategoryStats()) {it ->
it.map {it.key to it.value}.toMap()
}
'it.key' and 'it.value' are the fields in the entity object that you want to use as... key and value pairs in the map.
Transformations give you a live data object based on another live data object. I don't know what the overhead is, but I assume it shouldn't be too big.

Dynamic Order By using Room

I want to create a dynamic query using room so that in one case the query returns a particular order type and during runtime if the order type changes then a new query is created and the data is returned ordered according to this type.
I am returning a DataSource.Factory object using Room.
I am using the below statement to process my query:-
if(getSortOrderType().equals(context.getString(R.string.sortByPopular))) {
dbSortType = "popularity";
} else {
dbSortType = "vote_average";
}
movieLiveData =
new LivePagedListBuilder<>(MovieDatabase
.getMovieDbInstance(context)
.getMovieDao().getAllMovies(new
SimpleSQLiteQuery("SELECT * FROM main_movie ORDER BY ? DESC",
new Object[]{dbSortType})), config)
.setBoundaryCallback(movieModelBoundaryCallback)
.build();
But, during runtime I see that the data being returned is ordered by the already set Primary Key i.e id and not according to this type that I am constructing in the above statement.
How to use the statement to return the result sorted by the sort type selected.
The Dao method used is:-
#RawQuery(observedEntities = MovieModel.class)
DataSource.Factory<Integer,MovieModel> getAllMovies(SupportSQLiteQuery query);
Okay so, I have found a simple answer to that.
I just replaced the statement --
new SimpleSQLiteQuery("SELECT * FROM main_movie ORDER BY ? DESC", new Object[]{dbSortType})
to this:-
new SimpleSQLiteQuery("SELECT * FROM main_movie ORDER BY "+ dbSortType + " DESC"))
First of all check that getAllMovies function in your DAO is like this:
#RawQuery
fun getAllMovies(query: SupportSQLiteQuery): List<MainMovie>
and if your return type is observable types like liveData, PagingSource, etc. :
#RawQuery(observedEntities = [MainMovie::class])
fun getAllMovies(query: SupportSQLiteQuery): PagingSource<Int, MainMovie>
then use this:
val myQuery=SimpleSQLiteQuery("SELECT * FROM main_movie ORDER BY "+ dbSortType + " DESC")
getMovieDao(). getAllMovies(myQuery)

Categories

Resources