批量修改时的错误信息:
Caused by: java.sql.SQLException: sql injection violation, multi-statement not allow
SQL注入冲突,多语句不允许.
由于开启了wall SQL监控 批量SQL不允许执行.
源码报错位置 multiStatementAllow为false抛出的异常
有两种解决办法:
1.关掉wall监控
2.配置wall 将multiStatementAllow配置为true
先看配置
db.properties
#Oracle
#jdbc.driver=oracle.jdbc.driver.OracleDriver
#jdbc.url=jdbc:oracle:thin:@localhost:1521:orcl
#jdbc.username=scott
#jdbc.password=admin
#MySQL
# driverClassName 根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/sys?allowMultiQueries=true&autoReconnect=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=admin
# 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
initialSize=0
# 最大连接池数量
maxActive=1000
#定义最小空闲
minIdle=1
# 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,
# 缺省启用公平锁,并发效率会有所下降,
# 如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
maxWait=60000
# druid 监控
# 属性类型是字符串,通过别名的方式配置扩展插件,
# 常用的插件有:
# 监控统计用的filter:stat
# 日志用的filter:log4j
# 防御sql注入的filter:wall
filters=stat,log4j
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis=300000
# 建议配置为true,不影响性能,并且保证安全性。
# 申请连接的时候检测,如果空闲时间大于
# timeBetweenEvictionRunsMillis,
# 执行validationQuery检测连接是否有效。
testWhileIdle=true
# 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnBorrow=false
# 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testOnReturn=false
# 是否缓存preparedStatement,也就是PSCache。
# PSCache对支持游标的数据库性能提升巨大,比如说oracle。
# 在mysql5.5以下的版本中没有PSCache功能,建议关闭掉。
# 作者在5.5版本中使用PSCache,通过监控界面发现PSCache有缓存命中率记录,
# 该应该是支持PSCache。
poolPreparedStatements=false
# 要启用PSCache,必须配置大于0,当大于0时,
# poolPreparedStatements自动触发修改为true。
# 在Druid中,不会存在Oracle下PSCache占用内存过多的问题,
# 可以把这个数值配置大一些,比如说100
maxOpenPreparedStatements=-1
spring配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:/properties/db.propertiesvalue>
list>
property>
bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="driverClassName" value="${jdbc.driver}" />
<property name="initialSize" value="${initialSize}"/>
<property name="minIdle" value="${minIdle}" />
<property name="maxActive" value="${maxActive}" />
<property name="maxWait" value="${maxWait}">property>
<property name="filters" value="${filters}" />
<property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
<property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
<property name="testWhileIdle" value="${testWhileIdle}"/>
<property name="testOnBorrow" value="${testOnBorrow}"/>
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="${poolPreparedStatements}"/>
<property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}"/>
<property name="timeBetweenLogStatsMillis">
<value>60000value>
property>
<property name="statLogger" ref ="statLoggerb"/>
<property name="proxyFilters">
<list>
<ref bean="wall-filter"/>
list>
property>
bean>
<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>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:/mybatis/mybatis.cfg.xml" />
<property name="mapperLocations" value="classpath:com/ys/mapper/*Mapper.xml"/>
bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.ys.dao" />
bean>
<context:component-scan base-package="com.ys.service"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="insert*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
<tx:method name="delete*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="find*" propagation="SUPPORTS" read-only="true" />
<tx:method name="select*" propagation="SUPPORTS" read-only="true" />
<tx:method name="get*" propagation="SUPPORTS" read-only="true" />
tx:attributes>
tx:advice>
<bean id="statLoggerb" class="com.alibaba.druid.pool.DruidDataSourceStatLoggerImpl">
<property name="logger" ref="log4jb" />
bean>
<bean id="log4jb" class="com.alibaba.druid.support.logging.Log4jImpl">
<constructor-arg>
<value>druid.statlogvalue>
constructor-arg>
bean>
<bean id="druid-stat-interceptor"
class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor">
bean>
<bean id="druid-stat-pointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut" scope="prototype">
<property name="patterns">
<list>
<value>com.ys.service.*value>
<value>com.ys.dao.*value>
list>
property>
bean>
<aop:config>
<aop:advisor advice-ref="druid-stat-interceptor" pointcut-ref="druid-stat-pointcut" />
aop:config>
beans>
这是完整的配置信息
解决办法
1.去除wall监控
在db.properties里找到 filters 去掉wall
并在jdbc的URL 上加上allowMultiQueries=true参数 ,最好是加在最前边
此方法配置后在Druid的监控页是没有SQL监控信息的 所以不推荐此方法 ,下边看第二种
2.配置wall
wall的配置与filters分开 ,db里的filters不需要配置wall
然后在连接池配置后边加上
<property name="proxyFilters">
<list>
<ref bean="wall-filter"/>
list>
property>
单独配置wall
再单独引用wall的filter
<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>
此时再执行批量操作就可以了.
SQL如下:
<update id="batchUpdate" parameterType="java.util.Map">
<foreach collection="list" item="cus" index="index" open="" close="" separator=";">
UPDATE sys_power
<trim prefix=" SET " prefixOverrides=",">
<if test="cus.menu_name != null"> , menu_name = #{cus.menu_name} if>
<if test="cus.menu_icon != null"> , menu_icon = #{cus.menu_icon} if>
<if test="cus.menu_type != null"> , menu_type = #{cus.menu_type} if>
<if test="cus.pid != null"> , pid = #{cus.pid} if>
<if test="cus.menu_url != null"> , menu_url = #{cus.menu_url} if>
<if test="cus.sort_num != null"> , sort_num = #{cus.sort_num} if>
<if test="cus.create_id != null"> , create_id = #{cus.create_id} if>
<if test="cus.create_time != null"> , create_time = #{cus.create_time} if>
<if test="cus.modify_id != null"> , modify_id = #{cus.modify_id} if>
<if test="cus.modify_time != null"> , modify_time = #{cus.modify_time} if>
<if test="cus.is_use != null"> , is_use = #{cus.is_use} if>
<if test="cus.is_del != null"> , is_del = #{cus.is_del} if>
<if test="cus.status != null"> , status = #{cus.status} if>
<if test="cus.remark != null"> , remark = #{cus.remark} if>
trim>
<trim prefix=" WHERE " prefixOverrides="">
<if test="cus.id != null"> id = #{cus.id}if>
trim>
foreach>
update>
dao
/**
* @Title: batchUpdate
* @Description: 批量修改
* @param @param param 接收map参数 {list:list}
* @return int
* @date createTime:2018年6月8日下午6:17:35
*/
int batchUpdate(Map param);
service
/**
* @Title: batchUpdate
* @Description: 批量修改菜单信息
* @param @param list
* @return Map
public Map
controller
/**
* @Title: batchUpdate
* @Description: 批量修改菜单
* @param @param list
* @return Map
@RequestMapping(value="batchUpdate",method={RequestMethod.POST,RequestMethod.GET})
@ResponseBody
public Map
JSONUtils工具类
package com.ys.utils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
/**
*@Title JSONUtils.java
*@description: JSON工具类
*@time 创建时间:2018年6月9日 上午10:39:21
**/
public class JSONUtils {
/**
* @Title: strToList
* @Description: json字符串转list对象集合
* @param @param str
* @param @param clazz
* @return List
* @date createTime:2018年6月9日上午10:42:16
*/
@SuppressWarnings("unchecked")
public static List strToList(String str, Class clazz) {
JSONArray json = JSONArray.fromObject(str);
JSONObject object = null;
T t = null;
List list = new ArrayList<>();
for (int i = 0; i < json.size(); i++) {
object = JSONObject.fromObject(json.get(i));
t = (T) JSONObject.toBean(object, clazz);
list.add(t);
}
return list;
}
}
var list=[{“id”:4,”sort_num”:0},{“id”:9,”sort_num”:1},{“id”:10,”sort_num”:2}];
ajax传入data:{json:JSON.stringify(list)}
记录错误.