Android Room Database - Query validation for success or failure - android

We are using Room database in android to store the data.
Now, In Dao class we all perform various queries like Insert, Select, Delete, Update etc.
I want to know that How can We know that these queries executed successfully?
i.e If I am doing as below :
appDatabase.userDao().insert(userData)
How Can I notified that the user data is inserted in particular table and operation is successful ?
And Yes Is there any tools or plugin available through which we can see the data of Room database? (I have googled about it, but it was a bit confused regarding Device Monitor)
Please guide.

Everything is explained in the documentation here.
We can return a value for insert, update and delete operations to determine if it was successful or not.
#Insert
fun insert(item: ItemEntity): Long // returns row that entity was inserted at or -1 on failure
#Update
fun update(item: ItemEntity): Int // returns the number of rows updated successfully
#Delete
fun delete(item: ItemEntity): Int // returns the number of rows deleted successfully
And then when you do the operation:
val result = cache.insert(item.toItemEntity())
println("result is $result")
Queries don't seem to have an obvious way to implement this functionality though.

I assume that you re using room database to persist your data , the insert() method room returns a long value and you can use that value to check if data is persisted
// make insert method returns a long value
long insert()
Then you can do get check for your long value
long value = appDatabase.userDao().insert(userData)
if(value == -1) no data insert
if(value == 0) data inserted
PS : If i'm wrong , someone corrects me , thank you

First you need to assign Dao functions to return a Long variable as follows
#Dao
interface DoctorCallDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(dcrData: DCRData):Long
}
Then you can catch this when you insert data into the database
val state:Long= database.doctorCallDao.insert(dcrData)
if(state>0) {
//Successfully inserted
} else {
//Error occured
}

Related

Room database check for a condition before insert

I have to insert a value to table one in a hour for a specific id. I know it can be achieved by SQLite trigger but i read somewhere else that Room database currently doesn't support SQLite trigger function.
How to achieve above task? I have included my data model here.
#Entity(tableName = "device_table")
public class Device {
#PrimaryKey (autoGenerate = true)
private int id;
private String userId;
private long time;
public Device(String userId) {
this.userId = userId;
}
// and rest of the getter setter methods
}
I think you don't need SQLite trigger there (if update of your row doesn't depend on insert/delete another row|rows). You can divide your task to 2 subtasks:
How to create function, that updates row in Sqlite with some id.
How in Android invoke this function once in an hour.
For first subtask decision depends on how you want to update your data.
The simplest decision here - to use in your DAO interface method with #Insert annotation:
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertDevice(device: Device)
You should put "updated" instance of your data class Device to this method.
"OnConflictStrategy.REPLACE" means that if row with this "id" exists, then row will be overridden, else - it will be added.
For second subtask you can use one of the mechanisms - Handler, Executor, Timer, AlarmManager, WorkManager (last works even if your app is inactive). There are many answers/examples on using them here on SO.

Firestore query cache

I have 1 method in my repository in which I receive a status. Either this status is 0 or 1. If it is 0 I need to generate a different query than if it is 1, let's say
fun getData(status:Int) {
val docRef = FirebaseFirestore.getInstance().collection("orders")
if(status == 0){
docRef.whereEqualTo("status",0)
}else{
docRef.whereGreaterThanOrEqualTo("status",1).whereLessThan("status",4)
}
val suscription = docRef.addSnapshotListener { ... }
Now, I use this method to either query with status 0 or status 1 different documents in my collection, now, when I come back where status is 0 in my UI, will Firestore cache this two queries and return me the cached docRef of status 0? or it will be requiring all the documents again because is in the same method and there are not two different docRefs?
I wonder this because I have a bottomnavigation where I switch tabs, I don't want to require the data if its already queried.
I want to know if this conditional If statement will cache the two queries into my client when I need either the first one or the second one below
Edit
This question is because if I need to create a separate method with all the same data but with a different reference to hold the data
thanks
First of all, your method should look like this:
fun getData(status:Int) {
val docRef = FirebaseFirestore.getInstance().collection("orders")
if(status == 0){
docRef = docRef.whereEqualTo("status",0)
} else {
docRef = docRef.whereGreaterThanOrEqualTo("status",1).whereLessThan("status",4)
}
}
val suscription = docRef.addSnapshotListener { /* ... */ }
And this because Cloud Firestore queries are immutable, which means that you cannot change the properties of an existing query. If you change the value by calling .whereEqualTo("status",0) method, it becomes a new query.
Firestore cache these two queries and return me the cached docRef of status 0?
Firestore will cache all the documents that are returned by your query. If the if part of the statement is triggered, then you'll have in the cache only those documents, otherwise you'll have the other ones.
I want to know if this conditional If statement will cache the two queries into my client when I need either the first one or the second one below
If you switch between both tabs and both queries are executed, you'll have all the documents from both queries cached.

in Room, Why I could #insert a row in a table but unable to retrieve it with SQL request?

I have a problem with room (my fist time with room).
I can insert some data in my DB but not retrieve something. I'm stuck with this problem. If you can explain me.
I explain :
I use Android Architecture Components. So I have an DAO to make my SQL requests. I have a repository to not handle my DB directly.
And finally, I have a viewModel to be not worry by the data persistance.
My DAO uses the annotation #Insert to create a row .
I use Stetho to look through my DB and everything is oK.
My row is created.
So, When I want to get it with another SQL request (by the same way DAO => repository =>ViewModel),
my request returns always null.
// --- DAO ---
#Query("SELECT * FROM users WHERE id = :id")
LiveData<RealEstate> getRealEstate(long id);
// --- REPOSITORY ---
private final RealEstateDao realEstateDao;
public RealEstateRepository(RealEstateDao realEstateDao) { this.realEstateDao = realEstateDao; }
public LiveData<RealEstate> getRealEstate(long id) { return this.realEstateDao.getRealEstate(id); }
// --- VIEWMODEL ---
public LiveData<RealEstate> getUser(long userId) {
return userDataSource.getRealEstate(userId);
}
I think that you are using incorrectly LiveData, Room have bridge adapter for RXJava/Kotlin, Coroutines, etc.
First, change the return value to something like this
#Query("SELECT * FROM users WHERE id = :id")
RealEstate getRealEstate(long id);
Remember that you cannot make database operations in the Main Thread, you should be using a mechanism for that RX, Coroutines, threads, etc.
After this, you can use LiveData in your viewmodel wrapping the data retrieved from your database to notify their UI parent.

How to setup an application level livedata observer pattern

Background
I have multiple ways to get data from server, such as background polling and server push. These items may be contain same item.when data is ready, I insert these data into database with Android Room. when the insert item have exist in database, I will abort the item. I expect to get notify when these data insert success, so I use livedata in the Dao:
#Dao
public interface WordDao {
#Query("SELECT * from word_table where isNew = 0")
LiveData<List<Word>> getAlphabetizedWords();
#Insert
void insert(List<Word> word);
#Update
void update(List<Word> words);
}
In the application code:
#Override
public void onCreate() {
super.onCreate();
wordRepository = WordRepository.getInstance(this);
wordRepository.getAllWords().observeForever(new DatabaseObserver(this));
}
In the DatabaseObserver, I receive data change notify. I will handle these data, then I will set one property of the class(set isNew = 1) to indicate that the item has been handled. At the same time, that handle keep the later insert will not notify old insert data.
My Question
Is this pattern using observeForever() will be worked as expect?
When I write the demo code, I encounter that the DatabaseObserver's onChanged method receive same item list. After I change my code like this, the problem is still there. How to resolve this problem?

Android -room persistent library - DAO calls are async, therefore how to get callback?

From what i have read Room doesn’t allow you to issue database queries on the main thread (as can cause delays on the main thread)). so imagine i am trying to update a textview on the UI main thread which some data how would i get a call back. Let me show you an example. Imagine i want to store my business model data into a object called Events. We would therefore have a EventDao object:
imagine we have this DAO object below:
#Dao
public interface EventDao {
#Query("SELECT * FROM " + Event.TABLE_NAME + " WHERE " + Event.DATE_FIELD + " > :minDate" limit 1)
LiveData<List<Event>> getEvent(LocalDateTime minDate);
#Insert(onConflict = REPLACE)
void addEvent(Event event);
#Delete
void deleteEvent(Event event);
#Update(onConflict = REPLACE)
void updateEvent(Event event);
}
and now in some activity i have a textview and i'd like to update its value so i do this:
myTextView.setText(EventDao.getEvent(someDate));/*i think this is illegal as im trying to call room dao on mainthread, therefore how is this done correctly ? would i need to show a spinner while it updates ?*/
since the fetching is occuring off of the main thread i dont think i can call it like this and expect a smooth update. Whats the best approach here ?
Some more information: i wanted to use the room database as mechanism for retrieving model information instead of keeping it statically in memory. so the model would be available to me locally through the db after i download it through a rest service.
UPDATE: so since i am returning a livedata then i can do this:
eventDao = eventDatabase.eventDao();
eventDao.getEvent().observe(this, event -> {
myTextView.setText(event.get(0));
});
and that works for something very small. but imagine my database has a million items. then when i do this call, there will be a delay retrieving the data. The very first time this gets called it will be visible to the user that there is a delay. How to avoid this ? So to be clear , there are times i do not want live data, i just need to update once the view. I need to know how to do this ? even if its not with liveData.
If you want to do your query synchronously and not receive notifications of updates on the dataset, just don't wrap you return value in a LiveData object. Check out the sample code from Google.
Take a look at loadProductSync() here
There is a way to turn off async and allow synchronous access.
when building the database you can use :allowMainThreadQueries()
and for in memory use: Room.inMemoryDatabaseBuilder()
Although its not recommended. So in the end i can use a in memory database and main thread access if i wanted super fast access. i guess it depends how big my data is and in this case is very small.
but if you did want to use a callback.... using rxJava here is one i made for a list of countries i wanted to store in a database:
public Observable<CountryModel> queryCountryInfoFor(final String isoCode) {
return Observable.fromCallable(new Callable<CountryModel>() {
#Override
public CountryModel call() throws Exception {
return db.countriesDao().getCountry(isoCode);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
you can then easily add a subscriber to this function to get the callback with Rxjava.
As Bohsen suggested use livedata for query synchronously. But in some special case, we want to do some asynchronous operation based on logic.
In below example case, I need to fetch some child comments for the parent comments. It is already available in DB, but need to fetch based on its parent_id in recyclerview adapter. To do this I used return concept of AsyncTask to get back the result. (Return in Kotlin)
Repositor Class
fun getChildDiscussions(parentId: Int): List<DiscussionEntity>? {
return GetChildDiscussionAsyncTask(discussionDao).execute(parentId).get()
}
private class GetChildDiscussionAsyncTask constructor(private val discussionDao: DiscussionDao?): AsyncTask<Int, Void, List<DiscussionEntity>?>() {
override fun doInBackground(vararg params: Int?): List<DiscussionEntity>? {
return discussionDao?.getChildDiscussionList(params[0]!!)
}
}
Dao Class
#Query("SELECT * FROM discussion_table WHERE parent_id = :parentId")
fun getChildDiscussionList(parentId: Int): List<DiscussionEntity>?
Well, the right answer is to use ListenableFuture or Observable depending if you need one shot query or a new value emitted after database change and the framework you want to use.
From the doc "To prevent queries from blocking the UI, Room does not allow database access on the main thread. This restriction means that you must make your DAO queries asynchronous. The Room library includes integrations with several different frameworks to provide asynchronous query execution."
Exemple with a one shot query. You just have to add this in your gradle file.
// optional - Guava support for Room, including Optional and ListenableFuture
implementation "androidx.room:room-guava:$room_version"
Then your SQL query in your DAO become.
#Query("SELECT * FROM " + Event.TABLE_NAME)
ListenableFuture<List<Event>> getEventList();
Last step is the future call itself.
ListenableFuture<List<Event>> future = dao.getEventList();
future.addListener(new Runnable() {
#Override
public void run() {
try {
List<Event>> result = future.get();
} catch (ExecutionException | InterruptedException e) {
}
}
}, Executors.newSingleThreadExecutor());
Source : https://developer.android.com/training/data-storage/room/async-queries#guava-livedata

Categories

Resources