I have two tables.
Table Decks:
Decks:
-id
-name
Table Cards:
Cards:
-id
-name
-deckId
I created a data class for this query:
data class DeckWithDueDatedCards(
#Embedded
var deckEntity: DeckEntity,
var list: List<CardEntity>
)
I removed the #Relation top of list, because it's queried all cards.
I want to add a condition. So I don't need all card, from the deck.
Let see:
I created the next Query:
#Query("SELECT *, (SELECT * FROM cards WHERE deckId = D.id AND dueDate < date('now')) as list FROM decks D WHERE D.id = :deckId")
fun getDeckWithDueDatedCards(deckId: Long): Flowable<DeckWithDueDatedCards>
But I got an error, because the inner select is bad. It can't inflate the list.
There is a problem with the query: [SQLITE_ERROR] SQL error or missing database (sub-select returns 8 columns - expected 1)
public abstract io.reactivex.Flowable<....database.entity.DeckWithDueDatedCards> getDeckWithDueDatedCards(long deckId);
I see the issue is the Inner select have 8 item, but it's except only 1. But I have a list there. How to inflate it?
Anyone have idea how to fix this issue? It's possible with query?
Room Dao's allow non abstract method.
If you want to getDeckWithDueDatedCards you can write a method that gonna load all CardEntity first, and then load linked DeckEntity to build your DeckWithDueDatedCards
Java code :
#Query("SELECT * FROM decks WHERE id = :deckId")
public abstract DeckEntity loadDeck(int deckId);
#Query("SELECT * FROM cards WHERE deckId = D.id AND dueDate < :date")
public abstract Flowable<List<CardEntity>> loadCardEntity(String date);
#Transaction
public Flowable<DeckWithDueDatedCards> getDeckWithDueDatedCards(int deckId, String date) {
return loadCardEntity(date)
.map(cardEntityList -> {
List<DeckWithDueDatedCards> res = new ArrayList<>();
for(CardEntity cardEntity : cardEntityList) {
res.add(new DeckWithDueDatedCards(cardEntity, loadDeck(deckId)));
}
return res;
});
}
note: be aware that modification on CardEntity gonna trigger onNext on your subscriber, but modifications on DeckEntity won't ...
edit: if you need to be notify on DeckEntity changes update loadCardEntity query
#Query("SELECT cards.* FROM cards INNER JOIN decks ON cards.deckId = decks.id WHERE deckId = D.id AND dueDate < :date")
public abstract Flowable<List<CardEntity>> loadCardEntity(String date);
Related
I am posting this because same issue is already there on stackoverflow but no solution on this. I am using Room library for db operations. I have created data classes with #Embedded and #Relation with other tables. Now the issue is when I put join queries with multiple where conditions on main as well as joined tables, it returns all/incorrect data of joined tables. This shows it ignores the conditions I have put in a DAO class query. Important thing is when I run the same query on a database externally (using stetho in chrome) it works as expected. Please help me with this as this is highly critical issue. Room version: 2.4.0
This is the data class:
data class ProductFull{
#Embedded val product: ProductMaster,
#Relation(
entity = ProductZone::class,
parentColumn = "productId",
entityColumn = "productId",
)
var productZone: ProductZone? = null,
}
This is the DAO class method:
#Query("select * from ProductMaster as pm inner join ProductZone as pz on pz.productId = pm.productId where pz.zoneId = 3")
abstract suspend fun getTempProducts(): List<ProductFull>
Above query returns data in productZone field of data class having zoneId = 1. Whereas it should only return zones having zoneId = 3.
When using #Relation room builds the underlying query(ies) to get ALL children (ProductZones) for each parent (ProductMaster) that the query selects.
A convenience annotation which can be used in a POJO to automatically fetch relation entities. When the POJO is returned from a query, all of its relations are also fetched by Room.
https://developer.android.com/reference/kotlin/androidx/room/Relation
A get-around is two have 2 dao's one that selects the parents and the other that selects the required children and a function (use an abstract class rather than an interface for the Dao's) that gets the parents using the first query and then for each parent gets the required children using the second query.
The function should be annotated with #Transaction, to allow this also annotate it with #Query("")
You would want something like:-
#Transaction
#Query("SELECT * FROM productmaster JOIN productzone on productmaster.productId = productzone.productId WHERE productzone.zoneId = 3")
abstract fun getTempProducts(): List<ProductFull>
#Query("SELECT * FROM productzone WHERE productId=:productId AND zoneId=3")
abstract fun getTempZone(productId: Long): ProductZone
#Transaction
#Query("")
fun buildFullProducts(): List<ProductFull> {
var rv = getTempProducts()
for (pf: ProductFull in rv) {
pf.productZone = getTempZone(pf.product.productId!!)
}
return rv
}
and use the buildFullProducts function to retrieve the list of ProductFull's
I want to return multiple counts from Room Select query in android.
My query is like
Select Count(CASE WHEN x = '0' or x = '2'), Count(Case when a = '33' or a = '23') FROM my_table WHERE id=10
I want above query to return something as list which will contain values of both the above Count() function. This can be easily done using SQLite but I want to use it in room.
You can give names to the counts and return them as a class, like:
data class MyCounts(
#ColumnInfo(name = "first_count")
val firstCount: Int,
#ColumnInfo(name = "second_count")
val secondCount: Int
)
#Dao
interface MyDao {
#Query("SELECT COUNT(*) as first_count, COUNT(*) as second_count from my_table")
suspend fun getMyCounts(): MyCounts
}
I have the following query function -
#Query("select chats.groupId, (select count(*) from ${Constants.messagesTable} where groupId = chats.groupId) as 'count' from ${Constants.chatsTable} chats where chats.groupId in (:groupIdListAsString)")
fun getChatMessagesCount(groupIdListAsString : String) : LiveData<List<ChatCountModel>>
and the following object -
#Dao
data class ChatCountModel (val groupId : String, val count : Int)
I have checked the query, it is working fine. But it can't create the needed custom object from the 2 selections I make from this query.
How can I make it work?
Solved - groupIdListAsString was a list joined to string. I just gave it the list and it was able to work with it as a list object.
I am using Room Database to make a database to store information in a table. I want to access one entry from the table and delete the same entry without the need to call two functions.
#Query("SELECT * FROM history_packet_table ORDER BY timestamp ASC LIMIT 1")
fun get(): HistoryPacket?
#Query("DELETE FROM history_packet_table ORDER BY timestamp ASC LIMIT 1")
fun delete()
I want these two operations to happen only by calling get. Is there a way?
I believe that you can add the following to the Dao :-
#Transaction
fun getAndDelete() {
get()
delete()
}
Obviously you can call the function what you wish. However, the get seems to be useless as it is.
So you may want something like :-
#Query("SELECT * FROM history_packet_table WHERE timestamp = (SELECT min(timestamp) FROM history_packet_table)")
fun get() :HistoryPacketTable
#Query("DELETE FROM history_packet_table WHERE timestamp = (SELECT min(timestamp) FROM history_packet_table)")
fun delete() :Int
#Transaction
fun getAndDelete() :HistoryPacketTable {
// Anything inside this method runs in a single transaction.
var rv: HistoryPacketTable = get()
val rowsDeleted: Int = delete()
if (rowsDeleted < 1) {
rv = HistoryPacketTable();
//....... set values of rv to indicate not deleted if needed
}
return rv
}
Note as LIMIT on delete is turned off by default, the queries can be as above, this assumes that timestamp is unique otherwise multiple rows may be deleted, in which case the Dao could be something like
:-
#Delete
fun delete(historyPacketTable: HistoryPacketTable) :Int
#Transaction
fun getAndDelete() :HistoryPacketTable {
// Anything inside this method runs in a single transaction.
var rv: HistoryPacketTable = get()
val rowsDeleted: Int = delete(rv)
if (rowsDeleted < 1) {
rv = HistoryPacketTable();
//....... set values to indicate not deleted
}
return rv
}
I'm aware Room lets us establish 1-N relations with the #Relation keyword. Although I'd like to know if it's possible to apply conditions to this relationship.
Let's say that I have the following POJO
class UserAndPets {
#Embedded
lateinit var user: User
#Relation(entityColumn = "ownerId", parentColumn = "userId")
lateinit var pets: List<Pets>
}
interface UserDao {
#Query("SELECT * FROM users WHERE userId = :userId LIMIT 1")
fun getUserAndPetsForUserId(userId: String): UserAndPets?
}
The above method lets me query a User and all his pets. Although is there a way for me to query the User and his last 10 pets for example? Or a User and all his Pets that would be a certain type?
Thanks
If the Id field in Pets is auto-incremented then this query should give you the result you are looking for:
#Query("SELECT * FROM pets WHERE ownerId = :userId ORDER BY Id DESC LIMIT 10")
By the way, the "LIMIT 1" in your Users query is unneccessary, the query would always return a single user.