栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

ORM框架tk.mybatis在构造SQL时,条件字段为空,导致条件不生效,bug产生原因源码分析

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ORM框架tk.mybatis在构造SQL时,条件字段为空,导致条件不生效,bug产生原因源码分析

注:
大家好我是妈妈的好大儿,
笔者联系方式
QQ:3302254385
微信:yxc3302254385
交个朋友!
创作不易,三连十分感谢!!!,

线上问题:

由于近期有一个重推物流信息的刷数据需求,在实际生产过程中发生多次oom导致服务down机,最后通过skyWalKing排查原因发现是因为全表扫描导致的问题!

最终定位到具体的代码 见下

    	LopWrapDistribution distribution = new LopWrapDistribution();
        distribution.setWrapNo(wrapNo);
        List distributionList = distributionMapper.select(distribution);

由于wrapNo为空导致wrap_no条件不生效,导致最后的查询语句变成,见下,导致全表扫描

SELECT *  FROM lop_wrap_distribution

本应该是

SELECt *  FROM lop_wrap_distribution where wrap_no = #{?}

对于数据量特别大的表,这是一个特别特别严重切无法容忍的问题,不单单会导致服务oom,也会导致占用mysql的性能

问题原因解析

先看一下tk.mybatis所使用的版本

     
           tk.mybatis
           mapper
           3.3.6
       

然后我们去看一下tkmapper在底层是怎么构建你的查询语句
首先是第一层,我们的抽象接口

public interface SelectMapper {

    
    @SelectProvider(type = BaseSelectProvider.class, method = "dynamicSQL")
    List select(T record);

}

第二层,我们的基础查询具体实现类,看到select方法

public class BaseSelectProvider extends MapperTemplate {

public String select(MappedStatement ms) {
        Class entityClass = getEntityClass(ms);
        //修改返回值类型为实体类型
        setResultType(ms, entityClass);
        StringBuilder sql = new StringBuilder();
        sql.append(SqlHelper.selectAllColumns(entityClass));
        sql.append(SqlHelper.fromTable(entityClass, tableName(entityClass)));
        sql.append(SqlHelper.whereAllIfColumns(entityClass, isNotEmpty()));
        sql.append(SqlHelper.orderByDefault(entityClass));
        return sql.toString();
    }
}

第三层SqlHelper.whereAllIfColumns
这里可以很明显的看到,现在构建XXmapper.xml中执行的SQL语句,来拼接我们where条件部分

    public static String whereAllIfColumns(Class entityClass, boolean empty) {
        StringBuilder sql = new StringBuilder();
        sql.append("");
        //获取全部列
        Set columnList = EntityHelper.getColumns(entityClass);
        //当某个列有主键策略时,不需要考虑他的属性是否为空,因为如果为空,一定会根据主键策略给他生成一个值
        for (EntityColumn column : columnList) {
            sql.append(getIfNotNull(column, " AND " + column.getColumnEqualsHolder(), empty));
        }
        sql.append("");
        return sql.toString();
    }

第四层 拼接SQL的具体实现
我们首先关注
sql.append(getIfNotNull(column, " AND " +column.getColumnEqualsHolder(), empty));
此行代码,然后再一路点下去,见到下面代码为止

    public static String getIfNotNull(String entityName, EntityColumn column, String contents, boolean empty) {
        StringBuilder sql = new StringBuilder();
        sql.append("
            sql.append(entityName).append(".");
        }
        sql.append(column.getProperty()).append(" != null");
        if (empty && column.getJavaType().equals(String.class)) {
            sql.append(" and ");
            if (StringUtil.isNotEmpty(entityName)) {
                sql.append(entityName).append(".");
            }
            sql.append(column.getProperty()).append(" != '' ");
        }
        sql.append("">");
        sql.append(contents);
        sql.append("");
        return sql.toString();
    }

很轻易的就能看明白,这里在写




结论

当我们的对象属性为空时,或者为空字符时,会把此字段忽略掉,从而有可能导致全表扫描

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/937615.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号