How to Use @Lock in Spring Boot to Solve Concurrency Issues
This article explains how Spring Boot 3.5.0 leverages the @Lock annotation and JPA LockModeType to apply pessimistic read or write locks, shows custom repository implementations, provides concrete code snippets and generated SQL, and emphasizes that all lock operations must run inside a transaction.
1. Introduction
In high‑concurrency scenarios such as order‑status queries, inventory deduction, or sensitive data reads, read/write locks ensure data consistency. Spring Data JPA can apply declarative pessimistic locks via @Lock together with transaction management; the framework translates the lock mode to the appropriate FOR UPDATE or FOR SHARE clause.
JPA’s LockModeType enum provides:
LockModeType.PESSIMISTIC_READ – shared (read) lock.
LockModeType.PESSIMISTIC_WRITE – exclusive (write) lock.
If the underlying database does not support shared locks, PESSIMISTIC_READ falls back to PESSIMISTIC_WRITE.
2. Practical Cases
2.1 Direct entity locking
Use EntityManager.find with a lock mode:
private final EntityManager entityManager;
@Transactional
public Student queryById(Long id) {
return entityManager.find(Student.class, id, LockModeType.PESSIMISTIC_READ);
}Generated SQL (shared lock):
select s1_0.id, s1_0.name, s1_0.sno
from x_student s1_0
where s1_0.id=? for share2.2 Custom repository exposing a generic lock method
Define a generic repository interface:
public interface CustomRepository<T, ID> {
T lockById(Class<T> entityClass, ID id, LockModeType lockMode);
}Implement the interface (implementation class name must end with Impl):
public class CustomRepositoryImpl<T, ID> implements CustomRepository<T, ID> {
@PersistenceContext
private EntityManager entityManager;
@Transactional
@Override
public T lockById(Class<T> entityClass, ID id, LockModeType lockMode) {
return entityManager.find(entityClass, id, lockMode);
}
}Extend the custom repository in a concrete repository:
public interface StudentRepository extends JpaRepository<Student, Long>,
CustomRepository<Student, Long> {
}Use the lock method in a service:
private final StudentRepository studentRepository;
@Transactional
public Student queryById(Long id) {
return studentRepository.lockById(Student.class, id, LockModeType.PESSIMISTIC_READ);
}The generated SQL is identical to the direct‑entity example, using for share.
2.3 Query‑level locking with @Lock
Annotate a repository method with @Lock to lock rows returned by a JPQL query:
public interface PostCommentRepository extends JpaRepository<PostComment, Long> {
@Transactional
@Query("""
select pc
from PostComment pc
where pc.post.id = :postId
""")
@Lock(LockModeType.PESSIMISTIC_READ)
List<PostComment> lockAllByPostId(@Param("postId") Long postId);
}Generated SQL (shared lock):
select pc1_0.id, pc1_0.content, pc1_0.created_at, pc1_0.post_id
from post_comment pc1_0
where pc1_0.post_id=? for shareOverride an existing method to apply an exclusive lock:
@Override
@Transactional
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional<PostComment> findById(Long id);Generated SQL (exclusive lock):
select pc1_0.id, pc1_0.content, pc1_0.created_at, pc1_0.post_id
from post_comment pc1_0
where pc1_0.id=? for updateAll lock‑related operations must be executed within a transactional context.
Environment: Spring Boot 3.5.0
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
