SSM+Druid使用mybatis不能批量执行SQL问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36476972/article/details/80633725

批量修改时的错误信息:

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配置

<?xml version="1.0" encoding="UTF-8"?>
<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">


<!--Spring Dao Start-->
    <!-- 读取properties文件 -->
    <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:/properties/db.properties</value>
            </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>
        <!-- 监控统计用的filter:stat   日志用的filter:log4j  防御sql注入的filter:wall -->
        <property name="filters" value="${filters}" />
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}" />
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}" />
        <!-- 建议配置为true,不影响性能,并且保证安全性。 申请连接的时候检测 -->
        <property name="testWhileIdle" value="${testWhileIdle}"/>
        <!-- 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 -->
        <property name="testOnBorrow" value="${testOnBorrow}"/>
        <!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 -->
        <property name="testOnReturn" value="false" />
        <!-- 是否缓存preparedStatement,也就是PSCache  适用支持游标的数据库  如Oracle -->
        <property name="poolPreparedStatements" value="${poolPreparedStatements}"/>
        <!-- 要启用PSCache,必须配置大于0,当大于0时 poolPreparedStatements自动触发修改为true。 -->
        <property name="maxOpenPreparedStatements" value="${maxOpenPreparedStatements}"/>
        <!-- 定义监控日志输出间隔 -->
        <property name="timeBetweenLogStatsMillis">  
            <value>60000</value>  
        </property>  
        <property name="statLogger" ref ="statLoggerb"/> 
        <!-- 若需要mybatis的批量sql需配置     不配置则报错:nested exception is java.sql.SQLException: sql injection violation, multi-statement not allow-->
        <property name="proxyFilters">
            <list>  
                <ref bean="wall-filter"/>  
            </list>
        </property>    
    </bean>
    <!-- 若需要mybatis的批量sql需配置 -->
    <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> 



    <!-- 让spring管理sqlsessionfactory 使用mybatis和spring整合包中的 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 数据库连接池 -->
        <property name="dataSource" ref="dataSource" />
        <!-- 加载mybatis的全局配置文件 -->
        <property name="configLocation" value="classpath:/mybatis/mybatis.cfg.xml" />
        <!-- 加载Mybatis全局配置文件 -->
        <property name="mapperLocations" value="classpath:com/ys/mapper/*Mapper.xml"/>
    </bean>

    <!--扫描dao-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.ys.dao" />
        <!-- <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> -->
    </bean>

<!--Spring Dao End-->

<!--Spring Service Start-->

    <context:component-scan base-package="com.ys.service"/>

<!--Spring Service End-->

<!--Spring Transaction Start-->

    <!-- 事务管理器 -->
    <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>

    <!--Spring Transaction End-->


    <!-- Druid监控日志输出 实现 -->
    <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.statlog</value>  
        </constructor-arg>  
    </bean> 


    <!-- druid spring monitor 监控 setting -->
    <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

然后在连接池配置后边加上

<!-- 若需要mybatis的批量sql需配置     不配置则报错:nested exception is java.sql.SQLException: sql injection violation, multi-statement not allow-->
<property name="proxyFilters">
            <list>  
                <ref bean="wall-filter"/>  
            </list>
        </property> 

单独配置wall

再单独引用wall的filter

<!-- 若需要mybatis的批量sql需配置 -->
<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">  
    <!-- 此处collection的值list为map的key值 -->  
        <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<T>}
     * @return int
     * @date createTime:2018年6月8日下午6:17:35
     */
    int batchUpdate(Map<String,Object> param);

service

/**
     * @Title: batchUpdate 
     * @Description: 批量修改菜单信息
     * @param @param list
     * @return Map<Object,Object>
     * @date createTime:2018年6月8日下午6:23:13
     */
    public Map<Object, Object> batchUpdate(HttpServletRequest request,List<Power> list){
        Map<String, Object> map = new HashMap<>();
        if(list.size() == 0){
            return CodeUtil.mapResult(new Object[]{"status","error","msg","请求参数有误!"});
        }
        User user = (User)request.getSession().getAttribute("user");
        for (int i = 0; i < list.size(); i++) {
            list.get(i).setModify_id(user.getId());
            list.get(i).setModify_time(DateUtils.getCurrentDate("", String.class));
        }
        map.put("list", list);
        int result = mapper.batchUpdate(map);
        if(result > 0){
            return CodeUtil.mapResult(new Object[]{"status","success","msg","修改成功!"});
        }
        return CodeUtil.mapResult(new Object[]{"status","error","msg","修改失败!"});
    }

controller

/**
     * @Title: batchUpdate 
     * @Description: 批量修改菜单
     * @param @param list
     * @return Map<Object,Object>
     * @date createTime:2018年6月8日下午6:24:36
     */
    @RequestMapping(value="batchUpdate",method={RequestMethod.POST,RequestMethod.GET})
    @ResponseBody
    public Map<Object, Object> batchUpdate(HttpServletRequest request,@RequestParam(value="json")String json){
        List<Power> list = JSONUtils.strToList(json, Power.class);
        return service.batchUpdate(request,list);
    }

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<T>
     * @date createTime:2018年6月9日上午10:42:16
     */
    @SuppressWarnings("unchecked")
    public static <T> List<T> strToList(String str, Class<T> clazz) {
        JSONArray json = JSONArray.fromObject(str);
        JSONObject object = null;
        T t = null;
        List<T> 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)}

记录错误.

猜你喜欢

转载自blog.csdn.net/qq_36476972/article/details/80633725