java 利用tk.mybatis的通用mapper实现异步导出(二)

查询数据直接在job中实现,把提供接口和job合在一起

       这种知识把提供接口的查询的数据放在一起,减少接口的开发,使用通用的方法查询数据并起导出。

  •        优点
    •  只需要把查询条件等入库即可
  • 缺点
    • job功能较复杂,job根据条件查询到数据并起生成文件并起上传等一系列的操作

重点说一下job的核心逻辑和代码实现

首先,简单的说明tk.mybatis 查询数据的方法主要有一下的几种:

1、直接使用系统定义好的方法(selectXXXX)。

      可以使用 Mybatis Generator 直接生成,参数主要有两个参数,一个为查询条件等信息(Example 或 Condition),一个分页数据(RowBounds)

2、使用xml文件中写sql语句

     mybatis 中的方法其实也是在xml文件中定义

<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.math.BigDecimal">
    select
    <include refid="Base_Column_List"/>
    from TABLE_NAME
    where ID = #{id,jdbcType=DECIMAL}
</select>

3、使用注解(@Select 或者 @SelectProvider)

@Select("select max(id) from TABLE_NAME")
public int getMaxId();

核心代码:

由于涉及的代码还是有点多的,所以只粘贴部分代码作为参考,主要看看原理,以后有时间整理出来再上传上来,里面的包可以 URLClassLoader 动态调用

1、获取实体

      exampleJson 为Example序列化的值

Object example = JSON.parseObject(exampleJson, Object.class);
// 数据库实体
Object entityPath = ((JSONObject) example).get("entityClass");
Class<?> entityClass = Class.forName(String.valueOf(entityPath));

2、获取mapper

      tk中的example中有对应的实体对象,我们可以根据实体对象找到对应的mapper

/**
 * 获取mapper
 *
 * @param entityClass 实体类
 * @return
 */
private Mapper getMapper(Class<?> entityClass) {
    // 获取所有的Mapper
    Map<String, Mapper> mappers = applicationContext.getBeansOfType(Mapper.class);
    for (String key : mappers.keySet()) {
         mapper = mappers.get(key);
         Class<?>[] classes = mapper.getClass().getInterfaces();
         for (Class<?> aClass : classes) {
           if (getMapper(entityClass, mapper, aClass)) {
               return mapper;
            }
         }
    }
    return mapper;
}

/**
 * 获取mapper
 *
 * @param entityClass 实体类
 * @param mapper      mapper
 * @param aClass      aClass
 * @return
 */
private boolean getMapper(Class<?> entityClass, Mapper mapper, Class<?> aClass) {
    Type[] types = aClass.getGenericInterfaces();
    for (Type type : types) {
        Type[] arguments = ((ParameterizedType) type).getActualTypeArguments();
        for (Type argument : arguments) {
            String name = ((Class) argument).getName();
            if (name.equals(entityClass.getName())) {
                // 假如获取到的实体类一致,则说明正确
                return true;
            }
        }
    }
    return false;
}

3、动态执行方法查询数据

   动态查询数据的接口,现在支持的参数 Example(Condition),RowBounds和基本参数(基本参数现在直接从Example中提取获取)

private Object getMethodInvoke(Object example, RowBounds rowBounds, Mapper mapper, String methodName) throws InvalidException {
    // 查询总条数,没有则直接返回
    Class<? extends Mapper> mapperClass = mapper.getClass();
    // 获取需要执行的方法
    Method[] methods = mapperClass.getDeclaredMethods();
    Object rValue = null;
    for (Method method : methods) {
        if (!method.getName().equals(methodName)) {
            continue;
        }
        // 方法对应的值
        List<Object> lstValue = new ArrayList<>();
        // 获取所有的参数
        ArrayList<Pair<String, Object>> fieldNameAndValue = JsonObjectUtil.getFieldNameAndValue(example);
        Class<?>[] parameterTypes = method.getParameterTypes();
        for (int i = 0; i < parameterTypes.length; i++) {
            // 验证是不是example的参数
            Class<?> parameterType = parameterTypes[i];
            // 获取注解,指定注解的话按照注解设置的值,否则按照顺序
            Param annotation = parameterType.getDeclaredAnnotation(Param.class);
            if (Object.class.getTypeName().equals(parameterType.getTypeName())) {
                lstValue.add(example);
            } else if ((annotation != null && "example".equals(annotation.value().toLowerCase()))) {
                lstValue.add(example);
            } else if (RowBounds.class.getTypeName().equals(parameterType.getTypeName())) {
                lstValue.add(rowBounds);
            } else {
                if (annotation != null) {
                    // 验证参数的个数是否一致
                    if (parameterTypes.length != fieldNameAndValue.size()) {
                        throw new InvalidException(StatusCodeEnum.UNKNOWN, null, "参数不相等,请联系管理员。");
                    }
                    // 验证参数名称是否一致
                    Optional<Pair<String, Object>> objectEntry = fieldNameAndValue.stream().filter(n -> n.getKey().trim().toUpperCase().equals(annotation.value().toUpperCase().trim())).findFirst();
                    if (objectEntry != null && objectEntry.isPresent()) {
                        Object value = objectEntry.get().getValue();
                        lstValue.add(value);
                    } else {
                        lstValue.add(fieldNameAndValue.get(i).getValue());
                    }
                } else {
                    lstValue.add(fieldNameAndValue.get(i).getValue());
                }
            }
        }
        // 假如参数的个数和参数名称一致
        if (parameterTypes.length == lstValue.size()) {
            try {
                rValue = method.invoke(mapper, lstValue.toArray());
                break;
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new InvalidException(StatusCodeEnum.UNKNOWN, e);
            }
        }
    }
    return rValue;
}

4、导出svc用的组件

File file = new File(fileName);
FileOutputStream fileOutputStream = new FileOutputStream(file);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream, "GB2312");
Writer writer = new BufferedWriter(outputStreamWriter);
CSVPrinter printer = CSVFormat.EXCEL.print(writer);

List<Object> cells = new ArrayList<>();
// 处理数值首字母为零excel打开丢失的问题,这样也只是部分正常,比如文本中有特殊字符还是会异常,这个后面调整
cells.add("=trim(\"".concat(value.toString()).concat("\")"));
printer.printRecord(cells);
printer.flush();

5、导出模板注解

public enum FormatTypeEnum {
    /**
     * 默认当前的值
     */
    DEFAULT("DEFAULT", "当前的值"),
    /**
     * 解析文本
     */
    TEXT("TEXT", "解析文本"),
    /**
     * 读取数据库
     */
    DATABASE("DATABASE", "读取数据库"), 
    /** 
    * 缓存
    */
    CACHE("CACHE","缓存");
    /**
     * 编码
     */
    String code;
    /**
     * 提示信息
     */
    String message;

    /**
     * 构造函数
     *
     * @param code    编码
     * @param message 提示信息
     */
    FormatTypeEnum(String code, String message) {
        this.code = code;
        this.message = message;
    }

    /**
     * 获取编码
     *
     * @return
     */
    public String getCode() {
        return code;
    }

    /**
     * 获取提示信息
     *
     * @return
     */
    public String getMessage() {
        return message;
    }
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExportField {
    /**
     * 表头
     *
     * @return
     */
    String tableHeader() default "未指定";

    /**
     * 当前的值为数值要显示别名是读库还是字符串
     *
     * @return
     */
    FormatTypeEnum formatTypeEnum() default FormatTypeEnum.DEFAULT;

    /**
     * 当前类型为文本,则直接反序列化格式
     * 字符串:[{"值":""显示的名称},{"值":""显示的名称}]
     * 数据库则直接用数据库语句:select cols from table where a = %s
     *
     * @return
     */
    String formatContent() default "{}";

    /**
     * 数据库表实体对应的属性名称,用于定义的导出模板和实体类的属性名称不一致时的转换
     * 若是直接在提示中设置此注解或者名称和实体的名称一致,可以不指定
     *
     * @return
     */
    String fieldName() default "";
}

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

比嗨皮兔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值