Post

Solving Mysteries of JpaOptimisticLockingFailureException in Spring Framework

Do you keep getting a JpaOptimisticLockingFailureException while working with the Spring Framework? Don’t you worry! This exception, although initially intimidating, can be resolved using smart, effective coding strategies. In this comprehensive guide, we will dig into the nitty-gritty of JpaOptimisticLockingFailureException, why it occurs, and how to handle it effectively.

Understanding JpaOptimisticLockingFailureException

JpaOptimisticLockingFailureException is a javax.persistence.OptimisticLockException sub-type, specific to JPA-based data access. Spring’s DataAccessException hierarchy bridges the gap between various persistence technologies like JDBC, Hibernate, JPA or JDO and Spring’s exception handling strategy, allowing the Spring data access framework exceptions to get translated seamlessly to the technology specific exceptions like JpaOptimisticLockingFailureException.

public class JpaOptimisticLockingFailureException 
    extends OptimisticLockingFailureException
...

Why It Happens

Before we dive into solutions, let’s clarify why you’d encounter this exception in the first place. The following are common causes:

A. Unmanaged concurrent access

Multiple threads are concurrently accessing and changing the same data, leading to conflicts that throw JpaOptimisticLockingFailureException.

// Multiple threads trying to access same entity
Thread thread1 = new Thread(() -> {
    repository.findById(id).ifPresent(entity -> {
        entity.changeData();
        repository.save(entity);
    });
});

Thread thread2 = new Thread(() -> {
    repository.findById(id).ifPresent(entity -> {
        entity.changeData();
        repository.save(entity);
    });
});

thread1.start();
thread2.start();

B. Stale data in session state:

An entity fetched from the database at a certain point in time reflects the state of the database rows at that moment. Over time, other transactions could modify those rows, but your entity wouldn’t know about those changes. This stale state could cause JpaOptimisticLockingFailureException.

Entity originalEntity = repository.findById(id).get();
...
// Over time, state of originalEntity becomes stale as database rows change
Entity staleEntity = originalEntity;
...
repository.save(staleEntity); // Throws JpaOptimisticLockingFailureException

Handling JpaOptimisticLockingFailureException

Spring provides two different ways to solve the JpaOptimisticLockingFailureException, one by being optimistic (Optimistic Locking) and the other by being pessimistic (Pessimistic Locking).

A. Using Optimistic Locking

Optimistic Locking relies on checks made at transaction commit time to verify that the data involved in the transaction hasn’t changed since it was read.

1
2
@Version
private Integer version;

B. Using Pessimistic Locking

Pessimistic Locking involves using database-specific locking mechanisms to lock data from modifications as soon as it is read.

1
2
@Lock(LockModeType.PESSIMISTIC_WRITE)
Entity getEntityForUpdate(Long id);

Examples

Let’s look at how to use these strategies with code examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@Entity
public class MyEntity {
    ...
    @Version
    private Long version;
    ...
}

@Service
public class MyEntityService {
    
    @Autowired
    private MyEntityRepository repository;
    
    public void updateEntity(MyEntity entity) {
        try {
            repository.save(entity);
        } catch (JpaOptimisticLockingFailureException ex) {
            ...
        }
    }
}

@Repository
public interface MyEntityRepository extends JpaRepository<MyEntity, Long> {
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    MyEntity getEntityForUpdate(Long id);
}

Conclusion

Handling JpaOptimisticLockingFailureException requires a good understanding of your application’s behavior, especially concerning data access and multi-threading. Both optimistic and pessimistic locking have their pros and cons, but with Spring’s powerful support for data access management, the choice ultimately depends on your specific requirements and scenarios.

Reference:

Happy coding!

This post is licensed under CC BY 4.0 by the author.