mybatis之mysql中的xml文件理论上不支持批量更新

mybatis之mysql中的xml文件理论上不支持批量更新

通过源码截图看出。

com.alibaba.druid.wall.WallProvider#checkInternal方法中的如下代码。

if (statementList.size() > 1 && !config.isMultiStatementAllow()) {
    violations.add(new IllegalSQLObjectViolation(ErrorCode.MULTI_STATEMENT, "multi-statement not allow", sql));
}

2.作为码农还是要想办法。

网上找了两篇帖子。。

第一篇帖子-------------------------------start--------------------------------------------------

multi-statement not allow 没有设置批处理,解决方案网上很多,大部分基于以下 (本人基于springBoot开发)

(1)设置WallConfig里面的multiStatementAllow 为true

(2)DB URL追加&allowMultiQueries=true 

以上详细内网,网上大把不做赘述。但是对于本人没用。

druid 的配置是基于Filter的,在设置wallConfig的时候事先查看wallConfig在druidDataSource 里面的Filtter是否存在,如果已经存在那么MySqlWallProvider 这个类在初始化的时候就会有可能初始化成另外一个没有配置multiStatementAllow 为true 的Filter,然后放在缓存里面,后面就算你的wallConfig 再次被执行init函数也不再放到缓存里面去.

下面是我在初始化wallConfig的时候打的断点,明显实现就已经存在了WallConfig,如果盲目的在设置一个WallConfig都是白搭,这就是我为什么采用了网上配置之后都是没用的原因


下面这个是取druidDataSource的内容进行初始化

解决方法:在设置之前先判断是都已经存在WallConfig,如果有,直接将现有的替换掉

DB的URL的参数还是得加上&allowMultiQueries=true , 原因网上很多,自行百度,欢迎提问
————————————————

 

第二篇帖子-------------------------------start--------------------------------------------------

有两个原因,一个数据库层面的限制,通过在url地址中添加allowMultiQueries=true可以解决

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test_account?characterEncoding=utf-8&useSSL=false&allowMultiQueries=true

另外一个原因,与Druid的过滤器有关,其本质错误是Druid的防火墙配置(WallConfig)中变量multiStatementAllow默认为false
————————————————

解决方案就是把multiStatementAllow设为true,例如

 <bean name="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="proxyFilters">
            <list>
                <ref bean="stat-filter" />
                <ref bean="wall-filter"/>
            </list>
        </property>
        <!--<property name="filters" value="stat,wall"/>-->
        <!-- <property name="filters" value="mergeStat"/> -->
    </bean>
    <bean id="stat-filter" class="com.alibaba.druid.filter.stat.StatFilter"/>
    <bean id="wall-filter" class="com.alibaba.druid.wall.WallFilter">
        <property name="config" ref="wall-config" />
    </bean>
    <bean id="wall-config" class="com.alibaba.druid.wall.WallConfig">
        <property name="multiStatementAllow" value="true" />
    </bean>
或者通过代码注入新的datasource

 
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
 
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class MybatisConfig {
    @Autowired
    WallFilter wallFilter;
 
    @Bean(name = "dataSource", destroyMethod = "close")    //声明其为Bean实例
    @Primary  //在同样的DataSource中,首先使用被标注的DataSource
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource(){
        DruidDataSource datasource = new DruidDataSource();
 
        // filter
        List<Filter> filters = new ArrayList<>();
        filters.add(wallFilter);
        datasource.setProxyFilters(filters);
 
        return datasource;
    }
 
    @Bean(name = "wallFilter")
    @DependsOn("wallConfig")
    public WallFilter wallFilter(WallConfig wallConfig){
        WallFilter wallFilter = new WallFilter();
        wallFilter.setConfig(wallConfig);
        return wallFilter;
    }
 
    @Bean(name = "wallConfig")
    public WallConfig wallConfig(){
        WallConfig wallConfig = new WallConfig();
        wallConfig.setMultiStatementAllow(true);//允许一次执行多条语句
        wallConfig.setNoneBaseStatementAllow(true);//允许一次执行多条语句
        return wallConfig;
    }
 
}
 
 

本人的解决方案依据如下:

之前写的代码,批量更新的操作都是使用for循环语句进行集合的遍历,一次次调用单条更新的接口,代码既不美观,性能也不好,就想使用MyBatis在一个方法里批量更新数据,我一共想到两种思路,分享一下:

第一种就是使用MyBatis的 <foreach> 去循环创建多条完整的 update 语句,然后交由Mysql数据库一条一条去执行(这个和之前的性能差不多,都是一条一条去更新)
第二种就是使用数据库支持的case when函数,根据不同的条件去更新对应符合条件的数据
我们先来看看第一种方法吧:

1.使用 <foreach> 循环创建多条完整update语句

此处是将需要修改的参数放入一个 List 中,然后遍历list集合,创建多条 update 语句,最终的结果如下:

1.1 MyBatis 的XML语句
<!--批量更新 -->
    <update id="updateListByHouseId" parameterType="java.util.ArrayList">
        <foreach close=";" collection="list" index="index" item="item" open="" separator=";">
            update rba_house_status
            <trim prefix="set" suffixOverrides=",">
                last_push_time = now(),
                <if test="item.pushStatus != null">push_status = #{item.pushStatus},</if>
                <if test="item.createStatus != null">create_status = #{item.createStatus},</if>
                <if test="item.houseCreateTime != null">house_create_time = #{item.houseCreateTime},</if>
                <if test="item.updateStatus != null">update_status = #{item.updateStatus},</if>
                update_time = now(),
                <if test="item.auditStatus != null">audit_status = #{item.auditStatus},</if>
                <if test="item.auditDesc != null">audit_desc = #{item.auditDesc},</if>
                <if test="item.hotelId != null">hotel_id = #{item.hotelId},</if>
            </trim>
            <where>house_id = #{item.houseId}</where>
        </foreach>
    </update>
————————————————

 MyBatis最终生成的SQL

update rba_house_status set last_push_time = now(), push_status = ?, create_status = ?, house_create_time = ?, update_status = ?, update_time = now(), audit_status = ?, audit_desc = ?, hotel_id = ? WHERE house_id = ? ; 
update rba_house_status set last_push_time = now(), push_status = ?, create_status = ?, house_create_time = ?, update_status = ?, update_time = now(), audit_status = ?, audit_desc = ?, hotel_id = ? WHERE house_id = ? ; 

上脚本中的 “?” 对应参数对象中各个对应字段的值
写到这里,本以为第一种方法就可以执行了,直接去进行测试了,就在这时,服务器跟我闹别扭,扭扭捏捏不执行,一直报一个错误: multi-statement not allow 
 

一脸懵逼为啥还不让执行了?
查了半天资料发现,原来MyBatis想要一次执行多条语句是需要进行额外的配置,配置在数据库链接串里面,就是在链接串最后面加一个: &allowMultiQueries=true ,如下所示:

jdbc:mysql://127.0.0.1:3306/test_db?characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&serverTimezone=Asia/Shanghai&allowMultiQueries=true

加完以后信誓旦旦开始了第二次测试执行,按理来说应该到这里就可以,结果执行还是报错,难道是我执行的姿势不对?清除之前编译的文件,再试,还是不行好吧,继续google and 百度…
最后历经千辛万苦,还是没结果,看来还是只能自己找答案了,最后偶然间发现在数据源的配置文件里有一个 com.alibaba.druid.wall.WallFilter 的配置,这个filter里面有个一配置项 com.alibaba.druid.wall.WallConfig ,这个配置项里面有个一 multiStatementAllow ,这个配置默认是false,果断改掉配置,添加一个 multiStatementAllow 的配置,如下:
<bean id="wall-filter-config" class="com.alibaba.druid.wall.WallConfig" init-method="init">
            <property name="dir" value="META-INF/druid/wall/mysql" />
        <property name="selectWhereAlwayTrueCheck" value="false" />
        <property name="selectHavingAlwayTrueCheck" value="false" />
        <property name="multiStatementAllow" value="true" />
    </bean>
————————————————
配置完成,再次执行测试代码!

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值