背景
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
```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 (
AND EMAIL = ‘33333’ AND ((USERNAME NOT IN ('admin0','admin1','admin2','admin3','admin4','admin5','admin6','admin7','admin8')OR USERNAME IN ('admin9', 'admin10'))
'admin0','admin1','admin2','admin3','admin4','admin5','admin6','admin7','admin8')AND USERNAME NOT IN ('admin9', 'admin10'))OR EMAIL = '33333');
```
