TKMappper框架oracle in超过1000问题处理

445 阅读1分钟

「这是我参与2022首次更文挑战的第1天,活动详情查看:2022首次更文挑战

背景

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> 
  • 重点提示
  1. 修改的方法有两个,一个是exampleWhereClause,另外一个是updateByExampleWhereClause。
  2. 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');