Reputation: 6694
I want to make sure that only 1 thread has access to an Entity in my service function. How to do that?
Example: I want to safely decrease count: even if multiple threads execute OrderService.remove(Product), I don't want 2 DB updates with the same counter value.
In a multithreaded environemnt 2 threads can execute this function at the same time, they may read the same counter value, and therefore update the DB with the same decreased value. This is undesirable.
Product:
@Entity
public class Product {
@Id
@GeneratedValue
private UUID id;
private Integer count;
...
ProductService:
@Service
@Transactional
public class ProductService {
public void remove(UUID productId) {
final Product product = productRepository.findById(productId);
// TODO how to lock product, so no other thread can modify it before this operation is committed?
final Integer currentCount = product.getCount();
product.setCount(currentCount-1);
}
}
Upvotes: 2
Views: 5738
Reputation: 4417
You want you remove(..)
method work as a transaction (so that there can't happen a concurrent update to the Database with this method). In order to do that, you should mark the method as @Transactional
.
As long as serializability guarantees that the effect of concurrent transactions is equivalent to a serial (i.e. sequential) execution of them, setting isolation level explicitly to Isolation.SERIALIZABLE
is enough.
@Transactional(isolation = Isolation.SERIALIZABLE)
public void remove(UUID productId) {
...
}
If you need more advance logic in dealing with transactions, you can use lower isolation levels and retry policy: @Retryable(StaleStateException.class)
.
See https://github.com/spring-projects/spring-retry for details.
Upvotes: 2