背景

oracle 的sql in 或者 not in超过1000个项目,会报错。项目中已经大量使用了in,not in,考虑如何从框架层面统一处理。

解决方案

项目启动时候,Mapper会调用ExampleProvider类中的方法生成动态sql, 生成的动态sql的工具类为SqlHelper.java。因此修改SqlHelper.java中的源码即可。

  • 修改前

image.png

  • 修改后

image.png

  1. public static String exampleWhereClause() {
  2. return "<if test=\"_parameter != null\">" +
  3. "<where>\n" +
  4. " ${@tk.mybatis.mapper.util.OGNL@andNotLogicDelete(_parameter)}" +
  5. " <trim prefix=\"(\" prefixOverrides=\"and |or \" suffix=\")\">\n" +
  6. " <foreach collection=\"oredCriteria\" item=\"criteria\">\n" +
  7. " <if test=\"criteria.valid\">\n" +
  8. " ${@tk.mybatis.mapper.util.OGNL@andOr(criteria)}" +
  9. " <trim prefix=\"(\" prefixOverrides=\"and |or \" suffix=\")\">\n" +
  10. " <foreach collection=\"criteria.criteria\" item=\"criterion\">\n" +
  11. " <choose>\n" +
  12. " <when test=\"criterion.noValue\">\n" +
  13. " ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition}\n" +
  14. " </when>\n" +
  15. " <when test=\"criterion.singleValue\">\n" +
  16. " ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value}\n" +
  17. " </when>\n" +
  18. " <when test=\"criterion.betweenValue\">\n" +
  19. " ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ${criterion.condition} #{criterion.value} and #{criterion.secondValue}\n" +
  20. " </when>\n" +
  21. " <when test=\"criterion.listValue\">\n" +
  22. " ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ( ${criterion.condition}\n" +
  23. " <foreach collection=\"criterion.value\" item=\"listItem\" index=\"index\" open=\"(\" close=\")\">\n" +
  24. " <if test=\"index != 0\">\n" +
  25. " <choose>\n" +
  26. " <when test=\"index % 999 == 0\">) <choose> <when test='criterion.condition.contains(\"not in\")'> AND </when> <otherwise> OR </otherwise></choose> ${criterion.condition} (</when>\n" +
  27. " <otherwise>,</otherwise>\n" +
  28. " </choose>\n" +
  29. " </if>\n" +
  30. " #{listItem}\n" +
  31. " </foreach>\n" +
  32. " )\n" +
  33. " </when>\n" +
  34. " </choose>\n" +
  35. " </foreach>\n" +
  36. " </trim>\n" +
  37. " </if>\n" +
  38. " </foreach>\n" +
  39. " </trim>\n" +
  40. "</where>" +
  41. "</if>";
  42. }
  • 修改内容

    1. ## 如果criterion.listValue为true的时候表示是in, not in的情况。
    2. <when test="criterion.listValue">
    3. ## 解析出外面是and或者or, criterion.condition为:字段 in, 字段 not in
    4. ${@tk.mybatis.mapper.util.OGNL@andOr(criterion)} ( ${criterion.condition}
    5. ## 循环遍历值
    6. <foreach collection="criterion.value" item="listItem" index="index" open="(" close=")">
    7. ## index不等于0, 因为0的余数都是0,不能进入循环
    8. <if test="index != 0">
    9. <choose>
    10. ## 如果索引被999整除的情况
    11. <when test="index % 999 == 0">
    12. ## 添加括号
    13. )
    14. ## 如果是not in,用and连接, 如果是in, 用or连接
    15. <choose>
    16. <when test='criterion.condition.contains("not in")'> AND </when>
    17. <otherwise> OR </otherwise>
    18. </choose>
    19. ## 重新生成 in 或者 not in
    20. ${criterion.condition} (
    21. </when>
    22. ## 其他的情况,用逗号
    23. <otherwise>,
    24. </otherwise>
    25. </choose>
    26. </if>
    27. #{listItem}
    28. </foreach>
    29. )
    30. </when>
  • 重点提示

  1. 修改的方法有两个,一个是exampleWhereClause,另外一个是updateByExampleWhereClause。
  2. in的话生成的内层用or连接, not in 生成的内层sql 用 and 连接。

测试案例

为了便于测试,用10来代替1000作为边界进行测试。

  • 测试代码

image.png

  • 生成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 (
    1. 'admin0',
    2. 'admin1',
    3. 'admin2',
    4. 'admin3',
    5. 'admin4',
    6. 'admin5',
    7. 'admin6',
    8. 'admin7',
    9. 'admin8'
    10. )
    11. OR USERNAME IN ('admin9', 'admin10'))
    AND EMAIL = ‘33333’ AND ((USERNAME NOT IN (
    1. 'admin0',
    2. 'admin1',
    3. 'admin2',
    4. 'admin3',
    5. 'admin4',
    6. 'admin5',
    7. 'admin6',
    8. 'admin7',
    9. 'admin8'
    10. )
    11. AND USERNAME NOT IN ('admin9', 'admin10'))
    12. OR EMAIL = '33333');

```