RxJava2 and Room with loop - android

I have a program where data is stored in a local database using Room, and I receive data through RxJava2. In Provider, the database created a method that takes one parameter - an identifier, by which it sends a request and receives certain data that corresponds to the identifier. But I want to pass as a parameter not one identifier, but an array of identifiers and get an array as well, but I don't know how. I don’t want to implement it through for, because I think there is a better solution, but I couldn’t find it. My code is shown below.
...
userDatabase.userDao().getById(id)
.subscribeOn(Schedulers.io())
.observeOn(SchedulerProvider.ui())
.map {
InfoStruct(
it.Name.toString(),
it.Id.toString()
)
}
.subscribe(
{println("${it.userName} || ${it.userId}")},
{println("Error")}
)
...
I pass one value to the getById method parameter - an identifier, and send a request like this: select * where id =: id. I want to pass an array of identifiers as a parameter to get data for several users at once, but I don't want to change the structure of the request. How can this be done?

You could use a dao, e.g. getByManyIds(), that uses a WHERE clause that returns a list based upon a provided list.
The SELECT clause could be along the lines of
#Query("SELECT * FROM the_table WHERE id IN (:idlist)"
List<your_type> getByManyIds(appropriate_type idlist);
appropriate_type should be an array of the id's int[], long[], String[] etc as is suitable (Room will generate the appropriate comma separated list).

Assuming that your Dao method looks like this:
#Query("SELECT * FROM user WHERE id = :id"
Single<User> getById(String id);
You need to do is change it to this:
#Query("SELECT * FROM user WHERE id IN (:ids)"
Single<List<User>> getByIds(List<String> ids);
And then use it like that:
userDatabase.userDao().getByIds(listOfIds)
.subscribeOn(Schedulers.io())
.observeOn(SchedulerProvider.ui())
.map { users ->
users.map {
InfoStruct(
it.Name.toString(),
it.Id.toString()
)
}
}
....

Related

How to track only INSERT in Realm?

I need to track only Realm INSERT operations. Is there a listener for something like this?
I use a Realm-java for Android and work with a server that sends data in independent parts. For example, a Person or a Pet can be received via websocket in any order. For example, the server can first send me a Pet and then after a few minutes a Person. Or vice versa. I can't control it. I want to save data from the server to the database without any logic. At the same time, the listener, who reacts ONLY to the insertion, puts a link to the Pet in the Person after the recording. For example, it connects a newly received Person with a Pet already in the database, whose ownerId is equal to the inserted Person's id.
I found a solution: to track only the insert, you need to use DynamicRealm and OrderedRealmCollectionChangeListener on the search result.
val dRealm = DynamicRealm.getInstance(conf)
dRealm
.where('Person')
.findAllAsync()
.apply {
addChangeListener { results: RealmResults<DynamicRealmObject>, changeSet: OrderedCollectionChangeSet ->
if (changeSet.insertions.isNotEmpty()) {
// do what you need
}
}
}
.asFlowable()
.subscribe()

Android Room combining the result of N queries into a live data list

I have a room database setup and I want to query that database N number of times and combine the results of each query into a live data array to display back to the user.
I'm pretty sure I want to be using MediatorLiveData but every example online has a predefined amount of live data sources it is combining.
I have the following setup:
petDao
#Query("SELECT * FROM pet_table WHERE name LIKE :petName")
fun getPetsByPetName(petName: String): LiveData<Pet>
petRepository
fun getPetsByPetName(petNames: List<String>): LiveData<List<Pet>> {
for (petName: String in petNames) {
val pets = petDao.getPetsByPetName(petName)
// Combine into one live list of pets
}
}
Have you tried this in your DAO?
#Query("SELECT * FROM pet_table WHERE name IN (:petNames)")
fun getPetsByPetName(petNames: List<String>): LiveData<List<Pet>>
It should work with a list of up to 999 arguments. (not sure if the parameter has to be an array, or if the list is fine)
As an extension over SQLite bind arguments, Room supports binding a
list of parameters to the query. At runtime, Room will build the
correct query to have matching number of bind arguments depending on
the number of items in the method parameter.
https://developer.android.com/reference/androidx/room/Query
To me it seems more appropriate for the example you've given.

Can I get Data From Room Query Like in Pair<loan_amount,adv_interest_amount>?

I know that I can do that by using data class but I want to achieve Like this
// This is My Query
#Query("SELECT loan_amount,adv_interest_amount FROM new_pledge_receive WHERE (new_pledge_receive.bill_date BETWEEN :fromDate AND :toDate)")
fun getPledgeReceiveAmount(fromDate: Long,toDate: Long):LiveData<List<Pair<Double,Double>>> ```
Pair class has 2 properties: first and second. Try to set the name of the selected columns to fit these names.
Something like: SELECT my_custom_field as first, my_another_field as second FROM...
As they mention on the official website
For SELECT queries, Room will infer the result contents from the method's return type and generate the code that will automatically convert the query result into the method's return type. For single result queries, the return type can be any java object. For queries that return multiple values, you can use List or Array. In addition to these, any query may return Cursor or any query result can be wrapped in a LiveData.

Room not returning duplicates

So I have a room database all set up, everything is fine, so I can make queries and inserts, delete etc no problems, however i've just run into a situation where id like to return entries by their Ids and duplicates should be allowed, however room is removing the duplicates, so for instance I send it a list of ids say <1,2,3,2,3> and it returns items by their ids but only sends me <1,2,3> removing the duplicate entries. The query I'm making is below (btw complete noob at sql)
#Query("SELECT * FROM card WHERE cardId IN(:cardId)")
LiveData<List<Card>> getCardsByIds(List<Integer> cardId);
Im using it via a repository I created (just a level of abstraction) and calling this repo from a ViewModel, this ViewModel has a mutable live data integer list containing the ids and using a SwitchMap I get the latest live data. ill include the relevant pieces below
CARD REPO calls my Daos method like this
public LiveData<List<Card>> getCardsByIds(List<Integer> cardIds){
return cardDao.getCardsByIds(cardIds);
}
ViewModel calls for them
private MutableLiveData<List<Integer>> cardIds;
//** constructor etc
cards = Transformations.switchMap(cardIds, id -> cardRepository.getCardsByIds(id));
and through the magic of SwitchMap when the cardIds list updates a new query is made and I observe the ViewModel from my fragment. I've debugged it so I know the list of Ids is correct and has the duplicates Ids, but the returned LiveData list is missing the duplicate Ids. any help?
Edit:
The SQLiteDatabase always presents the results as a Cursor in a table format that resembles that of a SQL database.
Source : Google Developer Training
Before the results are returned by the query, the results are stored in a table, and if there are rows with duplicate primary keys, they would be omitted by default.
To achieve what you intend, you can execute a query to find single element by id in loop and append the result to a list.
Updated DAO method:
#Query("SELECT * FROM card WHERE cardId=:cardId")
LiveData<Card> getCardById(Integer cardId);
Update Repository method:
public LiveData<List<Card>> getCardsByIds(List<Integer> cardIds){
List list = new ArrayList();
for(Integer cardId: cardIds){
list.append(cardDao.getCardById(cardId));
}
return list;
}
Hope this helps.
Original Answer:
If id is the primary key of your model, It doesn't allow duplicate data to be entered. Hence while retrieving you might find duplicates missing.
If you have id with duplicate, create another attribute for primary key. (use autogenerate if you don't have any primary key attribute)
A primary key is by default UNIQUE and NOT NULL.

Room - SELECT query, get or default

In SQL Brite, there's a method mapToOneOrDefault. Is there a similar thing in Room?
Say for Model
#Entity(tableName = "users")
data class User(#PrimaryKey val name: String)
and Dao
#Dao
interface UserDao {
#Query("SELECT FROM users where name = :name")
fun getUserByName(name: String): Flowable<User>
}
Not the stream returns nothing for getUserByName("John") if there's no John in DataBase. Is there a way to get a default value, say User("")?
Not the stream returns nothing for getUserByName("John") if there's no
John in DataBase. Is there a way to get a default value, say User("")
There is no default mechanism.
You could change from Flowable<User> to Flowable<List<User>>. In case of no user you will get an empty list back. You can use a map to check and return a default value or filter+switchIfEmpty.
Or you could change from Flowable to Single. With Single, in case of no rows, matching your query, onError will be triggered. You can then implement onErrorReturn or onErrorResumeNext to return a default value
You can use Maybe instead of the Flowable in this case.
Maybe: Conceptually, it is a union of Single and Completable providing the means to capture an emission pattern where there could be 0 or 1 item or an error signaled by some reactive source.
You can then use the operator defaultIfEmpty to map to a new object if the query didn't return a value.
Reference

Categories

Resources