业务场景:

1.数据库命名不规范,采用驼峰,后因规范化需要改为下划线

对于queryWrapper.eq(Bean::getSomeProp,propVal)这样的代码,影响不大

但是对于queryWrapper.eq("propName",propVal)这样的代码,影响极大(MP原生只支持下划线转驼峰的配置,且eq条件只支持SFunction和column)

方案一:将所有类似eq的地方全部使用SFunction入参,将相关Bean的prop添加@TableField注解,开启自动驼峰转下划线配置

问题:1.column改为SFunction一个个修改效率低,容易遗漏

2.MP提供了alleq,但是业务需要忽略某些字段特殊处理,修改为SFunction入参不支持Ignore的写法

--------------------------------------

特殊处理:

有些地方使用了pojo2MapIgnore的写法,

例如查询时候会传入createTimeBegin,createTimeEnd,updateTimeBegin,updateTimeEnd,在用alleq的时候每次都要把这些字段忽略

写法:qw = new QueryWrapper<Bean>().alleq(MapUtil.pojo2MapIgnores(bean,"createtimeBegin,createtimeEnd,updatetimeBegin,updatetimeEnd".split(",")))

--------------------------------------

方案二:将相关Bean的prop添加@TableField注解,从Mapper层入手,实现字段名自动转column名

原理:

由于MP在初始化的时候其实已经将prop-》column映射存在内存中,只要找到方法即可实现,由于是基于内存的操作,因此不用担心影响Mapper层的速度,实际测试prop-》column耗时0ms,即在毫秒级别无感知

实现原理:LambdaUtils类中的Map<String, Map<String, ColumnCache>> COLUMN_CACHE_MAP 就是映射关系

其中COLUMN_CACHE_MAP的key是tableInfo.getClazz().getName()即com.pojo.Door,Map<String, ColumnCache>key是大写的propName,

ColumnCache内部结构包含两个字段 column和columnSelect

在MP源码的getColumn方法中 onlyColumn ? columnCache.getColumn() : columnCache.getColumnSelect();

其中column就是列名 columnSelect是形如 name as name

 

在LambdaUtils中有方法public static Map<String, ColumnCache> getColumnMap(Class<?> clazz) ,传入相对的Class就可以获得对应的map,由此结合Bean的反射可以轻易的实现prop转Map的方法(如果匹配不上返回原prop),然后再入参为column的方法中加一句fetchColumnSafe

page分页方法比较复杂,因为是直接传入的queryWrapper,需要先把queryWrapper解构,通过

List<ISqlSegment> list = queryWrapper.getExpression().getNormal();

获得sql片段列表,如你传入的条件是name=aaa sex=1

这个list就类似["name","eq","aaa","and","sex","eq","1"]

可以简单的实现为遍历 当i%4==0时候 进行property-》column的转换

但是对于复杂sql 如 select * from A where name="aaa" or (sex="1" and age="11")

list会变成["name","eq","aaa","or","(","sex","eq","1","and","age","eq","11",")"]

AND EQ OR SqlKeyword

name aaa AbstractQueryWrapper

() WrapperKeyword

这几个类都实现了ISqlSegment接口(仅1个getSqlSegment方法 用来获取sql片段)

处理这串有两点:

1.防止恶意注入,针对(),先判断是否是WrapperKeyword对象,再判断字符串

伪代码:
if(list.size()>0){
    ISqlSegment first = list.get(0);
    String prop = first = first.getSqlSegment();
    String col = MPUtils.fetchColumnSafe(prop);
    list.set(0,col)
    //正式处理
    for(int i = 1 ; i< list.size() ; i++){
        ISqlSegment seg = list.get(i);
        if(ISANDOR(seg)){//先判断是否SqlKeyword对象 再判断是否String Equals AND OR
            if(++i<list.size()){
                ISqlSegment next = list.get(i);
                while(ISL(next)){//先判断是否是WrapperKeyword对象 再判断是否是左括号 
                if(++i<list.size())
                    next = list.get(i);
                    else break;
                }
                if(i<=list.size()){
                    String column = MpUtils.fetchColumnSafe(next.getSqlSegment());
                    list.set(i,colum);
                }
            }
        }

    }
}

实际测试发现columnSelect总是空,为什么?

TableFieldInfo类中的 related取决于

!( propertyUpper.equals(columnUpper) ||

propertyUpper.equals(columnUpper.replace(StringPool.UNDERSCORE, StringPool.EMPTY)))

条件1,2都为false relate就为true 这时候才能触发columnSelect的赋值

getSqlSelect主要代码

sqlSelect = SqlUtils.sqlWordConvert(dbType, getColumn(), true);

if (related) {

sqlSelect += (" AS " + SqlUtils.sqlWordConvert(dbType, getProperty(), false));

}

很明显,只有在prop和column下划线转驼峰依然不匹配才有可能触发

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐