How to make SQL insert safely? - android

I am creating a Hotel Booking System for the Android application.
I was thinking about how to implement a safe way to insert a booking into my database. The request will be sent from the app to the MySQL database (which is hosted on a web server) via my web service.
When I create a booking I insert the: CustomerID, HotelID, RoomID (via a nested select statement to find an available room), checkInDate and checkOutDate.
However, how can I ensure that two different people won't book the same/last room if they both hit 'book now' at roughly the same time. I thought about using Synchornisedfor the java method but will that make any difference if a range of different users on different mobiles try to make a booking?

What you are looking for is transactions.
Using transactions you will be able to isolate access to a given set of records into a single, atomic logical entity. Any operation of a certain complexity affects multiple rows, columns and even tables, or needs multiple (atomic or non-atomic) statements to complete. A transaction is a mechanism to ensure logical consistency of your data even if any of these operations fails. In that case, the incomplete transaction is rolled back, otherwise the transaction is committed.
And these are exactly the both possible outcomes with transactions: commit or rollback. In pseudo code it looks like this:
begin transaction
try {
required operations to reserve a room for a given time frame
if( success)
commit transaction
else
rollback transaction
} catch {
rollback transaction
}
Synchronizing in Java is certainly possible, but it has several major drawbacks:
it prevents you from scaling the application, because it affects only the current process.
it also prevents you from extending the solution, e.g. by sharing the data with associated programs
a database transaction is designed to handle crashes without producing inconsistent data

I don't think that you're asking about threading as much as avoiding a race condition.
Not knowing anything about your architecture, one way to avoid such a race is to set a timestamp when each user hits the 'book now' button and pass it as a field in the transaction. The application server would then sort all the entries in its queue according to the timestamp, as opposed to simply accepting them in the order they arrive.

You need to create a unique key {room number, date} such that a room can only have one booking per date. Then a client booking becomes an insert of as many rows as there are days in the booking, carried out within a transaction.
Threads really have little to do with it.

Related

Logic to update network requests using Work Manager

I have an application which fetches data from an API. SO basically, right now, the app works as such:
If connected to the internet, fetch data and use Android Room to store for offline use
If not connected to the internet, check if data exists in Room. If exists, display it. If it doesn't exist, display an error message.
I did some research online on how to implement an efficient offline storing policy and Google suggests to use Work Manager to queue requests and then send it when connected.
I actually want to know how to implement this ? (not the code but the logic, i.e should i schedule requests everyday to the API or every time it's connected to the internet ?)
If someone with experience with offline apps could help would be great.
My network requests are done through Retrofit and i already create a class that perform calls to the API.
Keep in mind WM (work manager) is designed to perform operations when certain conditions are met (e.g.: the user has enough battery, the display is off, etc.). So this may end up with your data not being updated when you need it. WM is good for operations you want to happen but are not critical to occur "right now". I'd say always use the Room DB as the single source of truth. If the data is in room, show it, if it's not, fetch it, if you can't, well, you tried. Send a message to the user. You can use a NetworkConnectivityListener to monitor connectivity and check if you have a pending query (you could store the parameters of this query in your Room database in another table for ease of use). So you'd query the DB, obtain the pending queries (if any) and execute them, update the data, and let the ViewModel/Repository decide if there's a context to show this data (UI).
I feel like you are very close to achieve what you need.
So in other words:
UI: Observes its viewModel for some sealed class xxx state to tell it what to do (show an empty list, show an error, pass data to a recyclerview adapter, etc.).
ViewModel: Using its viewModelScope.launch { ... } will call a repository.fetch(...) or similar. Your viewModel will fetch this data when the Fragment tells it to do so (e.g. the user pressed a button) or on some lifecycle event (onStart for example).
The Repository in this case normally exposes a flow (if you can use the experimental api) or a suspend function that can perform the following actions (that can vary depending on your business rules)
If the data is available in the Database, return it immediately.
If the data is old (or we still want to refresh it), then perform the network API (if there's connectivity to do so). If there's No connectivity, you could store this "pending" query in the database for later. You could also check if you have a pending query before doing any of this, perhaps it's outdated or perhaps you need to execute it.
In any case, once the query goes through, you insert the results in the database, and call the same method you used in step 1.
Don't forget to update your "pending" query if you had one (or if you use this).
With WorkManager, you could schedule the "fetch data from API" part to happen at some point (so your data will be kept more up to date), but I all really depends on the use-cases you have.

Query construct in room (Android Kotlin)

I am working on android ROOM + Kotlin (Just started)
I want to make a query which update the row of my table, but I am not able to find a definition on how to access the value from the parameter inside the query
#Query("UPDATE note_table SET description = :description, title= :title, priority = :priority WHERE id =:id")
fun updateNote(note : Notes)
I want to access the description from note object. like note.description how to do that inside the query !! Any help will good!
That is not possible, as docs specify
#Query("SELECT * FROM user WHERE age > :minAge")
When this query is processed at compile time, Room matches
the :minAge bind parameter with the minAge method parameter. Room
performs the match using the parameter names. If there is a mismatch,
an error occurs as your app compiles.
Other option is using Raw query, but that is needlessly complected for the use case.
Understand why Room doesn't allow object references
Mapping relationships from a database to the respective object model is a common practice and works very well on the server side. Even when the program loads fields as they're accessed, the server still performs well.
However, on the client side, this type of lazy loading isn't feasible because it usually occurs on the UI thread, and querying information on disk in the UI thread creates significant performance problems. The UI thread typically has about 16 ms to calculate and draw an activity's updated layout, so even if a query takes only 5 ms, it's still likely that your app will run out of time to draw the frame, causing noticeable visual glitches. The query could take even more time to complete if there's a separate transaction running in parallel, or if the device is running other disk-intensive tasks. If you don't use lazy loading, however, your app fetches more data than it needs, creating memory consumption problems.
Object-relational mappings usually leave this decision to developers so that they can do whatever is best for their app's use cases. Developers usually decide to share the model between their app and the UI. This solution doesn't scale well, however, because as the UI changes over time, the shared model creates problems that are difficult for developers to anticipate and debug.
from docs : https://developer.android.com/training/data-storage/room/referencing-data

Android: how can I lock SQLite database accessible with ContentProvider (or other way of executing atomic conditional operations)

I got two tables in my SQLite DB: entities and user_actions. Their approximate schemes:
The flow of the program is something like this (all DB accesses handled by ContentProvider):
The user performs some action which modifies one of the entities
The corresponding entity is updated in entities immediately. locally_modified value of this entity is set to 1
The information about user's action is stored in user_actions
At some point in future a sync session with the server is being initiated (I use SyncAdapter framework)
User's actions from user_actions are uploaded to the server one by one and removed from the DB in a background thread
When the uploading completed, I need to clear locally_modified flags in entities
At this point I encounter my atomicity issue: the synchronization with the server happens in a background thread, therefore the user can use the app and perform additional actions. As a consequence, right before I clear locally_modified flag for an entity, I must check that there are no records in user_actions corresponding to this entity. These three steps must be executed atomically for each entity having locally_modified set to 1:
Query user_actions for entries corresponding to entity's _id
Test whether the query from #1 returned an empty set
Clear locally_modified of that entity to 0
Given the above scenario, I have three questions:
Q1: Is there a way to lock SQLite DB accessed over ContentProvider in Android such that it can be accessed only by the locking thread?
Q2: If the answer to Q1 is positive, what happens if some other thread tries to access a locked DB? What precautions should I take to ensure reliable operation?
Q3: It is possible to execute atomic transactions with conditional logic using ContentProviderOperation? You can use "back-references" as described in this answer and this blog post to reference the result of a previous operations, but is there a way to use that result in some kind of if-else statement?
Thanks
Is there a way to lock SQLite DB in Android such that it can be accessed only by the locking thread?
Yes, have a look at SQLiteDatabase.beginTransaction() (source). I believe you need SQLite's exclusive transactions, but you need to study that a bit more for your exact usage.
If the answer to Q1 is positive, what happens if some other thread tries to access a locked DB? What precautions should I take to ensure reliable operation?
There's an SQLite.amIInTransaction() method that you could check, or just catch an SQLiteDatabaseLockedException (more SQLite exceptions that you should look up)
It is possible to execute atomic transactions with conditional logic using ContentProviderOperation? You can use "back-references" as described in this answer and this blog post to reference the result of a previous operations, but is there a way to use that result in some kind of if-else statement?
Never done that, but it seems that overriding ContentProvider's applyBatch and wrapping it in a transaction should work:
Android: SQLite transactions when using ContentResolver
The answer turned out to be pretty simple, but it is kind of a "hack" - just add additional Uri to ContentProvider.
For example: initially my ContentProvider supported the following URIs:
Uri.withAppendedPath(MyContract.CONTENT_URI, "entities")
Uri.withAppendedPath(MyContract.CONTENT_URI, "user_actions")
In order to support the atomic operation described in the question I added an additional Uri:
Uri.withAppendedPath(MyContract.CONTENT_URI, "clear_modified_flag")
When this Uri is updated through:
getContentResolver().update(
MyContract.ClearModifiedFlag.CONTENT_URI,
new ContentValues(),
null,
null);
my ContentProvider executes an SQLite transaction that locks the database for the duration of the operation and rolls it back in case of any errors (as described in this answer).
That's it.
P.S. my ContentProvider is not exported (i.e. other apps can't access and use it), therefore it is safe to add this new Uri to it. But keep in mind that if you do export your ContentProvider, then exposing functionality like this one could be problematic.

Database operation using transaction methods in Android

I am reading SQLite in Android, I see that there is transaction methods to do CRUD operation on SQLite in Android, same thing can be done without transactions methods.
Transaction methods : beginTransaction, setTransactionSuccessfull, endTransaction.
In what cases should we use the transactions method over simple approach.
All sqlite writes are in transactions. If you don't start one explicitly yourself, one will be started and committed for you implicitly. This implicit transaction finishes automatically when the statement finishes. Further reading.
Generally, you want explicit transactions when you want to group more than one database operation as one to maintain ACID properties of your data model. As a consequence, you also gain performance benefit since you don't need to wait for I/O after each statement, only at commit.
It's also worth noting that sqlite does not support nested transactions but the Android sqlite API emulates them using nest level counting. As a consequence, any nested Android sqlite transaction really commits or rollbacks when the outermost transactions is committed or rolled back.

Android, SQLite transactions and process freeze

I'm processing a lot of XML data that validates the local data storage within an AsyncTask object. First I tried to use transactions for these operations but while a transaction is in progress any other actions by the user will make the app freeze and wait for the transaction to finish, and sometimes even make the app stop responding.
The transactions are divided into several steps sometimes counting to a couple of hundreds per iteration. Because of the problems I went from using transactions to real-time queries which is very slow but solves the freezing - resulting in a very battery consuming application.
My question is; Is there a way to stop the transactions from locking the database? Or is my problem a result of poor preparation before the transactions?
Transactions are tend to lock your table(s) while doing their business, so there's no way that you can play transactions and non-transactional queries on a same instance at same time.
However, what you need to do is to process your data (xml) first (which might be time consuming) and later kick-in transaction once you've got the data ready.
P.S. I would personally suggest you to make use of transactions (in an efficient way) when it comes to insert multiple records because it creates a single Journal-file to handle all the insertion and speeds up SQLite operations a lot.
make your transactions smaller and don't forget (like I did) to still end transaction if exception thrown - use try, catch, finally...

Categories

Resources