使用case when then语句解决mybatis批量更新multi-statement not allow问题

  • Post author:
  • Post category:其他



近期在开发过程中遇到了MyBatis批量更新的问题,控制台报错

multi-statement not allow

,对应xml文件中的SQL如下:

  <update id="batchUpdateFwzymlAndQqfxx" parameterType="vo.pojo.PageData">
        <foreach collection="fwsqupdatelist" item="fwsq" separator=",">
            UPDATE sjgx_fwml_sqxx
            <set>
                <if test="fwsq.zdqqcs!=null and fwsq.zdqqcs!=''" >
                    zdqqcs= #{fwsq.zdqqcs},
                </if>
                <if test="fwsq.ljqqcs!=null and fwsq.ljqqcs!=''" >
                    ljqqcs= #{fwsq.ljqqcs},
                </if>
                gxsj=NOW()
            </set>
            WHERE id=#{fwsq.id}
        </foreach>
    </update>


执行后报错:






org.springframework.jdbc.UncategorizedSQLException:

### Error updating database.  Cause: java.sql.SQLException: sql injection violation, multi-statement not allow :

UPDATE sjgx_fwml_sqxx

SET zdqqcs = ?,

ljqqcs = ?,

gxsj = NOW( )

WHERE

id =?;

UPDATE sjgx_fwml_sqxx

SET zdqqcs = ?,

ljqqcs = ?,

gxsj = NOW( )

WHERE

id =?;

UPDATE sjgx_fwml_sqxx

SET zdqqcs = ?,

ljqqcs = ?,

gxsj = NOW( )

WHERE

id =?;

UPDATE sjgx_fwml_sqxx

SET zdqqcs = ?,

ljqqcs = ?,

gxsj = NOW( )

WHERE

id =?;

UPDATE sjgx_fwml_sqxx

SET zdqqcs = ?,

ljqqcs = ?,

gxsj = NOW( )

WHERE

id =?;

UPDATE sjgx_fwml_sqxx

SET zdqqcs = ?,

ljqqcs = ?,

gxsj = NOW( )

WHERE

id =?


由控制台可见,其mybatis默认不支持多条语句的更新,需要设置相关属性。从社区里看到的解决方案大致思路为,先检查本地配置文件的url中是否配置了

allowMultiQueries=true

,如果配置了,但还是不起作用,再写一个配置类来解决。

查看本地的application.yml文件中的

url: jdbc:mysql://127.0.0.1:3306/sk_sjz_sjgx_dev?

allowMultiQueries=true

&useUnicode=true&characterEncoding=utf-8&autoReconnect=true

已经加上了allowMultiQueries=true


再写一个配置类MyBatisConfig:

@Configuration
public class MyBatisConfig {

    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource(){
        List<Filter> filterList = null;
        DruidDataSource druidDataSource = new DruidDataSource();
        List<Filter> proxyFilters = druidDataSource.getProxyFilters();
        boolean isExit = false;
        for(Filter filter:proxyFilters){
            if(filter instanceof  WallFilter){
                ((WallFilter) filter).setConfig(wallConfig());
                isExit = true;
            }
        }
        if(!isExit){
            filterList= new ArrayList<>();
            filterList.add(wallFilter());
            druidDataSource.setProxyFilters(filterList);
        }
        return druidDataSource;

    }

    @Bean
    public WallFilter wallFilter(){
        WallFilter wallFilter = new WallFilter();
        wallFilter.setDbType(DbType.MYSQL.getDb());
        wallFilter.setConfig(wallConfig());
        return wallFilter;

    }

    @Bean
    public WallConfig wallConfig(){
        WallConfig config = new WallConfig();
        config.setMultiStatementAllow(true);//允许一次执行多条语句
        config.setNoneBaseStatementAllow(true); //允许非基本语句的其他语句
        return config;
    }

}


但还是不起作用,死活不好使,还报出了其他的错误:


Failed to obtain JDBC Connection: dbType not support : null,url null


………


这个若要讲清楚,必须了解MyBatis和DruidDataSource的底层实现,暂时还没有挖掘那么深,

就尝试了另一种批量更新的方式,即绕开了multi-statement not allow的问题,如下所示,


使用

case when then

语句,可批量更新的操作:

<update id="batchUpdateFwzymlAndQqfxx" parameterType="vo.pojo.PageData">
        UPDATE sjgx_fwml_sqxx
        <trim prefix="set" suffixOverrides=",">

            <trim prefix="zdqqcs =case" suffix="end,">
                <foreach collection="updatelist" item="i" index="index">
                    <if test="i.zdqqcs!=null">
                        WHEN id=#{i.id} THEN #{i.zdqqcs}
                    </if>
                </foreach>
            </trim>

            <trim prefix="ljqqcs =case" suffix="end,">
                <foreach collection="updatelist" item="i" index="index">
                    <if test="i.ljqqcs!=null">
                        WHEN id=#{i.id} THEN #{i.ljqqcs}
                    </if>
                </foreach>
            </trim>

            <trim prefix="gxsj =case" suffix="end,">
                <foreach collection="updatelist" item="i" index="index">
                    <if test="i.gxsj!=null">
                        WHEN id=#{i.id} THEN #{i.gxsj}
                    </if>
                </foreach>
            </trim>

        </trim>
        WHERE id IN
        <foreach collection="updatelist" item="i" open="(" close=")" separator=",">
            #{i.id}
        </foreach>
    </update>


控制台打印执行的sql为:




UPDATE sjgx_fwml_sqxx

SET zdqqcs =

CASE

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

END,

ljqqcs =

CASE

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

END,

gxsj =

CASE

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

WHEN id =? THEN

?

END

WHERE

id IN ( ?, ?, ?, ?, ?, ? )


其他博客中也将末尾处的where条件使用OR来匹配,如:

WHERE

id=?

OR id=?

OR id=?

OR id=?

OR id=?

OR id=?


也是没有问题的,但是实际开发中不建议使用OR。

经测试,


如果IN和OR所在列有索引或主键,OR和IN没有差别,随着数据量的增加,执行计划和执行时间几乎一样



如果IN和OR所在列没有索引和主键,随着数据量的增加,IN的性能不受影响,而OR的性能会下降的非常厉害。



以上方式实际并未解决multi-statement not allow的问题,只是绕开了这个问题,使用了另一种方式来解决,

特此记录,希望遇到或已解决此问题的同学予以指教,互相学习,互相探讨!



版权声明:本文为u013506626原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。