• springboot解决multi-statement not allow(已部署生产)



    最近有一个需求,要在短时间内大量更新数据库数据,那肯定得上批量更新了。
    springboot支持批量更新的配置很简单。如下:

    //注意,allowMultiQueries要放在第一个参数,否则可能不生效
    jdbc:mysql://MYSQL_IP:MYSQL_PORT/MYSQL_DATABASE?allowMultiQueries=true
    
    • 1
    • 2

    1、问题复现

    本地测试没有问题,部署到测试环境后,就会报错:

    org.springframework.jdbc.UncategorizedSQLException: 
    ### Error updating database.  Cause: java.sql.SQLException: sql injection violation, dbType mysql, druid-version 1.2.16, multi-statement not allow : 
                UPDATE order SET express = ?,company = ? WHERE code = ?
             ; 
                UPDATE order SET express = ?,company = ? WHERE code = ?
             ; 
                UPDATE order SET express = ?,company = ? WHERE code = ?
             ; 
                UPDATE order SET express = ?,company = ? WHERE code = ?
                at com.alibaba.druid.wall.WallFilter.checkInternal(WallFilter.java:835)
    	at com.alibaba.druid.wall.WallFilter.connection_prepareStatement(WallFilter.java:270)
    	at com.alibaba.druid.filter.FilterChainImpl.connection_prepareStatement(FilterChainImpl.java:531)
    	at com.alibaba.druid.filter.FilterAdapter.connection_prepareStatement(FilterAdapter.java:908)
    	at com.alibaba.druid.filter.FilterEventAdapter.connection_prepareStatement(FilterEventAdapter.java:116)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    报错很明显就是,druid数据源默认不允许这种批量更新。看报错的堆栈,是一个叫做WallFilter的过滤器中报出来的。因为我们在springboot的配置文件中添加了filters:stat,wall。就是这个wall的配置引起的。

    2、解决方案

    解决方案有两种。
    第一:很简单,既然是wall引起的,直接把它去掉不就行了。这种方案是可以的。
    第二:如果你确实用到了这个wall呢?那就得想办法,带着wall的同时,还要支持批量更新。
    第一种不说了,删代码就行了。
    直接说第二种方案,我对druid的源码也不熟悉,碰到了这个问题,也是google,
    网上有一大堆解决方案,基本都是说要给WallFilter设置上一个属性:
    multiStatementAllow = true。
    首先来说,解决思路没有问题,就是将这个属性multiStatementAllow的值设置为true,关键是怎么做是有效的?反正我试了网上的思路,不管用,还是报错,要不然就是不报错了,但是会覆盖掉配置文件中数据源相关的配置

    3、解决方案对应的代码

    直接贴代码,已在生产环境验证。
    解决思路很简单,首先将我们已经生成的数据源注入到代码中,然后添加MultiStatementAllow=true的配置。

    @SpringBootApplication()
    public class Application implements CommandLineRunner {
    	//获取到当前项目中的数据源
    	@Autowired
    	private DataSource dataSource;
    
    	public static void main(String[] args) {
    		SpringApplication.run(Application.class, args);
    		log.info("启动成功。。。");
    	}
    
    	@Override
    	public void run(String... args) {
    		try {
    			DruidDataSource druidDataSource = (DruidDataSource) dataSource;
    			List<Filter> proxyFilters = druidDataSource.getProxyFilters();
    			for (Filter filter : proxyFilters) {
    				if (filter instanceof WallFilter) {
    					((WallFilter) filter).setConfig(wallConfig());
    				}
    			}
    			druidDataSource.setFilters("stat,wall");
    			log.info("druid配置信息:{}",druidDataSource);
    		} catch (Exception e) {
    			log.error("给druid数据源设置过滤器异常",e);
    		}
    	}
    
    	@Bean
    	public WallFilter wallFilter(){
    		WallFilter wallFilter = new WallFilter();
    		wallFilter.setConfig(wallConfig());
    		return wallFilter;
    	}
    
    	@Bean
    	public WallConfig wallConfig() {
    		WallConfig config = new WallConfig();
    		//允许一次执行多条语句
    		config.setMultiStatementAllow(true);
    		//允许执行DDL语句
    		config.setNoneBaseStatementAllow(true);
    		return config;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    以上就是如何解决multi-statement not allow的方案

    4、多数据源问题解决方案

    我们可能在项目中引入了多数据源,DynamicRoutingDataSource。这个时候,如果按照上面的方式,从DataSource直接强转DynamicRoutingDataSource会失败。我们需要从DynamicRoutingDataSource获取到DruidDataSource,然后再进行操作。从DynamicRoutingDataSource获取DruidDataSource的方式如下:

    DynamicRoutingDataSource dynamicRoutingDataSource = (DynamicRoutingDataSource) dataSource;
                Map<String, DataSource> dataSources = dynamicRoutingDataSource.getDataSources();
                if (MapUtil.isEmpty(dataSources)) {
                    return;
                }
                for (DataSource tempDataSource : dataSources.values()) {
                    ItemDataSource itemDataSource  = (ItemDataSource) tempDataSource;
                    DruidDataSource druidDataSource = (DruidDataSource) itemDataSource.getRealDataSource();
                    resetFilter(druidDataSource);
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
  • 相关阅读:
    如何制作HTML网页设计【体育运动主题网站——中国篮球NBA】
    Nginx 配置根据路径转发
    java-net-php-python-jsp高校师生交流平台计算机毕业设计程序
    Splay
    js坑之数字型字符串比对只比较第一位数字
    贪心 Leetcode 53 最大子数组和
    Spring Security配置个过滤器也这么卷
    html静态网站基于HTML+CSS+JavaScript上海美食介绍网站网页设计与实现共计5个页面
    六十六、vue组件
    [Azkaban] No active executors found分析
  • 原文地址:https://blog.csdn.net/qq_39839075/article/details/133688830