前言
有的时候我们需要对mybatis-plus的乐观锁插件进行自定义修改。那么其实mybatis 是已经有个默认的实现,我们只需要将其继承并扩展即可。
0. OptimisticLockerInnerInterceptor 介绍
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
取出记录时,获取当前 version
更新时,带上这个 version
执行更新时, set version = newVersion where version = oldVersion
如果 version 不对,就更新失败 但是我们也可以根据自己的业务需求实现自定义的乐观锁插件。
Mybatis-Plus虽然已经提供了一个默认的乐观锁插件,但是我们也可以根据自己的业务需求实现自定义的乐观锁插件。
1. Mybatis-plus 实现乐观锁的原理
在MyBatis-Plus中,我们可以通过在实体类的版本号字段上添加@Version注解来标识乐观锁字段。在更新一条记录时,MyBatis-Plus会自动将版本号字段的值加1,然后将新的版本号值与更新语句一起发送到数据库中。如果更新成功,说明版本号没有冲突,否则会抛出乐观锁异常。
1. 在Mapper接口中定义更新方法
int updateById(@Param(Constants.ENTITY) T entity);
2. 在实体类的版本号字段上添加@Version注解
public class User { @TableId(type = IdType.AUTO) private Long id; private String name; private Integer age; @Version private Integer version; // ... }
在执行更新操作时,MyBatis-Plus会拦截更新语句,并根据实体类中的@Version注解来判断是否需要加入乐观锁机制。如果实体类中没有@Version注解,则不使用乐观锁机制。
如果实体类中有@Version注解,则MyBatis-Plus会自动将版本号字段的值加1,并将新的版本号值与更新语句一起发送到数据库中。
在数据库中执行更新操作时,MyBatis-Plus会根据更新语句中的版本号值来判断是否需要进行乐观锁校验。如果版本号值与数据库中的版本号值不一致,则说明版本号冲突,此时会抛出乐观锁异常。
2. 自定义乐观锁插件
1. 创建自定义乐观锁插件
先创建一个继承自Mybatis-Plus的OptimisticLockerInnerInterceptor的自定义乐观锁插件类,然后重写其beforeUpdate()方法来实现自己的乐观锁逻辑。
CustomOptimisticLockerInterceptor
public class CustomOptimisticLockerInterceptor extends OptimisticLockerInnerInterceptor { @Override public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) { // 获取实体类对象 Object entity = getEntityFromParameter(parameter); // 获取乐观锁注解 Version versionAnnotation = entity.getClass().getAnnotation(Version.class); if (versionAnnotation != null) { // 获取版本号字段名 String versionColumn = versionAnnotation.value(); // 获取版本号字段的值 Object versionValue = getFieldValue(entity, versionColumn); // 如果版本号为空或者为0,则抛出乐观锁异常 if (版本号为空或为0) { throw new OptimisticLockException("乐观锁异常:版本号为空或为0"); } // 更新版本号字段值 setFieldValue(entity, versionColumn, Integer.parseInt(versionValue.toString()) + 1); } // 调用父类的beforeUpdate()方法 super.beforeUpdate(executor, ms, parameter); } private Object getEntityFromParameter(Object parameter) { if (parameter instanceof Map) { Map map = (Map) parameter; if (map.containsKey("et")) { return map.get("et"); } } else if (parameter != null) { return parameter; } throw new MybatisPlusException("Failed to process, the parameter object is null."); } private Object getFieldValue(Object entity, String fieldName) { try { Field field = entity.getClass().getDeclaredField(fieldName); field.setAccessible(true); return field.get(entity); } catch (Exception e) { throw new MybatisPlusException("Failed to get field value.", e); } } private void setFieldValue(Object entity, String fieldName, Object value) { try { Field field = entity.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(entity, value); } catch (Exception e) { throw new MybatisPlusException("Failed to set字段值.", e); } } }
2. 配置自定义乐观锁插件
在Mybatis-Plus的配置文件中,我们可以将自定义乐观锁插件配置为GlobalConfiguration的属性之一。下面是一个示例:
@Configuration public class MybatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new CustomOptimisticLockerInterceptor()); return interceptor; } }
在这个示例中,我们将自定义乐观锁插件添加到MybatisPlusInterceptor中,然后将其作为bean配置在MybatisPlusConfig类中。这样,我们就成功地配置了自定义乐观锁插件。
3. 总结
通过自定义乐观锁插件,我们可以根据自己的业务需求灵活地实现乐观锁机制。在实现自定义乐观锁插件时,我们需要重写OptimisticLockerInnerInterceptor的beforeUpdate()方法,并实现自己的乐观锁逻辑。最后,我们需要将自定义乐观锁插件配置到Mybatis-Plus的配置文件中。
在重写beforeUpdate()方法时,我们需要获取实体类对象、乐观锁注解、版本号字段名和版本号字段的值,并根据业务需求更新版本号字段的值。如果版本号为空或者为0,我们需要抛出乐观锁异常。
最后,我们需要将自定义乐观锁插件添加到MybatisPlusInterceptor中,这样它才能生效。