sql执行增加动态参数功能

This commit is contained in:
暮光:城中城
2021-06-27 22:45:30 +08:00
parent 6da66fac7c
commit 03dff7620e
12 changed files with 532 additions and 124 deletions

View File

@@ -1,6 +1,7 @@
package com.zyplayer.doc.db.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.zyplayer.doc.core.annotation.AuthMan;
@@ -18,8 +19,10 @@ import com.zyplayer.doc.db.framework.db.mapper.base.ExecuteParam;
import com.zyplayer.doc.db.framework.db.mapper.base.ExecuteResult;
import com.zyplayer.doc.db.framework.db.mapper.base.ExecuteType;
import com.zyplayer.doc.db.framework.db.mapper.base.SqlExecutor;
import com.zyplayer.doc.db.framework.db.transfer.SqlParseUtil;
import com.zyplayer.doc.db.framework.json.DocDbResponseJson;
import com.zyplayer.doc.db.framework.utils.JSONUtil;
import com.zyplayer.doc.db.framework.utils.SqlLogUtil;
import com.zyplayer.doc.db.service.DatabaseServiceFactory;
import com.zyplayer.doc.db.service.DbBaseService;
import org.apache.commons.lang.StringUtils;
@@ -30,10 +33,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.*;
/**
* sql执行器
@@ -71,6 +71,8 @@ public class DbSqlExecutorController {
String useDbSql = dbBaseService.getUseDbSql(dbName);
// 保留历史记录
dbHistoryService.saveHistory(sql.trim(), sourceId);
// 参数处理
Map<String, Object> paramMap = JSON.parseObject(params);
List<String> resultList = new LinkedList<>();
// 支持;分割的多个sql执行
String[] sqlArr = sql.split(";");
@@ -79,23 +81,25 @@ public class DbSqlExecutorController {
continue;
}
sqlItem = sqlItem.trim();
ExecuteResult executeResult;
ExecuteParam executeParam = new ExecuteParam();
try {
ExecuteType executeType = (manageAuth || update) ? ExecuteType.ALL : ExecuteType.SELECT;
ExecuteParam executeParam = new ExecuteParam();
executeParam = SqlParseUtil.getSingleExecuteParam(sqlItem, paramMap);
executeParam.setDatasourceId(sourceId);
executeParam.setExecuteId(executeId);
executeParam.setExecuteType(executeType);
executeParam.setSql(sqlItem);
executeParam.setPrefixSql(useDbSql);
executeParam.setMaxRows(1000);
ExecuteResult executeResult = sqlExecutor.execute(executeParam);
String resultJsonStr = JSON.toJSONString(executeResult, JSONUtil.serializeConfig, SerializerFeature.WriteMapNullValue);
resultList.add(resultJsonStr);
executeResult = sqlExecutor.execute(executeParam);
} catch (Exception e) {
logger.error("执行出错", e);
ExecuteResult executeResult = ExecuteResult.error(e.getMessage(), sqlItem);
resultList.add(JSON.toJSONString(executeResult));
executeResult = ExecuteResult.error(e.getMessage(), sqlItem);
}
// 执行的sql处理
String executeSqlLog = SqlLogUtil.parseLogSql(executeParam.getSql(), executeParam.getParameterMappings(), executeParam.getParamList());
executeResult.setSql(executeSqlLog);
resultList.add(JSON.toJSONString(executeResult, JSONUtil.serializeConfig, SerializerFeature.WriteMapNullValue));
}
return DocDbResponseJson.ok(resultList);
}

View File

@@ -0,0 +1,17 @@
package com.zyplayer.doc.db.framework.db.parser;
import cn.hutool.core.date.DateTime;
public interface FillParamParser {
/**
* 执行时间处理
*
* @param dateTime 时间
* @param paramOne 第一个参数
* @param paramThree 第三个参数
* @return 时间格式化
*/
String parser(DateTime dateTime, String paramOne, String paramTwo, String paramThree);
}

View File

@@ -0,0 +1,128 @@
package com.zyplayer.doc.db.framework.db.parser;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* 参数填充帮助类
*
* @author 暮光:城中城
* @since 2019-10-10
*/
public class FillParamUtil {
// 填充时参数map里传入的当前时间的key
public static final String PARAM_NOW = "_now";
public static String fillSqlParam(String sql, Map<String, Object> paramMap) {
return FillParamUtil.fillSqlParam(sql, paramMap, (dateTime, paramOne, format, paramThree) -> {
int addInt = NumberUtils.toInt(paramThree);
if (Objects.equals("now", paramOne)) {
dateTime.offset(DateField.DAY_OF_MONTH, addInt);
format = (format == null) ? "yyyy-MM-dd HH:mm:ss" : format;
} else if (Objects.equals("dt", paramOne)) {
dateTime.offset(DateField.DAY_OF_MONTH, addInt);
format = (format == null) ? "yyyyMMdd" : format;
} else if (Objects.equals("now_d", paramOne)) {
dateTime.offset(DateField.DAY_OF_MONTH, addInt);
format = (format == null) ? "yyyyMMdd" : format;
} else if (Objects.equals("now_m", paramOne)) {
dateTime.offset(DateField.MONTH, addInt);
format = (format == null) ? "yyyyMM" : format;
}
return format;
});
}
/**
* 解析${column}预处理的参数,支持内置参数和动态参数
* 内置参数格式:
* ${now}
* ${now, yyyy-MM-dd 00:00:00}
* ${now, yyyy-MM-dd 00:00:00, 1}
* 内置参数说明:
* 参数1、now当前时间 now_d当前天 now_m当前月
* 参数2、加减天数负数为减。参数1为 now/now_d 时,按天加减,为 now_m 时按月加减
* 参数3、日期格式yyyy-MM-dd HH:mm:ss
*
* @param sql
* @param paramMap
* @return
* @author 暮光:城中城
* @since 2019-10-10
*/
public static String fillSqlParam(String sql, Map<String, Object> paramMap, FillParamParser paramParser) {
// 组装参数
GenericTokenParser parser = new GenericTokenParser("${", "}", content -> {
Object o = paramMap.get(content);
if (o == null) {
Object nowDate = paramMap.get(FillParamUtil.PARAM_NOW);
String[] keyArr = content.split(",");
if (keyArr.length == 1) {
o = getFillParam(paramParser, nowDate, keyArr[0].trim(), null, null);
} else if (keyArr.length == 2) {
o = getFillParam(paramParser, nowDate, keyArr[0].trim(), keyArr[1].trim(), null);
} else if (keyArr.length == 3) {
o = getFillParam(paramParser, nowDate, keyArr[0].trim(), keyArr[1].trim(), keyArr[2].trim());
}
}
return (o == null) ? null : String.valueOf(o);
});
return parser.parse(sql);
}
/**
* 内置参数填充
* ${now, 'yyyy-MM-dd 00:00:00', 1}
*
* @param name
* @param format
* @param add
* @return
* @author 暮光:城中城
* @since 2019-10-10
*/
private static String getFillParam(FillParamParser parser, Object nowDate, String name, String format, String add) {
DateTime dateTime;
// 格式化加不加单引号都支持
if (format != null && format.startsWith("'") && format.endsWith("'")) {
format = format.substring(1, format.length() - 1);
}
// 使用系统时间还是传入的时间作为初始值
if (nowDate instanceof Date) {
dateTime = DateTime.of((Date) nowDate);
} else {
dateTime = DateTime.now();
}
format = (format == null) ? null : format.trim();
if (parser != null) {
format = parser.parser(dateTime, name, format, add);
}
if (StringUtils.isNotBlank(format)) {
return dateTime.toString(format);
}
return null;
// 内置参数格式:
// ${now}
// ${now, yyyy-MM-dd 00:00:00}
// ${now, yyyy-MM-dd 00:00:00, 1}
// 内置参数说明:
// 参数1、now当前时间 now_d当前天 now_m当前月 dt当前年月日默认yyyyMMdd格式
// 参数2、加减天数负数为减。参数1为 now/now_d 时,按天加减,为 now_m 时按月加减
// 参数3、日期格式yyyy-MM-dd HH:mm:ss
}
public static void main(String[] args) {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("day", 4);
String s = fillSqlParam("${now, yyyy-MM-dd 00:00:00, 2}\n dasda", paramMap);
System.out.println(s);
}
}

View File

@@ -0,0 +1,89 @@
/**
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zyplayer.doc.db.framework.db.parser;
import org.apache.ibatis.parsing.TokenHandler;
/**
* 参数预处理类
* @author Clinton Begin
* @author 暮光:城中城
* @since 2019-10-10
*/
public class GenericTokenParser {
private final String openToken;
private final String closeToken;
private final TokenHandler handler;
public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
this.openToken = openToken;
this.closeToken = closeToken;
this.handler = handler;
}
public String parse(String text) {
if (text == null || text.isEmpty()) {
return "";
}
int start = text.indexOf(openToken, 0);
if (start == -1) {
return text;
}
char[] src = text.toCharArray();
int offset = 0;
final StringBuilder builder = new StringBuilder();
StringBuilder expression = null;
while (start > -1) {
if (start > 0 && src[start - 1] == '\\') {
builder.append(src, offset, start - offset - 1).append(openToken);
offset = start + openToken.length();
} else {
if (expression == null) {
expression = new StringBuilder();
} else {
expression.setLength(0);
}
builder.append(src, offset, start - offset);
offset = start + openToken.length();
int end = text.indexOf(closeToken, offset);
while (end > -1) {
if (end > offset && src[end - 1] == '\\') {
expression.append(src, offset, end - offset - 1).append(closeToken);
offset = end + closeToken.length();
end = text.indexOf(closeToken, offset);
} else {
expression.append(src, offset, end - offset);
offset = end + closeToken.length();
break;
}
}
if (end == -1) {
builder.append(src, start, src.length - start);
offset = src.length;
} else {
builder.append(handler.handleToken(expression.toString()));
offset = end + closeToken.length();
}
}
start = text.indexOf(openToken, offset);
}
if (offset < src.length) {
builder.append(src, offset, src.length - offset);
}
return builder.toString();
}
}

View File

@@ -1,6 +1,7 @@
package com.zyplayer.doc.db.framework.db.transfer;
import com.zyplayer.doc.db.framework.db.mapper.base.ExecuteParam;
import com.zyplayer.doc.db.framework.db.parser.FillParamUtil;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
@@ -152,8 +153,8 @@ public class SqlParseUtil {
* @return
*/
public static ExecuteParam getSingleExecuteParam(String sql, Map<String, Object> paramMap) {
sql = FillParamUtil.fillSqlParam(sql, paramMap);
ExecuteParam executeParam = new ExecuteParam();
SqlSourceBuilder sqlSourceBuilder = new SqlSourceBuilder(new Configuration());
StaticSqlSource parse = (StaticSqlSource) sqlSourceBuilder.parse(sql, Object.class, paramMap);
BoundSql boundSql = parse.getBoundSql(new Object());

View File

@@ -0,0 +1,71 @@
package com.zyplayer.doc.db.framework.utils;
import org.apache.ibatis.mapping.ParameterMapping;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* SQL日志工具
* @author 暮光:城中城
* @since 2019-08-19
*/
public class SqlLogUtil {
private static final Logger logger = LoggerFactory.getLogger(SqlLogUtil.class);
private static String getParameterValue(Object obj) {
String value;
if (obj instanceof String) {
value = "'" + obj.toString() + "'";
} else if (obj instanceof Number) {
value = obj.toString();
} else if (obj instanceof Date) {
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format(obj) + "'";
} else {
value = (obj != null) ? obj.toString() : "'null'";
}
return value;
}
public static String parseLogSql(String sql, List<ParameterMapping> parameterMappings, List<Object> paramList) {
StringBuilder sqlSb = new StringBuilder(sql.replaceAll("[\\s]+", " "));
int fromIndex = 0;
if (parameterMappings.size() > 0) {
for (int i = 0; i < parameterMappings.size(); i++) {
Object obj = paramList.get(i);
fromIndex = replacePlaceholder(sqlSb, fromIndex, getParameterValue(obj));
}
}
// 最多返回300的长度
// String logSql = sqlSb.toString();
// if (sqlSb.length() > 300) {
// logSql = sqlSb.substring(0, 300) + "...";
// }
// logger.info("sql ==> {}", logSql);
return sqlSb.toString();
}
/**
* 替换?占位符
*
* @param sql
* @param fromIndex
* @param replaceStr
* @return
* @author 暮光:城中城
* @since 2018年10月27日
*/
private static int replacePlaceholder(StringBuilder sql, int fromIndex, String replaceStr) {
int index = sql.indexOf("?", fromIndex);
if (index >= 0) {
sql.replace(index, index + 1, replaceStr);
}
return index + replaceStr.length();
}
}