文章目录
前言
项目上有个需求,需要根据业务类型,动态配置表单信息,根据配置完成的表单字段信息,动态创建与表单对应的业务数据表。然后将表单信息通过流程下发到具体的使用者用户,使用者用户根据业务类型配置的表单,填报表单数据,并提交业务数据到动态表单绑定的动态数据表中。数据提交完成后,要支持对已提交的数据进行修改、查询、删除,同时数据提交要支持批量插入已方便数据导入。
整个需求,动态数据表的创建由其他人实现,论到我这儿就涉及到对表的数据的操作,因此要支持对通过代码创建的持久化业务表的数据的增加、修改、删除、查询操作,技术难度没有,关键点主要是自定义SQL的构建和SQL的执行两个,自定义SQL如果采用纯手工拼接太过杂乱而且容易出错。在实现过程中,采用了如下两个组件:
详细实现过程如下所见。
一、动态SQL格式化器-DynamicSqlFormater
1. 功能介绍
动态SQL格式化器,是所有动态SQL格式化的规范定义,因此该类采用接口。定义了两个方法:
- 通用的动态表的记录行单行格式化方法
- 通用的动态表的记录行多行批量格式化方法
多行记录批量格式化方法提供了一个默认实现,该实现内直接抛出了一个不支持的异常,提供给子类重写实现。依据项目需求情况,目前的示例中,仅提供了INSERT的批量格式化功能。
2. 源码示例
package com.nbcio.modules.flowable.jgpt.dynamic;
import org.apache.ibatis.mapping.SqlCommandType;
import java.util.List;
import java.util.Map;
/**
* @Description
* @Author misterbig
* @Date 2024/1/24
*/
public interface DynamicSqlFormater {
/**
* <h3>根据SqlCommandType格式化sql</h3>
* <strong>支持的格式化类型为</strong>
* <ul>
* <li>{@linkplain SqlCommandType#INSERT},支持批量格式化</li>
* <li>{@linkplain SqlCommandType#UPDATE}</li>
* <li>{@linkplain SqlCommandType#DELETE}</li>
* <li>{@linkplain SqlCommandType#SELECT}</li>
* </ul>
*
* @param dynamicObject {@literal 字段-数据映射}
* @param dynamicField {@literal 字段-类型映射}
* @param table {@literal 动态表名称}
* @return
* @see SqlCommandType
*/
String format(Map<String, Object> dynamicObject, Map<String, Object> dynamicField, String table);
/**
* <h3>根据SqlCommandType批量格式化sql</h3>
* <strong>多个数据对象的格式化: 目前仅支持{@linkplain ClassifySqlFormater#INSERT}</strong>
*
* @param dynamicObjects {@literal 字段-数据映射集合}
* @param dynamicField {@literal 字段-类型映射}
* @param table {@literal 动态表名称}
* @return
*/
default String format(List<Map<String, Object>> dynamicObjects, Map<String, Object> dynamicField, String table) {
throw new UnsupportedOperationException("格式化多个数据对象的SQL目前仅有批量插入支持, 当前实例如需支持需要自行实现格式化逻辑");
}
}
二、动态表字段类型支持器-DynamicFieldSupporter
1. 功能介绍
动态表字段类型支持器,定义了项目要求的三种支持类型(VARCHAR、DATETIME、NUMBER),同时设置了一个其他类型(OTHER)用以提供默认处理。项目采用MYSQL数据库,VARCHAR和DATETIME类型在SQL格式化的时候,可以直接转换为字符串进行写入,只有NUMBER需要单独处理。其他类型,默认按照字符串进行处理。该支持器提供了两个方法:
- 根据字段的类型值查找对应的支持类型,未找到对应类型时候,返回默认类型(OTHER)。
- 根据提交的字段-类型映射关系数据,验证所有字段类型是否能够支持,不支持直接抛出异常并提示。
2. 源码示例
package com.nbcio.modules.flowable.jgpt.dynamic;
import java.util.Arrays;
import java.util.Map;
/**
* @Description
* @Author misterbig
* @Date 2024/1/26
*/
public enum DynamicFieldSupporter {
VARCHAR("1"),
DATETIME("2"),
NUMBER("3"),
OTHER("0");
public final String key;
DynamicFieldSupporter(String key) {
this.key = key;
}
/**
* 检查动态表字段类型处理器是否支持
*
* @param fieldType
* @throws UnsupportedOperationException
*/
public static DynamicFieldSupporter getFieldType(String fieldType) {
return Arrays.stream(DynamicFieldSupporter.values())
.filter(supporter -> supporter.name().equalsIgnoreCase(fieldType) || supporter.key.equals(fieldType))
.findAny()
.orElse(OTHER);
}
/**
* 检查动态表字段类型处理器是否支持
*
* @param fields
* @throws UnsupportedOperationException
*/
public static void checkFieldSupported(Map<String, Object> fields) {
for (Map.Entry<String, Object> field : fields.entrySet()) {
boolean isSupported = false;
String fieldType = String.valueOf(field.getValue());
for (DynamicFieldSupporter supporter : values()) {
if (supporter.name().equalsIgnoreCase(fieldType) || supporter.key.equals(fieldType)) {
isSupported = true;
}
}
if (!isSupported) {
throw new UnsupportedOperationException(
String.format("当前字段类型格式化器暂不支持, 字段名称: {}, 字段类型: {}", field.getKey(), field.getValue()));
}
}
}
}
三、分类SQL格式化器-ClassifySqlFormater
1. 功能介绍
针对项目要求支持的对动态表数据的增加、修改、删除、查询操作,由于该四种类型刚好符合DML的类型,参照org.apache.ibatis.mapping.SqlCommandType定义的操作类型,采用枚举的形式,定义了符合映射关系的四种SQL格式化实现。在INSERT类型的SQL格式化实例中,重写实现了父类的多记录行插入的SQL语句格式化方法,提供批量插入的SQL格式化功能。在格式化的过程中,还添加了系统通用字段及值。SQL格式化主要采用mybatis的SQL 语句构建器进行格式化,比手工拼接更为优雅。
2. 源码示例
package com.nbcio.modules.flowable.jgpt.dynamic;
import com.nbcio.modules.flowable.jgpt.<