I am trying to create a Restful API with Spring boot and Spring data JPA to do the CRUD operations. The database will be Oracle relational database.Now for concurrent access , If we only use spring transactions using @Transactional, will that serve our purpose of concurrent CRUD operations.
I see there are JPA Optimistic and pessimistic locking strategy version column. My specific question is , for concurrent CRUD operations do we need both Spring transactions and JPA locking strategy? OR only configuring Spring transactions accordingly will be sufficient?
Optimistic lock is default strategy of JPA. Optimistic locking is can be used for most of the applications. Optimistic lock is much more easier and efficient. Pessimistic lock need to be used in cases like, where you need to know Collision before committing your transaction.
So you do not need to configure a locking strategy.
Try to start with the following simple approach that IMO will be suitable in many cases: Optimistic locking with Spring Retry.
1) Add version
property annotated with @Version
to your entities (you can do it in base abstract entity class, for example, to simplify the process):
@Entity
public class MyEntity {
@Id
@GeneratedValue
private Long id;
@Version
private Long version;
// other stuff
}
In this case when you, for example, will update your entity then Hibernate will use the current value of version
property in condition clause of update query, and increment this value to store the entity with it. For example this code of some service:
@Transactional
public Optional<MyEntity> update(Long id, MyEntity source) {
return myEntityRepository
.findById(id)
.map(target -> mapper.updateEntity(source, target));
}
will generate the following SQL queries:
1. select * from my_entities where id = ?;
2. update my_entities set ..., version = <version value from query #1> + 1 where id = ? and version = <version value from query #1>;
So if another concurrent process manages to update this entity first, then your method fails with an exception (OptimisticLockException).
2) To manage to exceptions in that method, add @Retryable
annotation to it (and @EnableRetry
annotation on your config or application class):
@Retryable(maxAttempts = 2)
@Transactional
public Optional<MyEntity> update(Long id, MyEntity source) {
// ...
}
In this case, if an exception rises in that method it will be called again in a new transaction to repeat the operation.
Additional info:
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