背景
oracle 的sql in 或者 not in超过1000个项目,会报错。项目中已经大量使用了in,not in,考虑如何从框架层面统一处理。
解决方案
项目启动时候,Mapper会调用ExampleProvider类中的方法生成动态sql, 生成的动态sql的工具类为SqlHelper.java。因此修改SqlHelper.java中的源码即可。
- 修改前
- 修改后
public static String exampleWhereClause() { return "<if test="_parameter != null">" + "<where>\n" + " ${@tk.mybatis.mapper.util.OGNL@andNotLogicDelete(_parameter)}" + " <trim prefix="(" prefixOverrides="and |or " suffix=")">\n" + " <foreach collection="oredCriteria" item="criteria">\n" + " <if test="criteria.valid">\n" + " ${@tk.mybatis.mapper.util.OGNL@andOr(criteria)}" + " <trim prefix="(" prefixOverrides="and |or " suffix=")">\n" + " <foreach collection="criteria.criteria" item="criterion">\n" + " <choose>\n" + " <when test="criterion.noValue">\n" + " ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition}\n" + " </when>\n" + " <when test="criterion.singleValue">\n" + " ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value}\n" + " </when>\n" + " <when test="criterion.betweenValue">\n" + " ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value} and #{criterion.secondValue}\n" + " </when>\n" + " <when test="criterion.listValue">\n" + " ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ( ${criterion.condition}\n" + " <foreach collection="criterion.value" item="listItem" index="index" open="(" close=")">\n" + " <if test="index != 0">\n" + " <choose>\n" + " <when test="index % 999 == 0">) <choose> <when test='criterion.condition.contains("not in")'> AND </when> <otherwise> OR </otherwise></choose> ${criterion.condition} (</when>\n" + " <otherwise>,</otherwise>\n" + " </choose>\n" + " </if>\n" + " #{listItem}\n" + " </foreach>\n" + " )\n" + " </when>\n" + " </choose>\n" + " </foreach>\n" + " </trim>\n" + " </if>\n" + " </foreach>\n" + " </trim>\n" + "</where>" + "</if>"; } 复制代码
- 修改内容
## 如果criterion.listValue为true的时候表示是in, not in的情况。 <when test="criterion.listValue"> ## 解析出外面是and或者or, criterion.condition为:字段 in, 字段 not in ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ( ${criterion.condition} ## 循环遍历值 <foreach collection="criterion.value" item="listItem" index="index" open="(" close=")"> ## index不等于0, 因为0的余数都是0,不能进入循环 <if test="index != 0"> <choose> ## 如果索引被999整除的情况 <when test="index % 999 == 0"> ## 添加括号 ) ## 如果是not in,用and连接, 如果是in, 用or连接 <choose> <when test='criterion.condition.contains("not in")'> AND </when> <otherwise> OR </otherwise> </choose> ## 重新生成 in 或者 not in ${criterion.condition} ( </when> ## 其他的情况,用逗号 <otherwise>, </otherwise> </choose> </if> #{listItem} </foreach> ) </when> 复制代码
- 重点提示
- 修改的方法有两个,一个是exampleWhereClause,另外一个是updateByExampleWhereClause。
- in的话生成的内层用or连接, not in 生成的内层sql 用 and 连接。
测试案例
为了便于测试,用10来代替1000作为边界进行测试。
- 测试代码
- 生成SQL
SELECT USERNAME, PASSWORD_, REALNAME, TITLE, ORG_ID , IS_ENABLED, MULTILOGIN_ENABLED, ORG_PATH, EMAIL, MOBILE , CREATE_TIME, UPDATE_TIME, LAST_PWD_TIME, PWD_ERROR_TIMES, CREATE_BY , UPDATE_BY, PAGE_SIZE, UNLOCK_TIME FROM sys_user WHERE ORG_ID = 'aaa' OR (USERNAME IN ( 'admin0', 'admin1', 'admin2', 'admin3', 'admin4', 'admin5', 'admin6', 'admin7', 'admin8' ) OR USERNAME IN ('admin9', 'admin10')) AND EMAIL = '33333' AND ((USERNAME NOT IN ( 'admin0', 'admin1', 'admin2', 'admin3', 'admin4', 'admin5', 'admin6', 'admin7', 'admin8' ) AND USERNAME NOT IN ('admin9', 'admin10')) OR EMAIL = '33333');