话说那是一个愉快的周五的下午,刚经历双11双黑五12大促连环迎战,一周的工作也接近结束,任务也都要走向提测的节点了,心里美滋滋,可以早点回家啦~

巴特,popo弹出一份:xxx master 的单元测试静态代码检查失败,单元测试失败用例x个。请在1小时内修复并重新执行,否则可能会打回提测,单测失败详情xxx。

点开详情看看失败原因吧~

org.springframework.jdbc.UncategorizedSQLException:
### Error updating database.  Cause: java.sql.SQLException: It is not allowed to execute a(n) DELETE without where condition, sql:delete from mt_flash_sale_nav
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: delete from mt_flash_sale_nav
### Cause: java.sql.SQLException: It is not allowed to execute a(n) DELETE without where condition, sql:delete from mt_flash_sale_nav
; uncategorized SQLException for SQL []; SQL state [HY000]; error code [0]; It is not allowed to execute a(n) DELETE without where condition, sql:delete from mt_flash_sale_nav; nested exception is java.sql.SQLException: It is not allowed to execute a(n) DELETE without where condition, sql:delete from mt_flash_sale_nav

瓦特,怎么可能,之前还是好好的呀~ 我没动过这块代码呀~一定是加班太多晃了我的狗眼~本地跑一次还是这样~!GG

赶紧联系DBAbaba们,原来是测试环境qs升级啦!xxx


好吧,扯犊子就到这里了

前段时间测试环境ddb开始限制不带where条件的update/delete的sql语句的执行,单测各种失败,且后续还会在生产环境也会这样,于是开始在工程中各种搜索,人工处理难免有遗漏的可能,怎么地也要用程序全部扫描下才放心呀!

那mybatis是如何解析xml和生成sql的呢,比如这样的sql是如何解析的呢?

 <select id="selectByCond" resultMap="BaseResultMap">select    <include refid="Base_Column_List" />from test_db    <where><if test="a != null">and `a` = #{a}      </if><if test="list != null">and b in        <foreach collection="list" open="(" close=")" item="item" separator=",">#{item}        </foreach></if>order by db_update_time    </where></select>

通过分析mybatis的初始化SqlSessionFactoryBean过程,可以一探究竟。

/*** Build a {@code SqlSessionFactory} instance.** The default implementation uses the standard MyBatis {@code XMLConfigBuilder} API to build a* {@code SqlSessionFactory} instance based on an Reader.* Since 1.3.0, it can be specified a {@link Configuration} instance directly(without config file).** @return SqlSessionFactory* @throws IOException if loading the config file failed*/protected SqlSessionFactory buildSqlSessionFactory() throws IOException {// ...if (!isEmpty(this.mapperLocations)) {for (Resource mapperLocation : this.mapperLocations) {if (mapperLocation == null) {continue;}try {XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),configuration, mapperLocation.toString(), configuration.getSqlFragments());// 这里解析xmlxmlMapperBuilder.parse();} catch (Exception e) {throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);} finally {ErrorContext.instance().reset();}if (LOGGER.isDebugEnabled()) {LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");}}} else {if (LOGGER.isDebugEnabled()) {LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");}}return this.sqlSessionFactoryBuilder.build(configuration);}

而sql最终如何生成呢?主要靠SqlSource Sql源接口,代表从xml文件或注解映射的sql内容,主要就是用于创建BoundSql,有实现类DynamicSqlSource(动态Sql源),StaticSqlSource(静态Sql源)等:

/*** Represents the content of a mapped statement read from an XML file or an annotation. * It creates the SQL that will be passed to the database out of the input parameter received from the user.** @author Clinton Begin*/
public interface SqlSource {BoundSql getBoundSql(Object parameterObject);
}

如此,想要打印工程项目中所有sql并判断是否带有where条件就比较明晰了,直接上代码:

    @Resourceprivate SqlSessionFactory sqlSessionFactory;@Testpublic void test_check() {Configuration configuration = sqlSessionFactory.getConfiguration();        System.out.println("#sql.size#" + configuration.getMappedStatements().size());Set<String> errors = Sets.newHashSet();int i = 1;for (Object obj : configuration.getMappedStatements()) {if (obj instanceof MappedStatement) {MappedStatement mappedStatement = (MappedStatement) obj;                String sql = mappedStatement.getSqlSource().getBoundSql(new SqlParamMap()).getSql();sql = sql.replaceAll("\n", "");sql = sql.replaceAll("\\s+", " ");System.out.println(String.format("#sql,#%02d #%s #%s", i++, mappedStatement.getSqlCommandType(), sql));if (!sql.toLowerCase().startsWith("insert") && !sql.toLowerCase().startsWith("select")&& !sql.toLowerCase().startsWith("replace")) {if (!sql.toLowerCase().contains("where")) {errors.add(sql);                    }                }            }        }        System.err.println("#error#" + errors.size());for (String errorSql : errors) {System.err.println(errorSql);        }    }        // 这里为了方便生成sql时,解析入参对象的public static class SqlParamMap extends AbstractMap<String, Object> implements Map<String, Object> {@Overridepublic Set<Entry<String, Object>> entrySet() {return Collections.emptySet();}@Overridepublic Object get(Object key) {return new Object[] {1, 2};}}

如此便可打印出不符合条件的sql语句了。比如在我们haitao-matter工程里搜索出:

#error#21
delete from mt_baby_article_config
delete from mt_coupon_center_nav
update mt_auction_goods set price_check_status = 4
delete from mt_spring_label
delete from mt_scene_brand
delete FROM mt_newgoods_content_configupdate mt_auction_goods_edit set price_check_status = 4
DELETE from mt_coupon_center_coupon_info
delete from mt_spring_label_goods
delete from mt_goods_stock_rel
delete from mt_flash_sale_nav
delete from mt_auction_homeshow_inferior
delete from matter_switcher_param_center
delete from mt_mission_award
delete from `mt_newgoods_category_tab_config`
delete from mt_goods_stock_rel_edit
delete from mt_album_label
delete from matter_app_channel_relations
delete from element_user_baby_coupon_info_log
delete from mt_album_label_category

此外还能打印出工程所有sql出来,比如:

#sql,#1974 #DELETE #delete from mt_auction_homeshow_inferior_edit where id=?
#sql,#1975 #SELECT #select id,title,begin_time,end_time,type,status,db_update_time,content from matter_common_schedule WHERE type =? and begin_time <=? and end_time >=? and type = ? order by begin_time limit ?,?
#sql,#1976 #UPDATE #update element_user_baby_coupon_info_log SET award_info=?, create_time=?, update_time=? where id=?
#sql,#1977 #SELECT #SELECT id from mt_auction_goods where show_status!=2
#sql,#1978 #INSERT #insert into TB_ACTIVITY_SHOW_DETAIL_EDIT (id, zone_id, activity_show_id, related_type, related_id, image_url, image_link, sort_order, config,ui_data ) values
#sql,#1979 #SELECT #select id, related_id, goods_id, goods_title, category_id, category_name, advance_price, question_mark_pos, config, sort_order, db_create_time, db_update_time from mt_advance_price_goods_edit where id = ?
#sql,#1980 #SELECT #select id, apply_category_id, apply_brand_id, import_type, db_create_time,db_update_time, status, goods_qa_scheme_edit_id from goods_qa_category_scope_edit where apply_category_id = ? and apply_brand_id = ? and import_type = ? and goods_qa_scheme_edit_id <> ?
#sql,#1981 #SELECT #select id, skin_scheme_id, skin_order, skin_name, skin_introduce, skin_img_config, skin_gif_config, skin_status, operator, db_create_time, db_update_time from mt_private_custom_skin_config_edit where skin_status = ? order by skin_order asc

如此,算是放心不会又遗漏了。整个思路简单直接,其中涉及到mybatis解析xml和生产动态sql的原理和过程的东西有待分析,这里先留个坑,日后来填。

Reference:

  • http://www.mybatis.org/mybatis-3/dynamic-sql.html

  • https://www.cnblogs.com/fangjian0423/p/mybaits-dynamic-sql-analysis.html

作者:网易工程师-李云鹏

www.toutiao.com/i6874790802107826700

精彩推荐SpringBoot内容聚合IntelliJ IDEA内容聚合Mybatis内容聚合

亲身经历:一次sql缺少where条件的惨案,绩效奖金差点没啦~相关推荐

  1. 亲身经历:一次sql缺少where条件的惨案…

    来源:网易工程师-李云鹏 话说那是一个愉快的周五的下午,刚经历双11双黑五12大促连环迎战,一周的工作也接近结束,任务也都要走向提测的节点了,心里美滋滋,可以早点回家啦- 巴特,popo弹出一份:xx ...

  2. 网易工程师亲历:一次sql缺少where条件的惨案…

    话说那是一个愉快的周五的下午,刚经历双11双黑五12大促连环迎战,一周的工作也接近结束,任务也都要走向提测的节点了,心里美滋滋,可以早点回家啦- 巴特,popo弹出一份:xxx master 的单元测 ...

  3. 我被裁员了!让保安把身患绝症的我被强赶出公司,亲身经历的噩梦!

    本文转载自公众号:你的游戏我的心,希望能帮作者发个声,希望能有更多的人关注这件事,也希望作者维权成功.早日康复. 我是网易的一名游戏策划.14年从上海交大毕业后就进入网易工作,5年里,我和大部分网易员 ...

  4. 网易回应暴力裁员事件并道歉!程序员曝亲身经历逼迫、算计、监视、陷害、威胁,甚至被保安赶出公司...

    作者: 网易前员工   转载自公号:你的游戏我的心 文中的"我"是网易前员工,化名 欢迎转发扩散出去! 网易针对暴力裁员事件发表回应称,确实存在简单粗暴.不近人情等诸多行为,向相关 ...

  5. 逃出你的肖申克(一):为什么一定要亲身经历了之后才能明白?

    前言:<逃出你的肖申克>这个题目我早就放在心中,一直想写一写,但一直没有找到恰当的切入点.上次一个偶然的时候,发现可以以对一些人们常常放在嘴边的俗语进行解释为入口,以一年多来学习的关于思维 ...

  6. 拆掉思维里的墙:亲身经历后才能明白?

    拆掉思维里的墙:亲身经历后才能明白? 该转帖仅你自己可见 该转帖你的好友都能看见 长辈总传授经验教训,我们却不以为然,觉得只己亲身经历后明白的道理才真正属于自己.其实不然.希望本文能帮助你拆掉思维里的 ...

  7. 思维改变生活:很多事情亲身经历之后才会明白

    1. 切身体验 亲身经历一个负性事件带来的情绪记忆要比看着或听说别人遭受一个同样的事件所感受到的强烈得多,形成的负性条件反射也远远更持久.我们一定程度上的确能够感同身受,但心理学实验同样也表明,自己是 ...

  8. 系统出现“预体验成员内版本遇到 问题”错误的处理(亲身经历)

    系统出现"预体验成员内版本遇到问题"错误的处理(亲身经历) 一.概述 2021.05.01晚可能未等Windows预览版更新完毕就关机或强制关机导致第二天早晨启动时出现了" ...

  9. 为什么我们常说很多时候一定要亲身经历了之后才能明白?

    为什么我们常说很多时候一定要亲身经历了之后才能明白? 1. 切身体验.亲身经历一个负性事件带来的情绪记忆要比看着或听说别人遭受一个同样的事件所感受到的强烈得多,形成的负性条件反射也远远更持久.我们一定 ...

最新文章

  1. 图解Transformer,读完这篇就够了
  2. bzoj 1207: [HNOI2004]打鼹鼠
  3. mysql workbench 在模板与数据库间同步
  4. 基于bootstrap实现简单用户管理功能
  5. mysql killed 不掉,解决:kernel: Out of memory: Killed process 15967, UID 27, (mysqld).
  6. 快速找到thtmlbUtil的定义位置
  7. hdu4405:概率dp
  8. java获取系统信息:java的信息、操作系统的信息、用户的信息、虚拟机的信息、系统设置的信息。
  9. 【转】fatal error C1010: unexpected end of file解决方案
  10. 世界之窗浏览器 v 3.6.1.0 [官方最新版]
  11. 基于Scala开发的spark临界点均值法填充缺失值的job
  12. C语言BMP图像的读取、存入、水平镜像、竖直镜像、马赛克模糊处理、灰度二值化处理
  13. Vue3.0 directive的使用说明
  14. C#下usb条码扫描枪的钩子实现的改进
  15. js判断字符串字符数(汉字算两个)
  16. 【数据结构】01-绪论《数据结构 C语言版(严蔚敏、吴伟民)》
  17. python数据分析与可视化【一】python基础实例
  18. LeetCode每日一题11.8
  19. 常用的git命令,基本工作够用了
  20. 打造全国性社会化大数据中心

热门文章

  1. 固态硬盘正确分区方法,好用的硬盘分区工具
  2. 使用aireplay-ng抓握手包笔记
  3. TensorFlow Estimator 官方文档之----内置Estimator
  4. maven jar包导入失败,Cannot resolve XXXXXXX
  5. C#获取系统空闲时间
  6. DataSheet查询网站
  7. 帅到没朋友分数 20
  8. XSS靶场level7秘籍
  9. PDF和CDF图的区别
  10. android实现新闻内容显示功能,Android开发实现自定义新闻加载页面功能实例