How do I lock database records from Java? - java

I'm developing a database program in Java with dbf. I need to know how to lock the database records from the Java side.
Example: we have a database table cheques with 5 records in the table (record 1 through 5). There are 2 users, user-1 and user-2. user-1 accesses record 1 and user-2 tries to access record 1 at the same time. I want to lock record 1 to prevent access to that record by user-2 while user-1 is accessing it. How do I do this in Java?

In case of MySQL, you can use, SELECT FOR UPDATE statement for locking the records. Lock will not be released until the transaction completes or rolls back.
More on the same here.

It depends on the environment you are working on. for a container managed transaction your container manages the transactions for you and all you have to do is to set the Lockmode to lockmode.write. What this does is that it blocks all write access to the class methods while userA is accessing record 1. On the other hand for a stand alone application you can just add Synchronization key word to your method to control concurrent access. I hope this helps.

Not every database supports per-record locking.
Generally, if you are in EE environment, you can use JPA EntityManager#find() method to lock certain record.
Full usage will be like that
// EntityManager em;
YourClass obj = em.find(YourClass.class, primaryKey, LockModeType.WRITE);
// do something
em.merge(obj);
After transaction commit the record(s) will be released.
In non-EE environment, as Darshan Mehta said, connection.createStatement().execute("SELECT * FROM table FOR UPDATE") will be the solution.

Related

Read/Write Locks on Methods of Injected Class

I'm currently working on a RESTful web service that is reading and writing from two different databases. The problem I am running into is handling cases where my service gets called and then a second call is received before the first finishes writing. The service reads a date field in the database to determine if it needs to INSERT or UPDATE and then writes to that field in the chosen operation. If the second call is made before the first is finished, the date field will not have been written to, so I end up with two INSERTS rather than an INSERT and an UPDATE.
I tried using the concurrency API available in java as well as in groovy but so far I have not been able to get it to work. The RESTful service looks up a fresh copy of the model class each time it is called. The model class then gets a new instance of the groovy object via dependency injection. As a result, I can't put an instance of the Lock in either place since each call of the RESTful service will be working on a new instance of the model and the groovy.
Can anyone suggest a better way to do this? Any help would be appreciated.
Update
Here is some pseudo code for the service
Lookup model data by id from table A of database 1.
Lookup the most recent entry in table B of database 2 where the id matches a key stored in the model (dw_id).
Compare the 'start date' column of the results to the current datetime.
If the 'day' of the start_date == the current day then:
4a. execute an UPDATE query on table B of database 2 using data
obtained from the model.
Else:
5a. Execute an UPDATE query on table B of database 2, replacing the
value of the end_date column with yesterday's date where id == dw_id.
5b. Execute an INSERT on table B of database 2, using data from the
model, setting start_date to today's date and the end_date to a
constant future date.
5c. Execute an UPDATE on table A of database 1, replacing the dw_id of
the model with the auto-generated id of the entry created by the
INSERT from 5b.
UPDATE 2
I ended up implementing something similar to the solution proposed by jan-willem-gmelig-meyling. I found a good example of an implementation here: https://stackoverflow.com/a/27806218/32453 that I was able to adapt to suit my needs.
In Java you can do this by synchronising on a lock object. This however only works if you acquire this lock before starting a database transaction. Otherwise, the two parallel threads will start a database transaction simultaneously and live in isolated database versions. So what you roughly get:
private final static Object lock = new Object();
public void myResource() {
synchronized (lock) {
// begin tx, do work, end tx.
// Other callee won't get here before first callee is finished
}
}
This however does break all server stateless conventions. It is much better to actually lock the database table in your transaction. This prevents another connection from simultaneously reading from and writing to the same table. You can investigate which locking mechanisms are supported for your database (or ORM library, if using any). A technique such as JTA can be utilised to share database transactions between servers, services, or multiple databases.

Counting Number Of Specific Record In Database

I have a application which needs to aware of latest number of some records from a table from database, the solution should be applicable without changing the database code or add triggers or functions to it ,so I need a database vendor independent solution.
My program written in java but database could be (SQLite,MySQL,PostgreSQL or MSSQL),for now I'm doing Like that:
In a separate thread that is set as a daemon my application sends a simple command through JDBC to database to be aware of latest number of the records with condition:
while(true){
SELECT COUNT(*) FROM Mytable WHERE exited='1'
}
and this sort of coding causes DATABASE To lock,slows down the whole system and generates huge DB Logs which finally brings down the whole thing!
how can i do it in a right way to always have latest number of certain records or only counting when the number changed?
A SELECT statement should not -- by itself -- have the behavior that you are describing. For instance, nothing is logged with a SELECT. Now, it is possible that concurrent insert/update/delete statements are going on, and that these cause problems because the SELECT locks the table.
Two general things you can do:
Be sure that the comparison is of the same type. So, if exited is a number, do not use single quotes (mixing of types can confuse some databases).
Create an index on (exited). In basically all databases, this is a single command: create index idx_mytable_exited on mytable(exited).
If locking and concurrent transactions are an issue, then you will need to do more database specific things, to avoid that problem.
As others have said, make sure that exited is indexed.
Also, you can set the transaction isolation on your query to do a "dirty read"; this indicates to the database server that you do not need to wait for other processes' transactions to commit, and instead you wish to read the current value of exited on rows that are being updated by those other processes.
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED is the standard syntax for using "dirty read".

Webapp java synchronized object aquire

I have a situation in my java, spring based web app. My server generates coupons ( a number mixed with alphabets , all random but unique) , each coupon can be applied or used by only one and only on logged in customer. They are shown on the front end to all the users, which then gets accepted/selected by the customers.But once accepted by one customer it gets assigned to him and not available to anyone else.
I tried to do synchronization of code block which checks if the coupon is already applied / availed, it worked but , cases like when two users click avail it at exact same time, it fails ( get allocated to both)
Please help.
Do not use synchronization for this. You can store the state of the coupons in a database, and work on these data in a DB transaction, using locks. So:
User tries the coupon, you get the ID
Start a DB transaction, get the coupon row from it, and lock it
Do what you need to, then invalidate the coupon
End the DB transaction, release the lock
The database do not necessarly need to be a standalone RDMS, in a simple case, even SQLite is sufficient. Anyway, DBs most certainly handle race conditions betten than you (or most of us) can.
If you prefer avoid database transactions you can use a Set with all the generated coupons and a set referencing only available coupons. When a user select a coupon in a synch block remove the coupon from available ones. The second user then fail to obtain it

How to avoid concurrent transactions affecting a single entity

HELP!
the case is when two or more transactions are trying to affect the same client monetary account in some external system. I need the second transaction be performed until the first one has finished.
consider:
- there are two or more transactions trying to affect the same balance
- there are multiple clients at the same time
- working with 1000 TPS with 100ms avg per transaction
ideas:
- as we are working with multi threads to support 1000TPS i'm trying to create Queues based on the Client ID. using some kind of workmanager that limit one thread by Client. So if i have 2 request with the same clientID at same time dynamically can queue the second.
tools
i'm trying to use Oracle tools for example:
- Fusion Middleware: using the Workmanager based on message context [not sure if possible because looks like context can be based only on session data] i like WorkManager because has no performance issues
- Oracle OCEP: creating a dynamic queue using the CQL [ not sure if possible and performance]
- Oracle Advance Queuing: maybe possible with transactions group.
thanks for any idea
I hope I got your problem.
In you question you asked, wether it is possible to perform a second transaction on a row before the first is completed. This is impossible! A database which follows the ACID paradigm has to be Consistent! So you can't "overtake" the first transaction!!! If you want to do that, you should use NoSQL Databases (like MongoDB, ...) where consistency is not that strong.
But maybe you want to know, if there is a Oracle view to figure out, wether a row is locked or not? Let's assume, that there is a view like that. You would check this view and if there is no lock, you start your update/delete. But you can't be sure that this will work because even 1ms after you checked it, another process can put a lock on it.
The only thing you can do is, to put a "select ... for update NOWAIT" before you UPDATE/DELETE statement.
If the row is locked, you will get a exception (ORA-00054: Resource busy). This is the recommended/"out of the box way" to let the database manage row-level-locking for you!
See the following example with the emp table. Consider: to check this out, start this code in two different sessions at the same time.
declare
l_sal number;
resource_busy exception; -- declare your own exception
pragma exception_init (resource_busy, -54); -- connect your exception with ORA-00054
begin
select sal
into l_sal
from emp
where empno = 7934
for update NOWAIT;
update emp
set sal = sal + 100
where empno = 7934;
exception
when resource_busy then
null; -- in your case, simply do nothing, if the row is locked
end;

Table level Lock in Hibernate

I use Hibernate version 4. We have a problem in batch process. Our system works as below
Select set of records which are in 'PENDING' state
Update immediately to 'IN PROGRESS' state
Process it and update to 'COMPLETED' state
The problem when we have two servers and executing at same time, we fear of having concurrency issue. So we would like to implement DB Lock for first two steps. We used query.setLockOptions(), but it seems not working. Is there any other to have table level lock or Row level lock till it completes select and update. Both are in same session.
We have options in JDBC that LOCK TABLE <TABLE_NAME> WRITE. But how do we implement in hibernate or is it possible to implement select..for update in hibernate?
"Select ... for update" is supported in Hibernate via LockMode.UPGRADE which you can set in, for example, a NamedQuery.
But using application/manual table-row locking has several drawbacks (especially when a database connection gets broken half-way a transaction) and your update-procedure can do without it:
Start transaction.
update table set state='PENDING', server_id=1 where state='IN PROGRESS';
Commit transaction
select from table where state='PENDING' and server_id=1;
[process records]
Each server must have a unique number for this to work, but it will be less error-prone and you let the DBMS do what it is supposed to be good at: isolation (see ACID).

Categories

Resources