toddeTV
toddeTV

Reputation: 1517

Spring JPA Lock

I am insecure when it comes to locking in spring jpa.
So please consider this question as a clarification. I hope I understood it correctly, but my English is not so good for understanding complex blog posts.

So this is what I think I got from some articles:

There are two basic types of locking:

First Question: Is this correct so far? And when I now want to test this knowledge, I can choose between this LockModeType's:

Why are there now so many sub-locks and what do they do? When "OPTIMISTIC" is the optimistic lock from the top I tried to understand, then what is "OPTIMISTIC_FORCE_INCEMENT"?
And what does a version update have to do with this? (or the @version?)

Going on:
There are three basic uses of lock (in Spring jpa):

  1. on a normal column, for example:

     @Entity
     public class PersonEntity {
         @Id
         @GeneratedValue(strategy = GenerationType.AUTO)
         private Long id;
    
         @Lock(LockModeType.PESSIMISTIC_WRITE)
         private String name;
     }  
    
  2. on a foreign key to another table, for example:

     @Entity
     public class PersonEntity {
         @Id
         @GeneratedValue(strategy = GenerationType.AUTO)
         private Long id;
    
         @Lock(LockModeType.PESSIMISTIC_WRITE)
         @OneToOne
         private Group group;
     }  
    
  3. on a table inside a repository, for example:

     interface PersonRepository extends Repository<Person, Long> {
         @Lock(LockModeType.PESSIMISTIC_WRITE)
         Person findOne(Long id);
     }  
    

Locking an Entity directly with

@Entity
@Lock(LockModeType.PESSIMISTIC_WRITE)
public class PersonEntity { }

is not possible. Therefore you can use 3 (locking inside a repository).

Second Question: Is this correct? Did I forget about the use of a locking?

Going on:

The idea behind locking is, that other methods/processes have to wait until a lock is released (except by the optimistic lock, where an error is thrown).

The Lock is there as long as the instance/object is active or until the next commit.

There are two main possibilities to unlock an object:

  1. Within a transaction: In this full method the lock is active. But when the return comes, the lock will be removed.

     @Transactional
     public void test(){
         System.out.println("start, but not locked yet");
         PersonEntity person1 = personRepository.findOne(1L); // locks this person or must wait, when locked
         System.out.println("now locked");
         // do something
         return true; // now the lock will be deleted -> unlocked again
     }  
    
  2. until the object is deleted: The data will be locked when the object is selected, and the data will be unlocked when the object is deleted.

     public void test(){
         System.out.println("start, but not locked yet");
         PersonEntity person1 = personRepository.findOne(1L); // locks this person or must wait, when locked
         System.out.println("now locked");
         // do something
         person1 = null; // now the lock will be deleted -> unlocked again
         System.out.println("not locked anymore");
         // do something more
         return true;
     }  
    

Third Question: is this correct so far? Does this function wait then the data is locked? Can a lock be deleted when the object is set to null?

Last words:

It is really difficult for me to understand such complex structures in the English language. I appreciate any little help. regardless of whether you give me links for more understanding or answer my questions directly :)

Upvotes: 39

Views: 38130

Answers (3)

geco17
geco17

Reputation: 5294

Locking

Locking prevents dirty reads (reading data that hasn't been committed) and non-repeatable reads (data getting deleted by another transaction before the one reading is finished).

Optimistic

Optimistic locking will lock a resource when committing the transaction. Optimistic locking will save the state of the data at the first access. As such, in optimistic locking you have the possibility of concurrent access. If an optimistic operation is to be performed, the initial state of the operation is compared to the current state before the operation. If there is a discrepancy (i.e., the resource has been modified in the meantime) then the transaction is not committed.

Optimistic force increment

For versioned objects, this will increment the object's version number. On non-versioned objects, an exception will be thrown.

Pessimistic

Pessimistic read

For repeatable reads, it's used to ensure that data isn't updated between reads. It is a shared lock meaning different processes can perform read operations (no write operations are permitted).

Pessimistic write

An exclusive lock that forces serialization of updates. Where optimistic locking only saved state, here it is locked to prevent transaction failure/deadlock in cases where this would happen with concurrent operations.

Pessimistic write force increment

Analogous to its optimistic counterpart, a pessimistic write that updates the object's version. Throws an exception for non-versioned objects.

Upvotes: 7

Arosha Dissanayake
Arosha Dissanayake

Reputation: 51

I had all these problems you are having and I'm posting an example I practically tried if it helps. I have a Device entity, DeviceStatus entity (one-to-one with Device), Configuration entity (one-to-many with Device). I have two paths for updating the device status (from a mqtt message handler and a http request).

Following methods are in my DeviceStatus Repository

@Lock(LockModeType.PESSIMISTIC_WRITE)
DeviceStatus findByDeviceMacAddress(String macAddress);    //used by the mqtt handler

@Lock(LockModeType.PESSIMISTIC_WRITE)
List<DeviceStatus> findByDeviceConfigurationId(Long deviceConfigurationId);    //used by the http request handler

If the http request handler calls the findByDeviceConfigurationId first, the PESSIMISTIC_WRITE is acquired and until the transaction is completed, mqtt handler cannot acquire the PESSIMISTIC_WRITE lock on findByDeviceMacAddress (Both handlers are trying to retrieve same DeviceStatus entities). That means the call to findByDeviceMacAddress will wait until the http request handler finishes the transaction. Also if the mqtt handler is the first to call the findByDeviceMacAddress then the http request handler will have to wait until the mqtt handler finishes its transaction.

I know this is not a full answer, but thought it might help you.

Upvotes: 5

nabeel
nabeel

Reputation: 355

Regarding the last point - whether deleting a row will take off the lock on the row - i think, it will not. The delete itself will get committed only at the end of the transaction; note that if the whole transaction rolls back, the delete also rolls back.

Upvotes: 0

Related Questions