业务场景:
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下划线转驼峰依然不匹配才有可能触发