Reputation: 1517
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):
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;
}
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;
}
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:
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
}
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
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
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
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