How to Prevent and Handle Database Deadlocks in Spring Boot 3

Learn how Spring Boot 3 handles database deadlocks by exploring transaction isolation levels, optimistic and pessimistic locking, and practical code examples that show prevention, detection, and retry strategies to keep your applications reliable under high concurrency.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
How to Prevent and Handle Database Deadlocks in Spring Boot 3

When multiple transactions concurrently access the same data, concurrency can cause deadlocks in Spring Boot applications. This article explains the mechanisms of database deadlocks, transaction isolation levels, optimistic and pessimistic locking, and provides practical Spring Boot code examples for prevention, detection, and handling of deadlocks.

1. Introduction

Concurrent transactions that lock the same resources may lead to deadlocks, where each transaction waits for the other to release a lock, creating a cycle that halts progress. Understanding transaction isolation, lock types, and how databases manage concurrency is essential.

Transaction and Lock Types

A transaction is a series of database operations that must all succeed or all be rolled back, ensuring ACID properties. Databases use locks—shared (read) and exclusive (write)—to enforce isolation. Locks can be applied at row, page, or table level.

Deadlock Mechanism

Deadlocks occur when transactions acquire locks in a circular order, e.g., Transaction A locks row 1 then requests row 2, while Transaction B locks row 2 then requests row 1.

Transaction A locks row 1, then tries to lock row 2.

Transaction B locks row 2, then tries to lock row 1.

Both wait indefinitely, causing a deadlock.

Deadlock diagram
Deadlock diagram

2. Practical Cases

2.1 Update Stock Example

@Service
public class ProductService {
    private final ProductRepository productRepository;
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @Transactional
    public void updateStock(Long productId, int quantity) {
        Product product = productRepository.findById(productId).orElseThrow();
        product.setStock(product.getStock() - quantity);
        productRepository.save(product);
    }
}

If two users update the same product simultaneously, the operations may block rather than deadlock unless multiple resources are locked in conflicting order.

2.2 Choose Appropriate Isolation Level

Isolation levels define how transactions interact. Common levels:

READ COMMITTED – prevents reading uncommitted data.

REPEATABLE READ – guarantees consistent reads within a transaction.

SERIALIZABLE – executes transactions sequentially, eliminating conflicts but reducing performance.

In Spring Boot you can set the level with @Transactional(isolation = Isolation.READ_COMMITTED):

@Service
public class OrderService {
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void processOrder(Long orderId) {
        // ...
    }
}

2.3 Optimistic Lock

Optimistic locking adds a version field to detect concurrent updates without holding locks.

@Entity
@Table(name = "s_product")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Version
    private Integer version;
    private String name;
    private Integer stock;
    // getters, setters
}

If the version has changed, Hibernate throws OptimisticLockException, prompting a retry.

2.4 Pessimistic Lock

Pessimistic locking forces exclusive access until the transaction completes.

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    Optional<Product> findById(Long id);
}

Use it when data loss is unacceptable, such as financial transactions.

2.5 Detect and Handle Deadlocks in Spring Boot

Even with precautions, deadlocks may occur. Databases typically throw a specific exception that can be caught and handled.

public interface UserRepository extends JpaRepository<User, Long> {
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    Optional<User> findById(Long id);
}

@Transactional
public void updateUser(Long id) {
    try {
        this.userRepository.findById(id);
        // other operations
    } catch (Throwable e) {
        e.printStackTrace();
    }
}

Example MySQL command line showing a deadlock:

MySQL command line
MySQL command line

2.6 Exponential Backoff Retry

Retrying with a delay reduces contention.

@Service
public class UserService {
    private final UserRepository userRepository;
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Transactional
    public void updateUser(Long id) {
        try {
            this.userRepository.findById(id);
            // other operations
        } catch (PessimisticLockingFailureException e) {
            System.err.printf("发生异常");
            retryTransaction(id);
        }
    }

    private void retryTransaction(Long id) {
        int retryCount = 0;
        int maxRetries = 3;
        while (retryCount < maxRetries) {
            try {
                updateUser(id);
                return;
            } catch (PessimisticLockingFailureException e) {
                retryCount++;
                long waitTime = (long) Math.pow(2, retryCount) * 100;
                System.out.println("Retrying transaction after " + waitTime + "ms...");
                try {
                    TimeUnit.MILLISECONDS.sleep(waitTime);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
    }
}

This approach avoids immediate retries, reduces competition, and improves the chance of successful transaction completion without triggering additional deadlocks.

For more details, refer to the linked articles on Spring Boot transaction management, optimistic/pessimistic locking, and high‑concurrency best practices.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

optimistic locktransaction isolationpessimistic-lockDatabase Deadlock
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.