Commit 02897568 authored by liang.tang's avatar liang.tang
Browse files

magic-api

parents
Pipeline #222 failed with stages
in 0 seconds
package org.ssssssss.magicapi.modules.db.inteceptor;
import org.ssssssss.magicapi.modules.db.model.SqlMode;
import org.ssssssss.magicapi.modules.db.table.NamedTable;
/**
* 单表模块拦截器
*
* @since 1.5.3
*/
public interface NamedTableInterceptor {
/**
* 执行之前
*/
void preHandle(SqlMode sqlMode, NamedTable namedTable);
}
package org.ssssssss.magicapi.modules.db.inteceptor;
import org.ssssssss.magicapi.core.context.RequestEntity;
import org.ssssssss.magicapi.modules.db.BoundSql;
/**
* SQL 拦截器
*
* @author mxd
*/
public interface SQLInterceptor {
/**
* 1.1.1 新增
*
* @since 1.1.1
* @param boundSql SQL信息
* @param requestEntity 请求信息
*/
default void preHandle(BoundSql boundSql, RequestEntity requestEntity) {
}
/**
* @since 1.7.2
* @param boundSql SQL信息
* @param result 执行结果
* @param requestEntity 请求信息
*/
default Object postHandle(BoundSql boundSql, Object result, RequestEntity requestEntity){
return result;
}
/**
* @since 2.1.0
* @param boundSql SQL信息
* @param throwable 异常信息
* @param requestEntity 请求信息
*/
default void handleException(BoundSql boundSql, Throwable throwable, RequestEntity requestEntity){
}
}
package org.ssssssss.magicapi.modules.db.model;
/**
* 分页对象
*
* @author mxd
*/
public class Page {
private long limit;
private long offset;
public Page() {
}
public Page(long limit, long offset) {
this.limit = limit;
this.offset = offset;
}
public long getLimit() {
return limit;
}
public void setLimit(long limit) {
this.limit = limit;
}
public long getOffset() {
return offset;
}
public void setOffset(long offset) {
this.offset = offset;
}
}
package org.ssssssss.magicapi.modules.db.model;
import java.util.List;
/**
* 分页执行结果
*
* @author mxd
*/
public class PageResult<T> {
/**
* 总条数
*/
private long total;
/**
* 数据项
*/
private List<T> list;
public PageResult(long total, List<T> list) {
this.total = total;
this.list = list;
}
public PageResult() {
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public List<T> getList() {
return list;
}
public void setList(List<T> list) {
this.list = list;
}
}
package org.ssssssss.magicapi.modules.db.model;
/**
* 单表API操作
*/
public enum SqlMode {
/**
* 执行插入动作
*/
INSERT,
/**
* 执行修改动作
*/
UPDATE,
/**
* 执行删除动作
*/
DELETE,
/**
* 执行查询操作
*/
SELECT,
/**
* 执行查询单个操作
*/
SELECT_ONE,
/**
* 执行分页查询动作
*/
PAGE,
/**
* 执行count查询操作
*/
COUNT
}
package org.ssssssss.magicapi.modules.db.mybatis;
import org.apache.commons.lang3.StringUtils;
import org.ssssssss.magicapi.utils.ScriptManager;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* 对应XML中 <foreach>
*
* @author jmxd
* @version : 2020-05-18
*/
public class ForeachSqlNode extends SqlNode {
/**
* 数据集合,支持Collection、数组
*/
private String collection;
/**
* item 变量名
*/
private String item;
/**
* 拼接起始SQL
*/
private String open;
/**
* 拼接结束SQL
*/
private String close;
/**
* 分隔符
*/
private String separator;
/**
* 序号
*/
private String index;
public void setCollection(String collection) {
this.collection = collection;
}
public void setItem(String item) {
this.item = item;
}
public void setOpen(String open) {
this.open = open;
}
public void setClose(String close) {
this.close = close;
}
public void setSeparator(String separator) {
this.separator = separator;
}
public void setIndex(String index) {
this.index = index;
}
@Override
public String getSql(Map<String, Object> paramMap, List<Object> parameters) {
// 提取集合
Object value = ScriptManager.executeExpression(this.collection, paramMap);
// 如果集合为空,则过滤该节点
if (value == null) {
return "";
}
// 如果集合是Collection对象或其子类,则转成数组
if (value instanceof Collection) {
value = ((Collection) value).toArray();
}
// 判断不是数组,则过滤子节点并返回
if (!value.getClass().isArray()) {
return "";
}
// 开始拼接SQL,
StringBuilder sqlBuilder = new StringBuilder(StringUtils.defaultString(this.open));
boolean hasIndex = index != null && index.length() > 0;
// 获取数组长度
int len = Array.getLength(value);
for (int i = 0; i < len; i++) {
// 存入item对象
paramMap.put(this.item, Array.get(value, i));
if (hasIndex) {
paramMap.put(this.index, i);
}
// 拼接子节点
sqlBuilder.append(executeChildren(paramMap, parameters));
// 拼接分隔符
if (i + 1 < len) {
sqlBuilder.append(StringUtils.defaultString(this.separator));
}
}
// 拼接结束SQL
sqlBuilder.append(StringUtils.defaultString(this.close));
return sqlBuilder.toString();
}
}
package org.ssssssss.magicapi.modules.db.mybatis;
import org.ssssssss.magicapi.utils.ScriptManager;
import org.ssssssss.script.parsing.ast.literal.BooleanLiteral;
import java.util.List;
import java.util.Map;
/**
* 对应XML中 <if>、<elseif>
*
* @author jmxd
* @version : 2020-05-18
*/
public class IfSqlNode extends SqlNode {
/**
* 判断表达式
*/
private final String test;
private final SqlNode nextNode;
public IfSqlNode(String test, SqlNode nextNode) {
this.test = test;
this.nextNode = nextNode;
}
@Override
public String getSql(Map<String, Object> paramMap, List<Object> parameters) {
// 执行表达式
Object value = ScriptManager.executeExpression(test, paramMap);
// 判断表达式返回结果是否是true,如果不是则过滤子节点
if (BooleanLiteral.isTrue(value)) {
return executeChildren(paramMap, parameters);
}
if (nextNode != null) {
return nextNode.getSql(paramMap, parameters);
}
return "";
}
}
package org.ssssssss.magicapi.modules.db.mybatis;
import org.ssssssss.magicapi.core.exception.MagicAPIException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.ByteArrayInputStream;
import java.util.regex.Pattern;
public class MybatisParser {
private static final Pattern ESCAPE_LT_PATTERN = Pattern.compile("<([\\d'\"\\s=>#$?(])");
private static final String ESCAPE_LT_REPLACEMENT = "&lt;$1";
public static SqlNode parse(String xml) {
try {
xml = "<magic-api>" + escapeXml(xml) + "</magic-api>";
DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = documentBuilder.parse(new ByteArrayInputStream(xml.getBytes()));
SqlNode sqlNode = new TextSqlNode("");
parseNodeList(sqlNode, new NodeStream(document.getDocumentElement().getChildNodes()));
return sqlNode;
} catch (Exception e) {
throw new MagicAPIException("SQL解析错误", e);
}
}
private static String escapeXml(String xml) {
return ESCAPE_LT_PATTERN.matcher(xml).replaceAll(ESCAPE_LT_REPLACEMENT);
}
private static void parseNodeList(SqlNode sqlNode, NodeStream stream) {
while (stream.hasMore()) {
SqlNode childNode;
if (stream.match(Node.TEXT_NODE)) {
childNode = new TextSqlNode(stream.consume().getNodeValue().trim());
} else {
if (stream.match("foreach")) {
childNode = parseForeachSqlNode(stream);
} else if (stream.match("if")) {
childNode = parseIfSqlNode(stream);
} else if (stream.match("trim")) {
childNode = parseTrimSqlNode(stream);
} else if (stream.match("set")) {
childNode = parseSetSqlNode(stream);
} else if (stream.match("where")) {
childNode = parseWhereSqlNode(stream);
} else {
throw new UnsupportedOperationException("Unsupported tags :" + stream.consume().getNodeName());
}
}
sqlNode.addChildNode(childNode);
}
}
private static IfSqlNode parseIfSqlNode(NodeStream stream) {
Node ifNode = stream.consume();
String test = getNodeAttributeValue(ifNode, "test");
SqlNode nextNode = null;
if (stream.match("else")) {
nextNode = new TextSqlNode("");
parseNodeList(nextNode, new NodeStream(stream.consume().getChildNodes()));
} else if (stream.match("elseif")) {
nextNode = parseIfSqlNode(stream);
}
return processChildren(new IfSqlNode(test, nextNode), ifNode);
}
private static <T extends SqlNode> T processChildren(T sqlNode, Node node) {
if (node.hasChildNodes()) {
parseNodeList(sqlNode, new NodeStream(node.getChildNodes()));
}
return sqlNode;
}
/**
* 解析foreach节点
*/
private static ForeachSqlNode parseForeachSqlNode(NodeStream stream) {
Node node = stream.consume();
ForeachSqlNode foreachSqlNode = new ForeachSqlNode();
foreachSqlNode.setCollection(getNodeAttributeValue(node, "collection"));
foreachSqlNode.setSeparator(getNodeAttributeValue(node, "separator"));
foreachSqlNode.setClose(getNodeAttributeValue(node, "close"));
foreachSqlNode.setOpen(getNodeAttributeValue(node, "open"));
foreachSqlNode.setItem(getNodeAttributeValue(node, "item"));
foreachSqlNode.setIndex(getNodeAttributeValue(node, "index"));
return processChildren(foreachSqlNode, node);
}
/**
* 解析trim节点
*/
private static TrimSqlNode parseTrimSqlNode(NodeStream stream) {
Node node = stream.consume();
TrimSqlNode trimSqlNode = new TrimSqlNode();
trimSqlNode.setPrefix(getNodeAttributeValue(node, "prefix"));
trimSqlNode.setPrefixOverrides(getNodeAttributeValue(node, "prefixOverrides"));
trimSqlNode.setSuffix(getNodeAttributeValue(node, "suffix"));
trimSqlNode.setSuffixOverrides(getNodeAttributeValue(node, "suffixOverrides"));
return processChildren(trimSqlNode, node);
}
/**
* 解析set节点
*/
private static SetSqlNode parseSetSqlNode(NodeStream stream) {
return processChildren(new SetSqlNode(), stream.consume());
}
/**
* 解析where节点
*/
private static WhereSqlNode parseWhereSqlNode(NodeStream stream) {
return processChildren(new WhereSqlNode(), stream.consume());
}
private static String getNodeAttributeValue(Node node, String attributeKey) {
Node item = node.getAttributes().getNamedItem(attributeKey);
return item != null ? item.getNodeValue() : null;
}
}
package org.ssssssss.magicapi.modules.db.mybatis;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.util.ArrayList;
import java.util.List;
public class NodeStream {
private final List<Node> nodes;
private int index = 0;
private final int len;
public NodeStream(NodeList nodeList) {
this.nodes = filterCommentAndBlankNodes(nodeList);
this.len = this.nodes.size();
}
private static List<Node> filterCommentAndBlankNodes(NodeList nodeList) {
List<Node> nodes = new ArrayList<>();
for (int i = 0, len = nodeList.getLength(); i < len; i++) {
Node node = nodeList.item(i);
short nodeType = node.getNodeType();
if (nodeType != Node.COMMENT_NODE && (nodeType != Node.TEXT_NODE || node.getNodeValue().trim().length() > 0)) {
nodes.add(node);
}
}
return nodes;
}
public boolean match(String nodeName) {
return hasMore() && nodeName.equalsIgnoreCase(this.nodes.get(this.index).getNodeName());
}
public boolean match(short nodeType) {
return hasMore() && nodeType == this.nodes.get(this.index).getNodeType();
}
public Node consume() {
return this.nodes.get(this.index++);
}
public boolean hasMore() {
return this.index < this.len;
}
}
package org.ssssssss.magicapi.modules.db.mybatis;
/**
* 对应XML中 <set>
*
* @author zhangxu
* @version : 2020-12-05
*/
public class SetSqlNode extends TrimSqlNode {
public SetSqlNode() {
this.prefix = "SET";
this.suffixOverrides = ",";
}
}
package org.ssssssss.magicapi.modules.db.mybatis;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* sql节点
*
* @author jmxd
* @version : 2020-05-18
*/
public abstract class SqlNode {
/**
* 子节点
*/
List<SqlNode> nodes = new ArrayList<>();
/**
* SQL参数
*/
List<Object> parameters;
/**
* 追加子节点
*/
public void addChildNode(SqlNode node) {
this.nodes.add(node);
}
/**
* 获取该节点的SQL
*/
public String getSql(Map<String, Object> paramMap) {
this.parameters = new ArrayList<>();
return getSql(paramMap, parameters);
}
/**
* 获取该节点的SQL
*/
public abstract String getSql(Map<String, Object> paramMap, List<Object> parameters);
/**
* 获取子节点SQL
*/
public String executeChildren(Map<String, Object> paramMap, List<Object> parameters) {
StringBuilder sqlBuilder = new StringBuilder();
for (SqlNode node : nodes) {
sqlBuilder.append(StringUtils.defaultString(node.getSql(paramMap, parameters)));
sqlBuilder.append(" ");
}
return sqlBuilder.toString();
}
public List<Object> getParameters() {
return parameters;
}
}
package org.ssssssss.magicapi.modules.db.mybatis;
import org.ssssssss.magicapi.utils.ScriptManager;
import org.ssssssss.script.functions.StreamExtension;
import org.ssssssss.script.parsing.GenericTokenParser;
import org.ssssssss.script.parsing.ast.literal.BooleanLiteral;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
/**
* 普通SQL节点
*
* @author jmxd
* @version : 2020-05-18
*/
public class TextSqlNode extends SqlNode {
private static final GenericTokenParser CONCAT_TOKEN_PARSER = new GenericTokenParser("${", "}", false);
private static final GenericTokenParser REPLACE_TOKEN_PARSER = new GenericTokenParser("#{", "}", true);
private static final GenericTokenParser IF_TOKEN_PARSER = new GenericTokenParser("?{", "}", true);
private static final GenericTokenParser IF_PARAM_TOKEN_PARSER = new GenericTokenParser("?{", ",", true);
/**
* SQL
*/
private final String text;
public TextSqlNode(String text) {
this.text = text;
}
public static String parseSql(String sql, Map<String, Object> varMap, List<Object> parameters) {
// 处理?{}参数
sql = IF_TOKEN_PARSER.parse(sql.trim(), text -> {
AtomicBoolean ifTrue = new AtomicBoolean(false);
String val = IF_PARAM_TOKEN_PARSER.parse("?{" + text, param -> {
ifTrue.set(BooleanLiteral.isTrue(ScriptManager.executeExpression(param, varMap)));
return null;
});
return ifTrue.get() ? val : "";
});
// 处理${}参数
sql = CONCAT_TOKEN_PARSER.parse(sql, text -> String.valueOf(ScriptManager.executeExpression(text, varMap)));
// 处理#{}参数
sql = REPLACE_TOKEN_PARSER.parse(sql, text -> {
Object value = ScriptManager.executeExpression(text, varMap);
if (value == null) {
parameters.add(null);
return "?";
}
try {
//对集合自动展开
List<Object> objects = StreamExtension.arrayLikeToList(value);
parameters.addAll(objects);
return IntStream.range(0, objects.size()).mapToObj(t -> "?").collect(Collectors.joining(","));
} catch (Exception e) {
parameters.add(value);
return "?";
}
});
return sql;
}
@Override
public String getSql(Map<String, Object> paramMap, List<Object> parameters) {
return parseSql(text, paramMap, parameters) + executeChildren(paramMap, parameters).trim();
}
}
package org.ssssssss.magicapi.modules.db.mybatis;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
import java.util.Map;
/**
* 对应XML中 <trim>,注意prefixOverrides和suffixOverrides大小写敏感
*
* @author zhangxu
* @version : 2020-12-05
*/
public class TrimSqlNode extends SqlNode {
/**
* 前缀 prefix
*/
protected String prefix;
/**
* 后缀 suffix
*/
protected String suffix;
/**
* 前缀 prefixOverrides
*/
protected String prefixOverrides;
/**
* 后缀 suffixOverrides
*/
protected String suffixOverrides;
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
public void setSuffixOverrides(String suffixOverrides) {
this.suffixOverrides = suffixOverrides == null ? null : suffixOverrides.toUpperCase();
}
public void setPrefixOverrides(String prefixOverrides) {
this.prefixOverrides = prefixOverrides == null ? null : prefixOverrides.toUpperCase();
}
@Override
public String getSql(Map<String, Object> paramMap, List<Object> parameters) {
StringBuilder sqlBuffer = new StringBuilder();
String childrenSql = executeChildren(paramMap, parameters).trim();
// 如果子节点不为null,则转成数组
if (StringUtils.isNotEmpty(childrenSql)) {
String upperSql = childrenSql.toUpperCase();
// 开始拼接SQL,
sqlBuffer.append(StringUtils.defaultString(this.prefix)).append(" ");
//去掉prefixOverrides
if (StringUtils.isNotEmpty(this.prefixOverrides)) {
String[] overrideArray = this.prefixOverrides.split("\\|");
for (String override : overrideArray) {
if (upperSql.startsWith(override)) {
childrenSql = childrenSql.substring(upperSql.indexOf(override) + override.length()).trim();
upperSql = childrenSql.toUpperCase();
break;
}
}
}
//去掉suffixOverrides
if (StringUtils.isNotBlank(this.suffixOverrides)) {
String[] overrideArray = this.suffixOverrides.split("\\|");
for (String override : overrideArray) {
if (upperSql.endsWith(override)) {
childrenSql = childrenSql.substring(0, upperSql.lastIndexOf(override));
break;
}
}
}
sqlBuffer.append(childrenSql);
// 拼接结束SQL
sqlBuffer.append(" ").append(StringUtils.defaultString(this.suffix));
}
return sqlBuffer.toString();
}
}
package org.ssssssss.magicapi.modules.db.mybatis;
import java.util.List;
import java.util.Map;
/**
* 对应XML中 <where>
*
* @author zhangxu
* @version : 2020-12-05
*/
public class WhereSqlNode extends TrimSqlNode {
public WhereSqlNode() {
this.prefix = "WHERE";
this.prefixOverrides = "AND | OR | AND\n| OR\n| AND\r| OR\r| AND\t| OR\t";
}
@Override
public String getSql(Map<String, Object> paramMap, List<Object> parameters) {
String sql = super.getSql(paramMap, parameters);
if (this.prefix.equals(sql.trim())) {
return "";
}
return sql;
}
}
package org.ssssssss.magicapi.modules.db.provider;
/**
* 驼峰命名转换
*
* @author mxd
*/
public class CamelColumnMapperProvider implements ColumnMapperProvider {
@Override
public String name() {
return "camel";
}
@Override
public String mapping(String columnName) {
if (columnName == null) {
return null;
}
columnName = columnName.toLowerCase();
boolean upperCase = false;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < columnName.length(); i++) {
char ch = columnName.charAt(i);
if (ch == '_') {
upperCase = true;
} else if (upperCase) {
sb.append(Character.toUpperCase(ch));
upperCase = false;
} else {
sb.append(ch);
}
}
return sb.toString();
}
@Override
public String unmapping(String name) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < name.length(); i++) {
char ch = name.charAt(i);
if (Character.isUpperCase(ch)) {
sb.append("_");
}
sb.append(Character.toLowerCase(ch));
}
return sb.toString();
}
}
package org.ssssssss.magicapi.modules.db.provider;
import org.springframework.jdbc.core.ColumnMapRowMapper;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Function;
/**
* 列名转换接口
*
* @author mxd
*/
public interface ColumnMapperProvider {
/**
* 获取转换器名称
*
* @return 转换器名称
*/
String name();
/**
* 转换方法
*
* @param columnName 列名
* @return 转换后的方法
*/
String mapping(String columnName);
/**
* 反向转换
*
* @param name 转换后的值
* @return 列名
*/
default String unmapping(String name) {
return name;
}
/**
* 获取 RowMapColumnMapper
*
* @return RowMapColumnMapper
*/
default Function<String, String> getRowMapColumnMapper() {
return this::unmapping;
}
/**
* 获取 ColumnMapRowMapper
*
* @return ColumnMapRowMapper
*/
default ColumnMapRowMapper getColumnMapRowMapper() {
return new ColumnMapRowMapper() {
@Override
protected Map<String, Object> createColumnMap(int columnCount) {
return new LinkedHashMap<>(columnCount);
}
@Override
protected String getColumnKey(String columnName) {
return mapping(columnName);
}
};
}
}
package org.ssssssss.magicapi.modules.db.provider;
/**
* 默认命名(保持原样)
*
* @author mxd
*/
public class DefaultColumnMapperProvider implements ColumnMapperProvider {
@Override
public String name() {
return "default";
}
@Override
public String mapping(String columnName) {
return columnName;
}
}
package org.ssssssss.magicapi.modules.db.provider;
import org.apache.commons.lang3.math.NumberUtils;
import org.ssssssss.magicapi.modules.db.model.Page;
import org.ssssssss.script.runtime.RuntimeContext;
import java.util.Objects;
/**
* 分页对象默认提取接口
*/
public class DefaultPageProvider implements PageProvider {
/**
* page参数名
*/
private final String pageName;
/**
* pageSize参数名
*/
private final String pageSize;
/**
* 默认分页大小
*/
private final long defaultPageSize;
/**
* 默认页数
*/
private final long defaultPage;
/**
* 最大页数
*/
private final long maxPageSize;
public DefaultPageProvider(String pageName, String pageSize, long defaultPage, long defaultPageSize, long maxPageSize) {
this.pageName = pageName;
this.pageSize = pageSize;
this.defaultPageSize = defaultPageSize;
this.defaultPage = defaultPage;
this.maxPageSize = maxPageSize;
}
@Override
public Page getPage(RuntimeContext context) {
// 改为从脚本中获取
long page = NumberUtils.toLong(Objects.toString(context.eval(this.pageName), null), this.defaultPage);
long pageSize = NumberUtils.toLong(Objects.toString(context.eval(this.pageSize), null), this.defaultPageSize);
if(maxPageSize > 0){
pageSize = Math.min(pageSize, this.maxPageSize);
}
// 计算limit以及offset
return new Page(pageSize, (page - 1) * pageSize);
}
}
package org.ssssssss.magicapi.modules.db.provider;
/**
* 全小写命名
*
* @author mxd
*/
public class LowerColumnMapperProvider implements ColumnMapperProvider {
@Override
public String name() {
return "lower";
}
@Override
public String mapping(String columnName) {
return columnName == null ? null : columnName.toLowerCase();
}
}
package org.ssssssss.magicapi.modules.db.provider;
import org.ssssssss.magicapi.modules.db.model.Page;
import org.ssssssss.script.runtime.RuntimeContext;
/**
* 分页对象提取接口
*
* @author mxd
*/
public interface PageProvider {
/**
* 从请求中获取分页对象
*
* @param context 脚本上下文
* @return 返回分页对象
*/
public Page getPage(RuntimeContext context);
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment