java 57 lines · 7 steps

Optimistic locking with @Version in Spring

A JPA @Version field plus @Retryable lets concurrent withdrawals stay correct by retrying when writes collide.

Explained by highlit
1@Entity
2@Table(name = "accounts")
3public class Account {
4 
5 @Id
6 @GeneratedValue(strategy = GenerationType.IDENTITY)
7 private Long id;
8 
9 @Column(nullable = false)
10 private String owner;
11 
12 @Column(nullable = false)
13 private BigDecimal balance;
14 
15 @Version
16 private long version;
17 
18 public void withdraw(BigDecimal amount) {
19 if (balance.compareTo(amount) < 0) {
20 throw new InsufficientFundsException(id, amount);
21 }
22 this.balance = balance.subtract(amount);
23 }
24 
25 public Long getId() {
26 return id;
27 }
28 
29 public BigDecimal getBalance() {
30 return balance;
31 }
32 
33 public long getVersion() {
34 return version;
35 }
36}
37 
38@Service
39public class AccountService {
40 
41 private final AccountRepository accounts;
42 
43 public AccountService(AccountRepository accounts) {
44 this.accounts = accounts;
45 }
46 
47 @Retryable(
48 retryFor = ObjectOptimisticLockingFailureException.class,
49 maxAttempts = 3,
50 backoff = @Backoff(delay = 50))
51 @Transactional
52 public void withdraw(Long accountId, BigDecimal amount) {
53 Account account = accounts.findById(accountId)
54 .orElseThrow(() -> new AccountNotFoundException(accountId));
55 account.withdraw(amount);
56 }
57}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A @Version column lets the database reject stale writes instead of locking rows up front.
  2. 2Optimistic locking trades pessimistic blocking for occasional retries, which suits low-contention workloads.
  3. 3Wrapping the transaction in @Retryable turns a lost-update conflict into a transparent second attempt.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Optimistic locking with @Version in Spring — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code