使用Druid批量更新报错解决方案

解决Druid连接池批量更新时因多语句不被允许导致的SQL注入异常问题,通过配置allowMultiQueries参数及WallConfig来允许执行多条语句。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Druid批量更新报错

Caused by: java.sql.SQLException: sql injection violation, multi-statement not
allowcom.alibaba.druid.wall.WallFilter.check(WallFilter.java:714) 
atcom.alibaba.druid.wall.WallFilter.connection_prepareStatement(WallFilter.java:240) 
atcom.alibaba.druid.filter.FilterChainImpl.connection_prepareStatement(FilterChainImpl.java:448)
atcom.alibaba.druid.filter.FilterAdapter.connection_prepareStatement(FilterAdapter.java:928) 
atcom.alibaba.druid.filter.FilterEventAdapter.connection_prepareStatement(FilterEventAdapter.java:122)
atcom.alibaba.druid.filter.FilterChainImpl.connection_prepareStatement(FilterChainImpl.java:448)
atcom.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.prepareStatement(ConnectionProxyImpl.java:342) 
atcom.alibaba.druid.pool.DruidPooledConnection.prepareStatement(DruidPooledConnection.java:318)

解决方案:

1、配置数据库连接,添加allowMultiQueries=true
jdbc.url=jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
  • 完整配置
jdbc.url=jdbc:mysql://localhost:3306/db?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root

druid.initialSize=10
druid.minIdle=10
druid.maxActive=50
druid.maxWait=60000
druid.timeBetweenEvictionRunsMillis=60000
druid.minEvictableIdleTimeMillis=300000
druid.validationQuery=SELECT 'x'
druid.testWhileIdle=true
druid.testOnBorrow=false
druid.testOnReturn=false
druid.poolPreparedStatements=true
druid.maxPoolPreparedStatementPerConnectionSize=20
2、如果需要开启wall监控,同时允许multiStatementAllow,就不要在application.yml中配置filter,自己定义
- 注解方式
//使用连接池dataSource

@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
    DruidDataSource druidDataSource = new DruidDataSource();
    List filterList=new ArrayList<>();
    filterList.add(wallFilter());
    druidDataSource.setProxyFilters(filterList);
    return druidDataSource;
}
@Bean
public WallFilter wallFilter(){
    WallFilter wallFilter=new WallFilter();
    wallFilter.setConfig(wallConfig());
    return wallFilter;
}
@Bean
public WallConfig wallConfig(){
    WallConfig config =new WallConfig();
    config.setMultiStatementAllow(true);//允许一次执行多条语句
    config.setNoneBaseStatementAllow(true);//允许非基本语句的其他语句
    return config;
}
- xml配置方式
   <!-- 数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <!-- 基本属性 url、user、password -->
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${druid.initialSize}"/>
        <property name="minIdle" value="${druid.minIdle}"/>
        <property name="maxActive" value="${druid.maxActive}"/>

        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${druid.maxWait}"/>
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}" />

        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" />

        <property name="validationQuery" value="${druid.validationQuery}" />
        <property name="testWhileIdle" value="${druid.testWhileIdle}" />
        <property name="testOnBorrow" value="${druid.testOnBorrow}" />
        <property name="testOnReturn" value="${druid.testOnReturn}" />

        <!-- 打开PSCache,并且指定每个连接上PSCache的大小  如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。-->
        <property name="poolPreparedStatements" value="${druid.poolPreparedStatements}" />
        <property name="maxPoolPreparedStatementPerConnectionSize" value="${druid.maxPoolPreparedStatementPerConnectionSize}" />

        <!-- 配置监控统计拦截的filters -->
        <!-- 关键部分 start-->
        <property name="proxyFilters">
            <list>
                <ref bean="stat-filter"/>
                <ref bean="wall-filter"/>
            </list>
        </property>

    </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"/>
        <property name="noneBaseStatementAllow" value="true"/>
    </bean>
        <!-- 关键部分 end-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:sqlMapConfig.xml"></property>
        <property name="dataSource" ref="dataSource" />
    </bean>
### 关于 ShardingSphere 分表时因表不存在导致的报错问题 在分布式数据库场景下,ShardingSphere 的分片功能通过逻辑表映射到实际物理表来实现数据分布。如果某些物理表未创建或者缺失,则可能会引发 SQL 执行失败等问题。 #### 1. **原因分析** 当使用 ShardingSphere 进行分表操作时,SQL 查询会被路由至多个目标表执行。然而,在配置文件中定义的目标表可能并未全部存在,这可能导致查询过程中抛出异常。例如,`xxMapper.xml` 文件中的 SQL 被解析后无法找到对应的物理表[^2]。 此外,对于聚合函数 `SUM` 和其他嵌套表达式的处理不当也可能引起错误。例如,`IFNULL(SUM(money), 0)` 是不被支持的形式,应改为 `SUM(IFNULL(money, 0))` 来确保兼容性[^1]。 --- #### 2. **解决方案** ##### 方法一:调整分片策略以忽略无效表 可以通过修改分片算法或规则,使那些尚未存在的表暂时不影响整体业务流程。具体做法如下: - 在分片键的基础上增加额外条件过滤掉非法分区。 - 使用自定义分片器(Custom Sharding Algorithm),针对特定情况返回空集合而非真实表名列表。 示例代码展示如何编写一个简单的 Java 自定义分片类: ```java public class CustomTableShardingAlgorithm implements StandardShardingAlgorithm<String> { @Override public Collection<String> doSharding(Collection<String> availableTargetNames, PreciseShardingValue<String> shardingValue) { String logicTableName = shardingValue.getLogicName(); Object value = shardingValue.getValue(); List<String> result = new ArrayList<>(); for (String each : availableTargetNames) { if (!isInvalidTable(each)) { // 假设 isInvalidTable() 判断表是否存在 result.add(each); } } return result; } private boolean isInvalidTable(String tableName) { // 实际应用中可通过元数据或其他方式验证表的有效性 return false; // 返回 true 表示该表不可用 } } ``` 此方法允许开发者灵活控制哪些表参与计算过程,从而规避潜在风险。 --- ##### 方法二:提前初始化所有必要的物理表结构 另一种更为直接的方式是在项目启动阶段预先准备好所有的分片表及其索引列等基础设置。这样即使部分记录为空也不会影响正常运行状态。 以下是基于 Spring Boot 应用程序的一个自动化建表脚本片段: ```sql -- 动态生成各节点上的子表 DELIMITER $$ CREATE PROCEDURE create_sharded_tables(IN total INT) BEGIN DECLARE i INT DEFAULT 1; WHILE i <= total DO SET @ddl := CONCAT('CREATE TABLE IF NOT EXISTS t_order_', i, ' LIKE t_order_template'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; SET i = i + 1; END WHILE; END$$ DELIMITER ; CALL create_sharded_tables(10); -- 创建编号从1到10之间的订单表实例 DROP PROCEDURE create_sharded_tables; ``` 上述存储过程会依据传入参数批量复制模板表的设计模式给定数量的新版本出来,并且利用了 MySQL 特有的动态语句特性完成跨库同步任务。 注意这里假设已经有一个名为`t_order_template`的标准原型可供参照;如果没有的话则需单独设计初始架构再推广开来。 --- ##### 方法三:升级依赖版本消除冲突隐患 有时由于第三方组件间协作不良也会间接造成此类现象发生。比如 Druid 数据源监控工具与 Apache Sharding-JDBC 存在版本差异就容易触发资源定位失误情形。因此建议统一管理 Maven 或 Gradle 构建清单里的条目组合关系使之更加协调一致。 更新后的 POM.XML 可能看起来像下面这样: ```xml <dependencies> <!-- 更高稳定性的 druid spring boot starter --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.8</version> <!-- 推荐较新版本 --> </dependency> <!-- 对应匹配良好的 shardingsphere jdbc starter --> <dependency> <groupId>org.apache.shardingsphere</groupId> <artifactId>sharding-jdbc-spring-boot-starter</artifactId> <version>5.0.0-alpha</version> <!-- 尝试最新预览版修复已知缺陷 --> </dependency> </dependencies> ``` 同时记得清理本地缓存重新下载所需构件以免残留旧包干扰正常使用效果评估测试结果准确性。 --- ### 总结 综上所述,面对 ShardingSphere 中因为缺少预期目标而导致的操作障碍可以从以下几个角度切入寻找突破口:优化现有分片机制使其具备更强健鲁棒性质;主动出击构建完整的底层支撑体系减少不确定性因素干扰程度;最后别忘了审视外部插件集成状况排除不必要的矛盾对立局面共同促进整个系统的平稳高效运转表现水平提升上去。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

superbeyone

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值