你真的会用mybatis-plus的条件构造器吗?你真的明白mybatis-plus是怎么拼接sql的吗?
先让问题扑面而来,你能否看出问题:
private final static QueryWrapper<ElderSon> queryWrapper = new QueryWrapper<>();
private final static UpdateWrapper<ElderSon> updateWrapper = new UpdateWrapper<>();
@Override
public ElderSon getElderSonByElderSonId(String elder_son_id) {
queryWrapper.eq("`elder_son_id`", elder_son_id);
ElderSon elderSon = getOne(queryWrapper);
return elderSon;
}
这是一个service组件的方法,来看看有没有问题?
笔者有话说:
不用运行都知道必然有一个问题是:由于 queryWrapper 笔者做了 final 与 static 修饰这导致了堆内存中这两个条件构造器必然是单例且静态不能改变类型的。
那为什么说这样的性质结合以上的方法代码运行就一定有问题呢?
笔者这么告诉大家:如果在应用的主程序不关闭情况下,运行两次这个方法呢?
有的人就急眼了,为什么像你这么说就会出现问题呢?
笔者说不要急,来看看方法中的 queryWrapper 条件构造器的 eq 方法都做了什么嘛?
笔者带你看源码:
于是笔者不紧不慢的拿出了 queryWrapper 条件构造器的 eq 方法 跟踪链:
1、Compare 接口 eq 被 AbstractWrapper 方法实现:
2、AbstractWrapper 方法 eq 的调用链 :
子类 AbstractLambdaWrapper 继承重写 columnToString 方法调用链:
笔者在这里做了个标记:反射原理,暂时不往下走,总之就是拿到对象与表映射的字段信息。
3、AbstractWrapper 方法 formatSql 调用链:
笔者在这里做个标注:paramNameValuePairs 是一个参数Map,笔者填进去的字段与对应的对象都被存进去了。
4、AbstractWrapper 方法 doIt :
5、MergeSegments 方法 add :
6、AbstractISegmentList 方法 addAll :
eq 方法的跟踪链结束,笔者这里要说的是:其实这是一个流水线模式的加工处理,当我们将条件构造器的方法调用之后,无论给出的参数是什么,它都能根据方法的不同去生成不同的sql,同时我们发现它还会在缓存中进行比对(信息见上方的反射方法,笔者插眼的地方),而我们给进去的参数其实是被放进去了一个参数Map中,最后是采用 #{ } 的方式注入到 sql 中。
分析问题阶段:
那么笔者提出问题的必要是什么?其实就是我们存进去的这些sql信息,并没有清理,下次再调用,就是在后面再加一个条件,那么两次的参数值只要不一样,是不是就错了?
这时候就有人说执行完异步做一个 queryWrapper.clear() 清理一下构造器的 sql :
private final static QueryWrapper<ElderSon> queryWrapper = new QueryWrapper<>();
private final static UpdateWrapper<ElderSon> updateWrapper = new UpdateWrapper<>();
@Override
public ElderSon getElderSonByElderSonId(String elder_son_id) {
try{
queryWrapper.eq("`elder_son_id`", elder_son_id);
ElderSon elderSon = getOne(queryWrapper);
return elderSon;
}finally{
queryWrapper.clear();
}
}
那么笔者就要问了:笔者为什么没想到?为什么这样就对了呢?
其实这样也不对,如果两个请求同时调用到这个方法呢?上一个线程 clear 方法刚好在这一个线程执行完 eq 方法 还未来得及执行 getOne 方法时运行了呢?是不是直接导致 sql 是 null 了?而且我们知道 Tomcat 是采用线程池处理请求,虽然请求并发量高了会复用线程,但是同时两个线程访问同一个方法的可能性还是蛮高的,出现问题的概率很大。
笔者的解决方案:
其实可以加同步且同步清理构造器(二者缺一不可,否则还是会出现上面的问题):
@Override
public synchronized ElderSon getElderSonByElderSonId(String elder_son_id) {
queryWrapper.eq("`elder_son_id`", elder_son_id);
ElderSon elderSon = getOne(queryWrapper);
queryWrapper.clear();
return elderSon;
}
笔者告诉大家,条件构造器很大,很重,慎用!