最近刚刚接手了泛微OA的项目,项目代码简直是一座庞大的屎山。不得不说,习惯了Spring的生态以后,再去适应其他的框架简直是一种折磨。尤其是这种连JavaBean都不用的项目,所有的参数处理全都是用json和map进行接收和响应……
最让我难以忍受的是,所有和数据库交互的地方全都是在Java代码里面直接嵌入的,代码读起来太阳穴突突跳动,咖啡在胃里翻涌成酸。
所以被逼无奈,只能亲手封装了一个工具类,简直是优雅极了!
package com.api.zp.interfaces.util;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import weaver.conn.RecordSet;
import weaver.general.BaseBean;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @Author zF.
* @ClassName JsonToDbUtil.java
* @ProjectName 通用json数据入库处理工具类
*/
public class JsonToDbUtil {
private static BaseBean base = new BaseBean();
/**
* 按数据库字段顺序将JSONObject写入指定表
* @param tableName 目标数据库表名
* @param json 待写入的JSON对象
* @param customJson 增自定义JSON参数,优先级高于原始json
* @param uniqueField 唯一字段,数据去重使用,避免重复落库
*/
public static void insertByDbFieldOrder(String tableName, JSONObject json, JSONObject customJson, String uniqueField) {
RecordSet rs = new RecordSet();
// 获取数据库字段列表(按表定义顺序)
System.out.println(json);
// 合并原始JSON与自定义JSON(自定义优先级更高)
JSONObject mergedJson = new JSONObject(json);
// 把json的所有key都转换成大写
JSONObject convertedJson = convertKeysToUpperCase(mergedJson);
if(customJson != null) {
convertedJson.putAll(customJson); // 自定义JSON的键会覆盖原始JSON的相同键
}
// 获取唯一字段值(转换为大写匹配数据库字段)
String upperUniqueField = uniqueField.toUpperCase();
Object uniqueValue = convertedJson.get(upperUniqueField);
if(uniqueValue == null) {
base.writeLog("错误:唯一字段[" + upperUniqueField + "]在JSON中不存在,插入终止!");
return;
}
// 检查唯一字段是否已存在
RecordSet checkRs = new RecordSet();
String checkSql = "SELECT " + upperUniqueField + " FROM " + tableName + " WHERE " + upperUniqueField + " IS NOT NULL";
try {
checkRs.execute(checkSql);
// 数据库中是否已经存在当的json
List array = checkRs.getArray();
List<String> newMergeList = mergeList(array);
if (newMergeList.contains(uniqueValue.toString())) {
base.writeLog("唯一字段[" + upperUniqueField + "]值[" + uniqueValue + "]已存在,不重复插入!");
return;
}
} catch (Exception e) {
base.writeLog("唯一字段检查失败!表[" + tableName + "],错误:" + e.getMessage());
throw new RuntimeException("唯一字段检查异常!", e);
}
List<String> dbFields = getTableFields(tableName, convertedJson);
if (dbFields.isEmpty()) {
base.writeLog("警告:表 [" + tableName + "] 无有效字段信息,插入终止!");
return;
}
// 按字段顺序收集参数值(过滤JSON中不存在的字段)
List<Object> params = new ArrayList<>();
for (String field : dbFields) {
Object value = convertedJson.get(field); // 从合并后的JSON获取值(包含自定义字段)
// 处理日期类型自动转换(示例)
// if(value instanceof String && field.contains("DATE")){
// try{
// value = new java.sql.Date(new java.text.SimpleDateFormat("yyyy-MM-dd").parse((String)value).getTime());
// }catch (Exception ignored){}
// }
params.add(value != null ? value : null); // 显式处理null
}
// 构造预编译SQL
String fieldsClause = String.join(", ", dbFields);
String placeholders = String.join(", ", Collections.nCopies(dbFields.size(), "?"));
String sql = "INSERT INTO " + tableName + "(" + fieldsClause + ") VALUES(" + placeholders + ")";
// 执行数据库插入
try {
rs.executeUpdate(sql, params.toArray());
base.writeLog("成功插入表 [" + tableName + "],数据:" + json.toJSONString());
} catch (Exception e) {
base.writeLog("插入失败!表 [" + tableName + "],错误信息:" + e.getMessage());
throw new RuntimeException("数据库插入异常", e);
}
}
/**
* 获取列表内部所有字符串并合并成新的列表
* @param array 目标列表
* @return 新的列表
*/
public static List<String> mergeList(List<Object[]> array) { // 明确参数类型
List<String> stringList = new ArrayList<>();
for (Object[] item : array) { // 直接遍历Object数组
if (item.length > 0) { // 先检查数组长度
Object element = item[0];
if (element instanceof String) {
String strElement = (String) element;
stringList.add(strElement); // 实际添加到结果列表
System.out.println("字符串内容: " + strElement);
}
}
}
return stringList;
}
/**
* 获取数据库表的字段列表(按定义顺序)
* @param tableName 目标表名
* @return 字段列表(有序)
*/
private static List<String> getTableFields(String tableName, JSONObject json ) {
List<String> fields = new ArrayList<>();
RecordSet metaRs = new RecordSet();
// Oracle专用元数据查询(使用ALL_TAB_COLUMNS视图)
String metaSql = "SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS " +
"WHERE TABLE_NAME = '" + tableName + "' AND OWNER = USER " +
"ORDER BY COLUMN_ID";
try {
metaRs.execute(metaSql); // Oracle表名默认大写存储
while (metaRs.next()) {
String field = metaRs.getString("COLUMN_NAME");
if(json.containsKey(field)){ // 检查JSON中key转换为全大写后是否存在该字段
fields.add(field);
}
}
if(fields.isEmpty()){
base.writeLog("警告:表 [" + tableName + "] 无JSON中存在的有效字段,插入终止!");
}
} catch (Exception e) {
base.writeLog("获取表字段失败!表 [" + tableName + "],错误:" + e.getMessage());
throw new RuntimeException("元数据查询异常!", e);
}
return fields;
}
/**
* 递归转换JSON对象的所有key为大写
* @param json 原始JSON对象
* @return 所有key大写的JSON对象(其他内容不变)
*/
public static JSONObject convertKeysToUpperCase(JSONObject json) {
if (json == null) {
return null;
}
JSONObject result = new JSONObject();
// 遍历原始JSON的所有键
for (Object ikey : json.keySet()) {
String key = StrUtil.toString(ikey);
Object value = json.get(key);
String upperKey = key.toUpperCase();
// 递归处理不同类型的值
result.put(upperKey, processValue(value));
}
return result;
}
private static Object processValue(Object value) {
if (value instanceof JSONObject) {
// 递归处理嵌套JSON对象
return convertKeysToUpperCase((JSONObject) value);
} else if (value instanceof JSONArray) {
// 处理JSON数组
JSONArray array = (JSONArray) value;
JSONArray newArray = new JSONArray();
array.forEach(item ->
newArray.add(processValue(item))
);
return newArray;
}
// 基础类型直接返回
return value;
}
}
使用前(例如):
private static void saveToUF_JYYQZL(String requestId, String[] fields) {
RecordSet rs = new RecordSet();
try {
String rzh = getField(fields, 5);
String querySelect = "select rzh from uf_jyyqzl where rzh ='" + rzh + "'";
baseBean.writeLog("查询明细是否存在:");
baseBean.writeLog(querySelect);
rs.execute(querySelect);
if (rs.next()) {
return;
}
// SQL(表结构为43个字段)
String sql = "INSERT INTO UF_JYYQZL(" +
// 按数据库字段顺序严格排列(共43个字段)
"ID,REQUESTID,FORMMODEID,MODEDATACREATER,MODEDATACREATERTYPE," +
"MODEDATACREATEDATE,MODEDATACREATETIME,MODEDATAMODIFIER,MODEDATAMODIFYDATETIME,MODEUUID," +
"SSDM,ZH,HBM,JYRQ,JYSJ,RZH,JYLX,JYXH,HM,FSEBZ," +
"DFZHSSDM,DFZH,DFZHHBM,DFZHHM,DFZHKHX,XZBZ,CZRQ,CZCPH," +
"JYJE,ZHYE,SBYE,SXFZE,PZZL,PZSSDH,PZPCH,PZH," +
"KHCKH,JYM,GYH,CPH,ZY,FY,JYLY) " +
"VALUES(" +
"uf_jyyqzl_Id.nextval,?,?,?,?,TO_CHAR(SYSDATE,'YYYYMMDD'),TO_CHAR(SYSDATE,'HH24MISS'),?,?,?," + // ID(1)由序列生成
"?,?,?,?,?,?,?,?,?,?," + // 10-19
"?,?,?,?,?,?,?,?,?,?," + // 20-29
"?,?,?,?,?,?,?,?,?,?," + // 30-39
"?,?,?)"; // 40-43
Object[] params = new Object[]{
// REQUESTID(字段2)
requestId,
// 系统字段(3个)
154512, // FORMMODEID(字段3)
0, // MODEDATACREATER(字段4)
1, // MODEDATACREATERTYPE(字段5)
// 修改信息(2个)
0, // MODEDATAMODIFIER(字段8)
"", // MODEDATAMODIFYDATETIME(字段9)
"", // MODEUUID(字段10)
// 业务字段(按数据库顺序)
getField(fields, 0), // SSDM(字段11)
getField(fields, 1), // ZH(字段12)
getField(fields, 2), // HBM(字段13)
getField(fields, 3), // JYRQ(字段14)
getField(fields, 4), // JYSJ(字段15)
getField(fields, 5), // RZH(字段16)
getField(fields, 6), // JYLX(字段17)
getField(fields, 7), // JYXH(字段18)
getField(fields, 8), // HM(字段19)
getField(fields, 9), // FSEBZ(字段20)
getField(fields, 10),// DFZHSSDM(字段21)
getField(fields, 11),// DFZH(字段22)
getField(fields, 12),// DFZHHBM(字段23)
getField(fields, 13),// DFZHHM(字段24)
getField(fields, 14),// DFZHKHX(字段25)
getField(fields, 15),// XZBZ(字段26)
getField(fields, 16),// CZRQ(字段27)
getField(fields, 17),// CZCPH(字段28)
parseDecimal(getField(fields, 18)), // JYJE(字段29)
parseDecimal(getField(fields, 19)), // ZHYE(字段30)
parseDecimal(getField(fields, 20)), // SBYE(字段31)
parseDecimal(getField(fields, 21)), // SXFZE(字段32)
getField(fields, 22),// PZZL(字段33)
getField(fields, 23),// PZSSDH(字段34)
getField(fields, 24),// PZPCH(字段35)
getField(fields, 25),// PZH(字段36)
getField(fields, 26),// KHCKH(字段37)
getField(fields, 27),// JYM(字段38)
getField(fields, 28),// GYH(字段39)
getField(fields, 29),// CPH(字段40)
getField(fields, 30),// ZY(字段41)
parseDecimal(getField(fields, 31)), // FY(字段42)
getField(fields, 32) // JYLY(字段43)
};
baseBean.writeLog("参数数量验证:" + params.length); // 必须输出42(ID由序列生成,占位符少1个)
// 在executeUpdate之前
baseBean.writeLog("===== 参数详情 =====");
for (int i = 0; i < params.length; i++) {
System.out.printf("参数 %2d: %s%n", i + 1, params[i]);
}
System.out.println("字段映射关系:");
String[] fieldNames = new String[]{"SSDM", "ZH", "HBM", "JYRQ", "JYSJ", "RZH", "JYLX", "JYXH", "HM", "FSEBZ",
"DFZHSSDM", "DFZH", "DFZHHBM", "DFZHHM", "DFZHKHX", "XZBZ", "CZRQ", "CZCPH",
"JYJE", "ZHYE", "SBYE", "SXFZE", "PZZL", "PZSSDH", "PZPCH", "PZH",
"KHCKH", "JYM", "GYH", "CPH", "ZY", "FY", "JYLY"};
for (int i = 0; i < fields.length; i++) {
System.out.printf("%s(%d) => %s%n",
(i < fieldNames.length) ? fieldNames[i] : "未知字段",
i,
fields[i]);
}
rs.executeUpdate(sql, params);
} catch (Exception e) {
baseBean.writeLog("保存失败:" + e.getMessage());
e.printStackTrace();
}
}
每个类都要单独创建一遍数据库映射关系,然后手动加很多占位符。这是历史遗留代码,虽然一眼Ai,但是依然需要人工核对修正。
使用后(例如):
JSONObject obj = JSONObject.parseObject(e.toString());
// UUID
String randomUUID = IdUtil.randomUUID();
// 需要插入字段的表名
String tableName = "UF_ZJJSYQ";
// 新建自定义json(存入自定义字段)
JSONObject customJson = new JSONObject();
customJson.put("REQUESTID", requestId);
customJson.put("FORMMODEID", 158512);
customJson.put("MODEDATACREATER", 1);
customJson.put("MODEDATACREATERTYPE", 0);
customJson.put("MODEDATACREATEDATE", date);
customJson.put("MODEDATACREATETIME", time);
customJson.put("MODEUUID", randomUUID);
// 唯一字段(数据去重使用)
String uniqueField = "serialId";
// 数据入库
JsonToDbUtil.insertByDbFieldOrder(tableName, obj, customJson, uniqueField);
由于工具类直接从数据库获取字段列表,优雅的省掉了映射关系配置和手动给SQL设置数量占位符,你不需要再关注数据入库的底层逻辑,只需要确认想要入库的数据。如果数据库字段和接收的字段不匹配,则可以通过自定义json来实现无缝替换。还可以手动指定唯一字段(主键),避免数据重复入库。