背景
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');
```