nagy.zsolt.hun
nagy.zsolt.hun

Reputation: 6694

Spring data JPA - concurrent access of Entity

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

Answers (1)

Viacheslav Shalamov
Viacheslav Shalamov

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

Related Questions