Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring JPA Lock

I am really insecure when it comes to locking in spring jpa.
So please consider this question as an clarification. I really hope I understood it correct, 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:

  • optimistic should be used when less write operation are planned. read operation will not lock an object.
    For example: You have an "money balance" float in an entity with an optimistic lock. Now two processes read this value and use it for calculating and stuff. One of them now changes the value and writes it to the database with an update. No error to this point.
    But now the other process also changes the value and wants to update it. Now there is an error. This only happened because of the second update.
    If the second process would have deleted the instance, nothing would have happend.
  • pessimistic should be used when much write operation are planned. read operation will lock an object.
    For example: You have an "money balance" float in an entity with an pessimistic lock. One process reads the data/value with "findOne".
    After this, an other process wants to read the data as well, what would be possible with an optimistic lock, but with the pessimistic lock he must now wait (no error, just wait).
    When process 1 is ready (changing the value and updating it), process 2 can go on.

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

  • OPTIMISTIC
  • OPTIMISTIC_FORCE_INCREMENT
  • PESSIMISTIC_READ
  • PESSIMISTIC_WRITE
  • PESSIMISTIC_FORCE_INCREMENT

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 an version update has 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 an other 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 a 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 forgot about a 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, here a 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 an 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 Questions: is this correct so far? Does this function really can wait then the data is locked? Can an lock really be deleted when the object is set to null?

Last words:
I really hope I will not annoy somebody. But like I said: it is really difficult for me to understand such complex structures in the english language :(
So thanks a lot for help in any form :) I really appreciate any little help. regardless of whether you give me links for more understanding or answering my questions directly :)

like image 943
christopher2007 Avatar asked Feb 20 '16 12:02

christopher2007


People also ask

How do you do specific locking in Spring JPA?

To specify a lock on a custom query method of a Spring Data JPA repository, we can annotate the method with @Lock and specify the required lock mode type: @Lock(LockModeType. OPTIMISTIC_FORCE_INCREMENT) @Query("SELECT c FROM Customer c WHERE c.

What is locking JPA?

JPA 2 supports both optimistic locking and pessimistic locking. Locking is essential to avoid update collisions resulting from simultaneous updates to the same data by two concurrent users. Locking in ObjectDB (and in JPA) is always at the database object level, i.e. each database object is locked separately.

Does spring @transactional lock table?

"@Transactional" as itself on any isolation level doesn't enabling any locking. To achieve locking behaviour you should use "@Lock" annotation or use " for update" in your query.

What is Pessimistic_write lock?

PESSIMISTIC_WRITE allows us to obtain an exclusive lock and prevent the data from being read, updated or deleted. PESSIMISTIC_FORCE_INCREMENT works like PESSIMISTIC_WRITE, and it additionally increments a version attribute of a versioned entity.


2 Answers

Locking

Locking is used to prevent dirty reads (reading data that hasn't been committed) and non-repeatable reads (reading data that gets 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, before the operation the initial state is compared to the current state. If there is a discrepancy (the resource has been modified in the meantime), 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, 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 which 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.

like image 197
geco17 Avatar answered Oct 05 '22 11:10

geco17


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.

like image 36
Arosha Dissanayake Avatar answered Oct 05 '22 11:10

Arosha Dissanayake