How to put integer literals into Room query? - android

Is it possible to put integer literals into Room query? Following query leads to build error:
#Query("UPDATE topics SET unreadCount=0 WHERE id=:chatId")
fun resetUnreadCount(chatId: Long)
I have tried several options (including RawQuery) and came to an option of passing Int argument with default value:
#Query("UPDATE chats SET unreadCount=:ZERO WHERE id=:chatId")
fun resetUnreadCount(chatId: Long, ZERO: Int = 0) // hacky way to pass int literal into the query
Is there a normal way to do that?

Please check this documentation.
https://www.sqlite.org/datatype3.html. Look in section 3.4, it talks about coumn affinity behviour
unreadCount='0'
This should also work
#Query("UPDATE topics SET unreadCount='0' WHERE id=:chatId")

First query has a different Entity class 'topics'
#Query("UPDATE topics SET unreadCount=0 WHERE id=:chatId")
Should it be chats too? as the below query is working. integer in the query is fine.
#Query("UPDATE chats SET unreadCount=:ZERO WHERE id=:chatId")

Related

Changing primaryKeys order changes the order of the data saved in ROOM

I have a situation where I need some information about my results, in my code I have this data class/Room's entity.
#Entity(primaryKeys = ["searchId","page","pr_id"])
data class ProductResponse(
var searchId: Int,
val page: Int,
#Embedded(prefix = "pr_")
val products: ProductSearchFormatted
)
Where you can see in the Android Studios App Inspector:
The data is saved as spected in ROOM, when I try to load it from ROOM:
#Query(
"SELECT * FROM PagedSearchResponse WHERE searchId ==:input"
)
fun loadPagedSearchResponse(input: Int): PagingSource<Int, ProductResponse>
I just need the data in the same order that was previously saved, and got the data in different order (ordered by pr_id):
I found out that if I change the primaryKeys order, like
#Entity(primaryKeys = ["pr_id","searchId","page"])
data class ProductResponse(
var searchId: Int,
val page: Int,
#Embedded(prefix = "pr_")
val products: ProductSearchFormatted
)
Now the data's order from ROOM is correct.
Why does this happen? Does the primaryKeys order matter?
Changing primaryKeys order changes the order of the data saved in ROOM
NO it does not, the data is ALWAYS saved in the order in which it is inserted. What is changing in your case, is the ORDER in which the data is extracted. That is because you aren't saying in what ORDER you want the data to be extracted and are leaving that choice to the query planner.
Why is this happened?, does the primaryKeys order matter?.
Yes it can do, especially in the absence of other indexes. Certainly pr_id before search_id will make a difference as the order within the index will be different and that as the WHERE clause is on the search_id then it is likely that the primary key index will be used (as in both cases search_id is an initial column (see the links below))
The query planner is an AI that tries to pick the fastest and most efficient algorithm for each SQL statement.
see :-
https://www.sqlite.org/queryplanner.html
https://www.sqlite.org/optoverview.html
https://www.sqlite.org/queryplanner-ng.html
If you want data to be in ORDER then you should specify an ORDER clause (ORDER BY ....). That is the only way to guarantee an ORDER. Assuming an ORDER without an ORDER clause will very likely result in issues.
Saying that using pr_id prior to search_id makes the composite (multiplte column) index likely to be more beneficial as the pr_id (according to the data shown) is less repeated than the search_id.
The way I fix it, it was simpler than I expected.
In the entity, I added a field called "order", and in my Mediator (I'm using Paging 3) basically did this:
list.mapIndex { s,t ->
ProductResponse(
...
order = s
...
)
}
That way, I'm following the EXACT order from Backend without the need to modify any primaryKeys.

Android Room dynamic Where clause

I would like to create fully dynamic where clause in Room.
Is there any way to make something like code below to work and return number of updated columns?
#Query("UPDATE table SET column = ${xyConstVal} WHERE :where")
fun updateTable(where:String):Int
Thanks.
I would like to create fully dynamic where clause in Room.
Instead of using #Query annotation in dao you could try using #RawQuery which accepts SupportSQLiteQuery as value then write your query in old fashioned way
Is there any way to make something like code below to work and return
number of updated columns?
A method, annotated with #Insert can return a long. This is the newly generated ID for the inserted row. A method, annotated with #Update can return an int. This is the number of updated rows.
In your case you can combine both

Room database onConflict = OnConflictStrategy.REPLACE not working

I am working on Room database and trying to insert list of items(eg. list of Quotes which contains author name and a quote in my case).
Following is the code I am using:
// view model
BaseApp.daoInstance?.appDao()?.insertQuotes(response!!)
// dao
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertQuotes(listData: MutableList<Quote>)
When I try to insert the same data again, it always inserts as a new data instead of replacing with the current items.
I have researched a lot for this OnConflictStrategy.REPLACE but could not find any proper answer.
Is there anyone facing the same issue and found solution or am I doing anything wrong?
Thank you in advance...!!!
Room, will not check and compare if you have the quote already in the DB.
What it will do is look if the primary key already exists in the DB if it does, Room will replace all old data with the new one.
In your case, you are not specifying an ID so the DB is generating a unique one for you.
What you should do is create a Query that will search for this quote in the DB something like this:
#Query("SELECT * from quote_table WHERE author = :author AND quote = :quote")
List<Quote> getQuoteByAuthorAndQuote(string author, string quote);
This should return a list with a single quote if one is found and empty if it does not exist.
If you would like to override the old one just update the data in the Quote POJO and insert it to the DB using Room.
Have you tried to index your main column and mark it as unique?
#Index(value = {"quote"}, unique = true)}
It suppose to search for your unique or primary key and compare then replace, while in your case you're not defining an ID so it will generate a unique one for you, so it won't even compare and will consider any item as a new one.
Write a new query and function to solve this issue.
When I had same problem, changes in imports did the trick, added following import:
import androidx.room.*;

Room - Select query with IN condition?

Is it possible to use SQLite's IN condition with Room?
I'm trying to select a list of items from my database where the value of a certain column (in this case a TEXT column) matches any one of a set of filter values. That's pretty easily done in SQL and SQLite, by my knowledge, just by adding an IN condition to your SELECT statement (see here). However, I can't seem to make it work with Room.
I keep getting this error:
Error:(70, 25) error: no viable alternative at input 'SELECT * FROM Table WHERE column IN :filterValues'
(where the input to the DAO #Query-annotated method is called filterValues)
I have tried three different methods now:
Passing the argument as a List<String>
Passing the argument as a String[]
And lastly passing the argument as simply a String, but formatted as (value_1, value_2, ..., value_n)
The last one in particular should work easily, as it will (or at least, it should) directly translate to SELECT * FROM Table WHERE column IN (value_1, value_2, ..., value_n), which is the exact way you would manually write out the SELECT if you were just accessing the database directly.
So as I was preparing to submit this, I double-checked a bunch of the stuff I had looked up previously and found the thing I had somehow missed and would have saved this question from being necessary.
As it turns out, both of these options:
Passing the argument as a List<String>
Passing the argument as a String[]
are viable (and you can replace String with any type the database can represent, such as char or int), you simply need to change the syntax in the #Query annotation from this:
#Query("SELECT * FROM Table WHERE column IN :filterValues")
to this:
#Query("SELECT * FROM Table WHERE column IN (:filterValues)")
Easy as pie, right?
Note that the third method above (passing the argument as simply a String, but formatted as (value_1, value_2, ..., value_n)) does not appear to be supported by Room, but that's probably not a bad thing, since that's the hard way.
Since I already had the whole thing typed out, I figured I would leave the question up in case other people are have as much difficulty finding this solution as I did and stumble upon this question.
Hi you can use this query:
#Query("SELECT * FROM user WHERE uid IN(:userIds)")
public abstract List findByIds(int[] userIds);
or
#Query("SELECT * FROM user WHERE uid IN(:userIds)")
public abstract List findByIds(List<Integer> userIds);
Similarly to above answers in Kotlin you can use vararg instead of array or list:
#Query("SELECT * FROM user WHERE uid IN (:userIds)")
fun getUsers(vararg userIds: Int): List<User>?
and use it like repository.getUsers(1, 2, 3).
If needed, to convert vararg to list see https://proandroiddev.com/kotlins-vararg-and-spread-operator-4200c07d65e1 (use *: format(output, *params)).

Hardcode Boolean Query In Room Database

I'm building an Android application that displays a list of potential matches for a user. The user can click on one to like the user, and I save all of those likes locally.
I can write a query to get the list of matches like this:
#Query("SELECT * FROM match WHERE liked = :liked ORDER BY match DESC LIMIT :limit")
fun getMatches(limit: Int = 6, liked: Boolean = true): Flowable<List<Match>>
I've learned that this works fine. However, I don't foresee any scenario where I'll ever set liked to false, and so I'm curious if there is a way to hardcode my Boolean condition? If I try:
#Query("SELECT * FROM match WHERE liked = true ORDER BY match DESC LIMIT :limit")
I get the following error at compile time:
Error:(8, 0) Gradle: error: There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (no such column: true)
How can I hard code this Boolean in my query string?
I have also tried:
Wrapping the condition in single quotes
#Query("SELECT * FROM match WHERE liked = 'true' ORDER BY match DESC LIMIT :limit")
SQLite does not have a boolean data type. Room maps it to an INTEGER column, mapping true to 1 and false to 0.
So, I would expect this to work:
#Query("SELECT * FROM match WHERE liked = 1 ORDER BY match DESC LIMIT :limit")
Bear in mind that this behavior is undocumented. However, it shouldn't change — at least not without alarm klaxons sounding — as we'd need to use migrations to deal with any changes.
CommonWare's approach does work and also answers the OPs question directly; however, I'm not a fan of making such an assumption about the database. The assumption should be safe, but it may create unexpected work down the road if Room ever decides to change it's boolean implementation.
I'd suggest that the better approach is to not hardcode the boolean 1 or 0 into the query. If the database is behind a repository, it is still possible for the repository to expose a graceful API. Personally, I think shielding the larger codebase from the database implementation is a good thing anyways.
Dao Method (copied from OP's question)
#Query("SELECT * FROM match WHERE liked = :liked ORDER BY match DESC LIMIT :limit")
fun getMatches(limit: Int = 6, liked: Boolean = true): Flowable<List<Match>>
Repository
class Repository {
public Flowable<List<Match>> getLikedMatches() {
return dao.getMatches(6, true);
}
}
Of course, this is an opinionated option in that it assumes a certain architectural style. However, it does not make assumptions about the internal database. Even without the repository shielding the database, the call can be made into the database by passing true everywhere - also without making assumptions as to the underlying data.
You don't have to compare boolean column to a value. Just use the column value itself as a boolean expression. You can easily change your query to SELECT * FROM match WHERE liked ORDER BY match DESC LIMIT :limit.
If you want to compare to false value you can use following expression: where not liked.
#Query("SELECT * FROM searched_data_table WHERE favourit_history==1 ORDER BY lang_id DESC")
Use this query to search data from the table this will give you data in descending order with respect to your key value

Categories

Resources