简体   繁体   中英

How to control concurrency in INSERT statements using Hibernate

I have a problem to control concurrency when inserting data through my web application.

Context: I have 3 tables (X, Y and Z) that record patient's hospitalizations. A patient cannot have more than one active hospitalization.

Currently my application do a verification before the hospitalization INSERT in the database, that checks if the patient is already hospitalized.

However, this verification does not work when two or more users try to admit the same patient at the same time.

What I do now:

1 - verify if patient is already hospitalized (SELECT in table X)
  If not:
    begin transaction A;
      2 - INSERT in table X;
      3 - INSERT in table Y;
      4 - INSERT in table Z;
    end transaction A;

As I mentioned before, the verification in 1 try to avoid patients from being hospitalized twice or more. However, it do not work if two (or more) users try to admit the same patient at the same time.

Maybe I can use something that may lock the SELECT statement from being executed until the transaction A be finished. In that way the SELECT would identify that the patient is already registered when executed.

I would like to treat this problem using EXPLICITING LOCKING from PostgreSQL in database. Accordingly to the documentation I can do something like that using the ACCESS EXCLUSIVE lock mode (since it is the only mode capable o lock an select statement).

But how to implement this ACCESS EXCLUSIVE lock in Hibernate?

I've already check and is not possible to treat this using constraints in database

What have you tried? Your question mentions postgres. I checked their documentation here . You can try a transaction at the Serializable Isolation Level as defined in their spec if you want to solve the problem from the database end of things.

From their documentation, dirty read, phantom read and non-repeatable read are impossible with this isolation level.

Alternatively if you want to solve the problem with Java you can make a synchronized service wrapper around whatever classes you need to in order to provide appropriate synchronization logic. So if you have

public interface MyService {
    public void hospitalize(Patient patient) throws Exception;
    // etc
}

with

public class MyServiceImpl implements MyService {
    @Transactional
    public void hospitalize(Patient patient) throws Exception {
        // your logic
    }
}

and

public MyServiceSerialImpl extends MyServiceImpl {
    @Override
    @Transactional
    public void hospitalize(Patient patient) throws Exception {
        // ...
        synchronized (lock) {
            super.hospitalize(patient);
        }

        // ...
    }

    private final Object lock = new Object();
}

or synchronized on the whole method, or something similar.

EDIT

As pointed out by Roman Konoval in the comments, a synchronization solution with Java will not help if there is more than one application, a cluster, more than one jvm, etc. The database is where you need to work.

In order to make sure that there could not be two hospitalizations for single patient you need to add unique constraint to the field that is a reference from X (and/or Y, Z) to patient (I assume that there's already a field like patient_id at least in one of these tables otherwise how do you which patient the inserted records into X, Y and Z are referred to).

In this case database will guarantee that you can't insert more than one record for given patient into the table.

You need to catch the unique constraint violation exception and do whatever you want to do in case if hospitalization already exists.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM