你真的会用mybatis-plus的条件构造器吗?你真的明白mybatis-plus是怎么拼接sql的吗?

54 篇文章 2 订阅
14 篇文章 1 订阅

先让问题扑面而来,你能否看出问题:

    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;
    }

笔者告诉大家,条件构造器很大,很重,慎用!

 

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ForestSpringH

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值