Optimistic Lock
Optimistic lock is a concurrency control mechanism used to ensure that when a record is updated, it has not been modified by other transactions. MongoPlus provides the
OptimisticLockerInterceptor
plugin, making it simple to implement optimistic locking in your application.
How Optimistic Lock Works
The implementation of optimistic lock generally includes the following steps:
- When reading a record, get the current version number (
version
). - When updating the record, pass this version number along.
- During the update operation, set the condition
version = newVersion
asversion = oldVersion
. - If the version number does not match, the update fails.
Configuring the Optimistic Lock Plugin
To use the optimistic lock plugin, two steps are required:
1. Configure the Plugin
java
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor(){
OptimisticLockerInterceptor optimisticLockerInterceptor = new OptimisticLockerInterceptor();
// Whether to throw an exception when the update fails
optimisticLockerInterceptor.setUpdateFailException(new RuntimeException("Update failed"));
// Whether to throw an exception when the version field is not obtained
optimisticLockerInterceptor.setVersionIsNullException(new MongoPlusException("Version field not obtained"));
// Set the increment value for the optimistic lock
optimisticLockerInterceptor.setAutoInc(1);
// Enable retry
optimisticLockerInterceptor.enableRetry(getRetryStrategy());
return optimisticLockerInterceptor;
}
/**
* Get retry strategy
*/
Retry getRetryStrategy() {
return Retry.builder()
// Maximum retry count
.maxRetryNum(10)
// Callback on first hit
.hitRetry(updateRetryResult -> System.out.println("First hit: " + updateRetryResult))
// Callback on successful update
.onSuccess(object -> System.out.println("Update successful: " + object))
// Callback on failed update
.onFailure(object -> System.out.println("Update failed: " + object))
// Fallback logic after reaching maximum retries
.fallback(((updateRetryResult, invocation) -> {
try {
return invocation.proceed();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}))
// Increase version number on each retry
.autoVersionNum(1)
// Whether to continue executing subsequent interceptors during retry (default true)
// If subsequent interceptors perform other operations, set this to false.
// For example, if subsequent interceptors modify other tables, retry will modify them multiple times.
.processIntercept(true)
// Retry interval
.retryInterval(100L)
// Enable asynchronous retry
// When async retry is enabled, the current update failure response is returned immediately,
// and the asynchronous retry is not under transaction control. You can use callbacks to monitor the final result.
.asyncRetry(true)
.build();
}
2. Add @Version
Annotation on Entity Field
In your entity class, add the @Version
annotation on the field representing the version number:
java
import com.mongoplus.annotation.collection.Version;
public class YourEntity {
@Version
private Integer version;
// Other fields...
}
Notes
- Only
int
type is supported. - Supported methods include built-in
updateById(entity)
,update(entity, wrapper)
,saveOrUpdate(entity)
. You can also manually setupdateWrapper.set("version",0)
via a condition builder. - In
update(entity, wrapper)
method, thewrapper
cannot be reused.
With the above configuration and the @Version
annotation on your entity class, you can easily implement optimistic locking in MongoPlus, effectively preventing data conflicts during concurrent updates.