Search code examples
javaspringmultithreadingopenjpa

How to recreate select for update row locking in Spring/OpenJPA 1


I need to not allow working simultaneously with the same confirmation code in my Spring/OpenJPA 1 application. We decided to use select for update strategy, so we created the table AdmissionLock(id, Confirmation_Code, Terminal).
The separate sample JDBC application works fine with this strategy. It allows simultaneous processing of different confirmation codes and not allow simultaneous processing of the same confirmation codes. Please, see the sample code below:

Thread code:

conn.setAutoCommit(false);
long retId = Util.lockBySelectUpdate(conn, threadName, terminalCode, confNumber);
Util.updateConfCode(conn, threadName, terminalCode, confNumber);
conn.commit();

Main Program code:

String confCode1 = "21";
String confCode2 = "22";
String terminalCode1 = "10";
String terminalCode2 = "11";
Connection conn1 = Util.getNewConnection();
Connection conn2 = Util.getNewConnection();
AdmissionThread admissionThread1=new AdmissionThread(terminalCode1, conn1, confCode1, "Thread1", 10);
AdmissionThread admissionThread2=new AdmissionThread(terminalCode2, conn2, confCode2, "Thread2", 0);
    admissionThread1.start();
    Thread.sleep(2000);
    admissionThread2.start();

SQL:

 "SELECT * FROM ADMISSIONLOCK WHERE CONFIRMATION_CODE=?  FOR UPDATE"

 "update ADMISSIONLOCK set CONFIRMATION_CODE=? where TERMINAL_SERIAL_NUMBER=?;";         

Since our application is Spring/OpenJPA 1 I need to incorporate this idea in Spring/OPenJPA 1 code. So, I created the service with this simplified code:

@Transactional
public void processAdmissionLock (String terminalSerialNumber, String confirmationCode){

AdmissionLock admissionLock = new AdmissionLock(terminalSerialNumber, confirmationCode);
        query = entityManager.createNativeQuery(SQL_LOCK_STRING);
        entityManager.lock(admissionLock, LockModeType.READ);
        query.setParameter(1, confirmationCode).getResultList();

        if(isEntityPersistent(admissionLock)) {
            admissionLock = entityManager.merge(admissionLock);
        } else {
            entityManager.persist(admissionLock);
        }
}

AdmissionLock Entity:

@Entity
@Table (name = SdiAdmissionLock.TABLE_NAME)
public class SdiAdmissionLock extends AbstractEntityImpl {

    private static final long serialVersionUID = 1L;
    private long uidpk;
    private String terminalSerialNumber;
    private String confirmationCode;
    private Date createdDate;

    public static final String TABLE_NAME = "SDIADMISSIONLOCK";
...
}

My problem is that this code does not lock anything. Both simultaneous threads with the same confirmation code pass through


Solution

  • I ended up using the same native queries as in the JDBC application. One important difference - starting transaction ("START TRANSACTION") and committing ("COMMIT") are shifted to MYSQL itself and called by the JDBC executeUpdate() method.