近期在开发过程中遇到了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的问题,只是绕开了这个问题,使用了另一种方式来解决,
特此记录,希望遇到或已解决此问题的同学予以指教,互相学习,互相探讨!