最近用myabtisplus比较多,发现其中的乐观锁必须查询才可以生效,原理是这样的:
1.如果不是update的方法直接返回:
if (SqlCommandType.UPDATE != ms.getSqlCommandType()) { return; }
2.把参数转化为map并得到entity的class对象:
Map<String, Object> map = (Map<String, Object>) parameter; Object et = map.getOrDefault(Constants.ENTITY, null);
备注:parameter中有两种参数,一种是et一种是ew,et对应的事update(Entity et),通过这种方式的update可以直接将参数封装为其Entity的Class对象,还有一种是ew,当使用QueryWrapper或者lamdaUpdate的时候会封装为ew,因为这两种方式传来的其实只是一个参数不是完整的对象,所以mp无法找到其中的version属性,自然也就无法去实现乐观锁,所以目前看来只有存在entity的时候才可以乐观锁.
所以原理知道了,在update之前必须先查一下才能得到version,然后update的时候在注入进去,本文要说的是不去查一下,因为这边代码已经写了2天了都去弄一下很麻烦~
方法也很简单,就是在其乐观锁插件外包一层提前去查一下并且注入进去就行了:
@Component public class MyOptimisticLockerInterceptor extends OptimisticLockerInnerInterceptor { @Override public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { return false; } @Override public void beforeUpdate(Executor executor, MappedStatement ms, Object parameter) throws SQLException { try { if (SqlCommandType.UPDATE != ms.getSqlCommandType()) { return; } if (parameter instanceof Map) { Map<String, Object> map = (Map<String, Object>) parameter; //获得参数的entity的CLass对象 Object et = map.getOrDefault(Constants.ENTITY, null); Object id = null; Field etversionField=null; if(et==null){ //如果是ew方式没有Entity则强行取id,这里可以忽略不计 /*AbstractWrapper<?, ?, ?> aw = (AbstractWrapper<?, ?, ?>) map.getOrDefault(Constants.WRAPPER, null); String sqlSegment = aw.getExpression().getSqlSegment(); String idSegment = sqlSegment.substring(sqlSegment.indexOf("id = #{ew.paramNameValuePairs.") + 30, sqlSegment.indexOf("id = #{ew.paramNameValuePairs.") + 39); id = aw.getParamNameValuePairs().get(idSegment);*/ }else{ TableInfo ettableInfo = TableInfoHelper.getTableInfo(et.getClass()); if (ettableInfo == null || !ettableInfo.isWithVersion()) { return; } TableFieldInfo etfieldInfo = ettableInfo.getVersionFieldInfo(); etversionField = etfieldInfo.getField(); Field idFiled = et.getClass().getDeclaredField("id"); idFiled.setAccessible(true); id = idFiled.get(et); } //获得当前confuguration Configuration configuration1 = ms.getConfiguration(); //Configuration configuration = GlobalConfigUtils.currentSessionFactory(et.getClass()).getConfiguration(); //获得mapper完整路径 String pNameRef = ms.getId(); //获得包名 String pName = pNameRef.substring(0,pNameRef.lastIndexOf(".")+1); //封装一个查询命令 MappedStatement ms2 = configuration1.getMappedStatement(pName+"selectById"); /*TableInfo tableInfo = TableInfoHelper.getTableInfo(et.getClass()); TableFieldInfo idFieldInfo = tableInfo.getFieldList().stream().filter(x -> x.getProperty().equals("id")).findFirst().get(); Object id = idFieldInfo.getField().get(et);*/ //执行查询,查到version List<Object> query = executor.query(ms2, id, new RowBounds(), Executor.NO_RESULT_HANDLER); if(!CollectionUtils.isEmpty(query)){ Object obj = query.get(0); TableInfo tableInfo = TableInfoHelper.getTableInfo(obj.getClass()); if (tableInfo == null || !tableInfo.isWithVersion()) { return; } TableFieldInfo fieldInfo = tableInfo.getVersionFieldInfo(); Field versionField = fieldInfo.getField(); Object versionVal = versionField.get(obj); //注入version etversionField.set(et,versionVal); //继续乐观锁 doOptimisticLocker(map, ms.getId()); } } //super.beforeUpdate(executor, ms, parameter); }catch (Exception e){ e.printStackTrace(); } } }
将这个MyOptimisticLockerInterceptor代替原来的OptimisticLockerInnerInterceptor就可以了,后面在方法中直接update就可以实现乐观锁了,但是需要注意的是这里依然使用的mp原来的实现方法只不过提前省却了提前获得对象的过程所以mp的不能少,然后这里并没有解决lamdaUpdate无法乐观锁的问题,因为其本质是无法获取到Entity,如果想解决比较蠢得方式就是我上面注释的那样,强行获取到id然后查出来,然后修改时也强行加进去.