sql执行增加动态参数功能
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user