Mybatis自定义注解(BatchUpdAnnotation),批量更新处理拦截器

一、需求

  • 通过注解的方式限制mybatis中foreach执行的数据数量
  • 比如传入1000条数据,每次限制处理100条,则通过10次sql进行执行
  • 这样可以避免SQL server限制2100个参数的情况,可以根据自己需要动态调整
  • 注意一个点,下面实现仅支持mapper层包含单个list的参数,其他情况不匹配

二、实现

1、首先创建一个注解:BatchUpdAnnotation,默认一次处理100条,可以自己调整

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BatchUpdAnnotation {

    int batchSize() default 100;

}

2、创建一个Mybatis的拦截器:MybatisBatchUpdateInterceptor,拦截update操作,如果存在对应的注解,则超过batchSize则分批处理,分批处理通过BatchUtil.execute( )方法

@Component
@Intercepts(@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}))
public class MybatisBatchUpdateInterceptor implements Interceptor {

    @Autowired
    @Lazy
    private SqlSessionFactory sqlSessionFactory;


    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        Object[] argsObjects = invocation.getArgs();
        MappedStatement mappedStatement = (MappedStatement) argsObjects[0];
        String id = mappedStatement.getId();
        String mapperClassName = id.substring(0, id.lastIndexOf("."));
        String methodName = id.substring(id.lastIndexOf(".") + 1);
        Class<?> mapperClass = ClassUtils.forName(mapperClassName, getClass().getClassLoader());
        Method method = ReflectionUtils.findMethod(mapperClass, methodName, List.class);
        if (method == null) {
            return invocation.proceed();
        }
        BatchUpdAnnotation batchUpdAnnotation = AnnotationUtils.findAnnotation(method, BatchUpdAnnotation.class);
        if (batchUpdAnnotation == null) {
            return invocation.proceed();
        }
        Object object = argsObjects[1];
        if (object instanceof ParamMap) {
            ParamMap<?> paramMap = (ParamMap<?>) object;
            List<?> pList = paramMap.values().stream().filter(v -> (v instanceof List)).map(v -> ((List<?>) v)).findAny().get();
            int batchSize = batchUpdAnnotation.batchSize();
            if (pList.size() <= batchSize) {
                return invocation.proceed();
            } else {
                SqlSession sqlSession = sqlSessionFactory.openSession(false);
                Object beanObject = sqlSession.getMapper(mapperClass);
                return BatchUtil.execute(beanObject, methodName, batchSize, pList);
            }
        } else {
            return invocation.proceed();
        }
    }
}

3、创建批量处理类:BatchUtil,多次循环调用mapper方法进行更新

public class BatchUtil {
    public static <T> List<T> execute(Class<T> resultType, Object obj, String methodName, int size, Object... args) {
        List<T> result = new ArrayList<>();
        Method method = Arrays.stream(obj.getClass().getDeclaredMethods()).filter(e -> methodName.equals(e.getName())).findFirst().orElseThrow(NoSuchElementException::new);
        List<?> listArg = (List<?>) args[0];
        int total = listArg.size();
        int offset = 0;
        try {
            while (offset < total) {
                int toIndex = Math.min(offset + size, total);
                args[0] = listArg.subList(offset, toIndex);
                result.addAll((List<T>) method.invoke(obj, args));
                offset += size;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    public static Object execute(Object obj, String methodName, int size, Object... args) {
        Method method = Arrays.stream(obj.getClass().getDeclaredMethods()).filter(e -> methodName.equals(e.getName())).findFirst().orElseThrow(NoSuchElementException::new);
        List<?> listArg = (List<?>) args[0];
        int total = listArg.size();
        int offset = 0;
        try {
            while (offset < total) {
                int toIndex = Math.min(offset + size, total);
                args[0] = listArg.subList(offset, toIndex);
                method.invoke(obj, args);
                offset += size;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return total;
    }
}

4、mapper层的使用

@BatchUpdAnnotation(batchSize = 50)
int batchUpdateEmp(@Param("list") List<EmpEntity> empEntityList);

5、mapper.xml

<update id="batchUpdateEmp">
    <foreach collection="list" index="index" item="item" separator=";">
        UPDATE EmpCore
        <set>
            <if test="item.name!= null">
                name = #{item.name},
            </if>
            <if test="item.gender!= null">
                gender = #{item.gender},
            </if>
        </set>
        WHERE empId = #{item.empId}
    </foreach>
</update>

你可能感兴趣的:(mybatis,java)