I need write-lock a parent entity while i am working with his child entities, in order of not allow to modify (or earse) the parent. I need to do this using Spring and performing the lock directly from the database (to avoid problems if the app is executed in a cluster).
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.
Transaction concepts and locks are different. However, transaction used locks to help it to follow the ACID principles. If you want to the table to prevent others to read/write at the same time point while you are read/write, you need a lock to do this.
"@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.
A lock with LockModeType. PESSIMISTIC_WRITE can be used when querying data and there is a high likelihood of deadlock or update failure among concurrent updating transactions. The persistence implementation must support use of locks of type LockModeType.
In order to implement the strategy you are looking for, you will need to fire a SELECT FOR UPDATE
SQL query on the parent row (for example, SELECT * FROM parent WHERE id = ? FOR UPDATE
. This obtains a lock on the rows fetched by the SELECT
query.
General strategy
SELECT FOR UPDATE
.You can use Spring Transactions to enforce transaction boundaries. Something like the following will work:
class SomeService {
@Transactional
public ... someMethod(...) {
// Load the parent row using SELECT FOR UPDATE.
// Save children.
}
}
@Transactional
will apply transaction semantics around calls to someMethod
. Note that the method must be public
for @Transactional
to work.
Executing a SELECT FOR UPDATE
depends on how exactly you are accessing the database - Spring JDBC, Spring ORM, Spring Data JPA, etc. Here is how you will achieve this with each of these libraries:
Spring JDBC
You can simply execute a SELECT FOR UPDATE
query using the JdbcTemplate
class. jdbcTemplate.execute("SELECT * FROM parent WHERE row = ? FOR UPDATE")
should work.
Spring ORM
You will have to use the ORM-specific template class to enforce the lock mode. For example, with Hibernate4 HibernateTemplate
you can use hibernateTemplate.get(Class<T> entityType, Serializable id, LockMode lock)
.
Spring Data JPA
You can annotate a repository method with @Lock(LockModeType.PESSIMISTIC_WRITE)
to enforce pessimistic locking when the query is executed. For example
interface ParentRepository extends CrudRepository<Parent, Long> {
@Lock(LockModeType.PESSIMISTIC_WRITE)
Parent findOne(Long id);
}
will do the job.
One thing you should be careful about is that if this operation is called too many times concurrently, you will experience time outs and may be deadlocks as well because rows are being exclusively locked. You will benefit from keeping the following in mind:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With