Pārlūkot izejas kodu

`db`模块增强

mxd 3 gadi atpakaļ
vecāks
revīzija
c1a24c55ef

+ 64 - 19
magic-api/src/main/java/org/ssssssss/magicapi/modules/BoundSql.java

@@ -1,15 +1,25 @@
 package org.ssssssss.magicapi.modules;
 
 import org.ssssssss.magicapi.context.RequestContext;
+import org.ssssssss.magicapi.exception.MagicAPIException;
 import org.ssssssss.magicapi.interceptor.SQLInterceptor;
+import org.ssssssss.magicapi.modules.mybatis.MybatisParser;
+import org.ssssssss.magicapi.modules.mybatis.SqlNode;
 import org.ssssssss.script.MagicScriptContext;
 import org.ssssssss.script.functions.StreamExtension;
 import org.ssssssss.script.parsing.GenericTokenParser;
 import org.ssssssss.script.parsing.ast.literal.BooleanLiteral;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
+import org.ssssssss.script.runtime.Variables;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.*;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Supplier;
 import java.util.regex.Pattern;
@@ -33,7 +43,9 @@ public class BoundSql {
 
 	private static final Pattern REPLACE_MULTI_WHITE_LINE = Pattern.compile("(\r?\n(\\s*\r?\n)+)");
 
-	private String sql;
+	private static final List<String> MYBATIS_TAGS = Arrays.asList("</where>","</if>", "</trim>", "</set>" ,"</foreach>");
+
+	private String sqlOrXml;
 
 	private List<Object> parameters = new ArrayList<>();
 
@@ -41,28 +53,61 @@ public class BoundSql {
 
 	private SQLModule sqlModule;
 
-	public BoundSql(String sql, List<Object> parameters, SQLModule sqlModule) {
-		this.sql = sql;
+	private Map<String, Object> bindParameters;
+
+	public BoundSql(String sqlOrXml, List<Object> parameters, SQLModule sqlModule) {
+		this.sqlOrXml = sqlOrXml;
 		this.parameters = parameters;
 		this.sqlModule = sqlModule;
 	}
 
-	private BoundSql(String sql) {
+	public BoundSql(String sqlOrXml, Map<String,Object> parameters, SQLModule sqlModule) {
+		this.sqlOrXml = sqlOrXml;
+		this.bindParameters = parameters;
+		this.sqlModule = sqlModule;
+		this.init();
+	}
+
+	private BoundSql(String sqlOrXml) {
+		this.sqlOrXml = sqlOrXml;
+		this.init();
+	}
+
+	private void init(){
+		Map<String, Object> varMap = new HashMap<>();
 		MagicScriptContext context = MagicScriptContext.get();
+		if(this.bindParameters != null){
+			varMap.putAll(this.bindParameters);
+		}else{
+			Variables variables = Variables.get();
+			if(variables != null){
+				varMap.putAll(variables.getVariables(context));
+			}
+		}
+		if(MYBATIS_TAGS.stream().anyMatch(it -> this.sqlOrXml.contains(it))){
+			SqlNode sqlNode = MybatisParser.parse(this.sqlOrXml);
+			this.sqlOrXml = sqlNode.getSql(varMap);
+			this.parameters = sqlNode.getParameters();
+		}else{
+			normal(context, varMap);
+		}
+	}
+
+	private void normal(MagicScriptContext context, Map<String, Object> varMap){
 		// 处理?{}参数
-		this.sql = IF_TOKEN_PARSER.parse(sql.trim(), text -> {
+		this.sqlOrXml = IF_TOKEN_PARSER.parse(this.sqlOrXml.trim(), text -> {
 			AtomicBoolean ifTrue = new AtomicBoolean(false);
 			String val = IF_PARAM_TOKEN_PARSER.parse("?{" + text, param -> {
-				ifTrue.set(BooleanLiteral.isTrue(context.eval(param)));
+				ifTrue.set(BooleanLiteral.isTrue(context.eval(param, varMap)));
 				return null;
 			});
 			return ifTrue.get() ? val : "";
 		});
 		// 处理${}参数
-		this.sql = CONCAT_TOKEN_PARSER.parse(this.sql, text -> String.valueOf(context.eval(text)));
+		this.sqlOrXml = CONCAT_TOKEN_PARSER.parse(this.sqlOrXml, text -> String.valueOf(context.eval(text, varMap)));
 		// 处理#{}参数
-		this.sql = REPLACE_TOKEN_PARSER.parse(this.sql, text -> {
-			Object value = context.eval(text);
+		this.sqlOrXml = REPLACE_TOKEN_PARSER.parse(this.sqlOrXml, text -> {
+			Object value = context.eval(text, varMap);
 			if (value == null) {
 				parameters.add(null);
 				return "?";
@@ -77,7 +122,7 @@ public class BoundSql {
 				return "?";
 			}
 		});
-		this.sql = this.sql == null ? null : REPLACE_MULTI_WHITE_LINE.matcher(this.sql.trim()).replaceAll("\r\n");
+		this.sqlOrXml = this.sqlOrXml == null ? null : REPLACE_MULTI_WHITE_LINE.matcher(this.sqlOrXml.trim()).replaceAll("\r\n");
 	}
 
 	BoundSql(String sql, SQLModule sqlModule) {
@@ -93,11 +138,11 @@ public class BoundSql {
 		return sqlModule;
 	}
 
-	BoundSql copy(String newSql) {
+	BoundSql copy(String newSqlOrXml) {
 		BoundSql boundSql = new BoundSql();
 		boundSql.setParameters(new ArrayList<>(this.parameters));
-		boundSql.setSql(this.sql);
-		boundSql.sql = newSql;
+		boundSql.bindParameters = this.bindParameters;
+		boundSql.sqlOrXml = newSqlOrXml;
 		boundSql.excludeColumns = this.excludeColumns;
 		boundSql.sqlModule = this.sqlModule;
 		return boundSql;
@@ -122,14 +167,14 @@ public class BoundSql {
 	 * 获取要执行的SQL
 	 */
 	public String getSql() {
-		return sql;
+		return sqlOrXml;
 	}
 
 	/**
 	 * 设置要执行的SQL
 	 */
 	public void setSql(String sql) {
-		this.sql = sql;
+		this.sqlOrXml = sql;
 	}
 
 	/**

+ 101 - 21
magic-api/src/main/java/org/ssssssss/magicapi/modules/SQLModule.java

@@ -311,8 +311,16 @@ public class SQLModule extends HashMap<String, SQLModule> implements MagicModule
 	 * 查询List
 	 */
 	@Comment("查询SQL,返回List类型结果")
-	public List<Map<String, Object>> select(@Comment("`SQL`语句") String sql) {
-		return select(new BoundSql(sql, this));
+	public List<Map<String, Object>> select(@Comment("`SQL`语句或`xml`") String sqlOrXml) {
+		return select(sqlOrXml, null);
+	}
+
+	/**
+	 * 查询List,并传入变量信息
+	 */
+	@Comment("查询SQL,并传入变量信息,返回List类型结果")
+	public List<Map<String, Object>> select(@Comment("`SQL`语句或`xml`") String sqlOrXml, @Comment("变量信息") Map<String, Object> params) {
+		return select(new BoundSql(sqlOrXml, params, this));
 	}
 
 	@UnableCall
@@ -339,8 +347,16 @@ public class SQLModule extends HashMap<String, SQLModule> implements MagicModule
 	 * 执行update
 	 */
 	@Comment("执行update操作,返回受影响行数")
-	public int update(@Comment("`SQL`语句") String sql) {
-		return update(new BoundSql(sql, this));
+	public int update(@Comment("`SQL`语句或`xml`") String sqlOrXml) {
+		return update(sqlOrXml, null);
+	}
+
+	/**
+	 * 执行update,并传入变量信息
+	 */
+	@Comment("执行update操作,并传入变量信息,返回受影响行数")
+	public int update(@Comment("`SQL`语句或`xml`") String sqlOrXml, @Comment("变量信息") Map<String, Object> params) {
+		return update(new BoundSql(sqlOrXml, params, this));
 	}
 
 	@UnableCall
@@ -358,9 +374,17 @@ public class SQLModule extends HashMap<String, SQLModule> implements MagicModule
 	 * 插入并返回主键
 	 */
 	@Comment("执行insert操作,返回插入主键")
-	public long insert(@Comment("`SQL`语句") String sql) {
+	public long insert(@Comment("`SQL`语句或`xml`") String sqlOrXml) {
+		return insert(sqlOrXml, (Map<String, Object>) null);
+	}
+
+	/**
+	 * 插入并返回主键,并传入变量信息
+	 */
+	@Comment("执行insert操作,并传入变量信息,返回插入主键")
+	public long insert(@Comment("`SQL`语句或`xml`") String sqlOrXml, @Comment("变量信息")Map<String, Object> params) {
 		MagicKeyHolder magicKeyHolder = new MagicKeyHolder();
-		insert(new BoundSql(sql, this), magicKeyHolder);
+		insert(new BoundSql(sqlOrXml, params,this), magicKeyHolder);
 		return magicKeyHolder.getLongKey();
 	}
 
@@ -368,8 +392,16 @@ public class SQLModule extends HashMap<String, SQLModule> implements MagicModule
 	 * 插入并返回主键
 	 */
 	@Comment("执行insert操作,返回插入主键")
-	public Object insert(@Comment("`SQL`语句") String sql, @Comment("主键列") String primary) {
-		return insert(new BoundSql(sql, this), primary);
+	public Object insert(@Comment("`SQL`语句或`xml`") String sqlOrXml, @Comment("主键列") String primary) {
+		return insert(sqlOrXml, primary, null);
+	}
+
+	/**
+	 * 插入并返回主键
+	 */
+	@Comment("执行insert操作,并传入主键和变量信息,返回插入主键")
+	public Object insert(@Comment("`SQL`语句或`xml`") String sqlOrXml, @Comment("主键列") String primary, @Comment("变量信息")Map<String, Object> params) {
+		return insert(new BoundSql(sqlOrXml, params, this), primary);
 	}
 
 	void insert(BoundSql boundSql, MagicKeyHolder keyHolder) {
@@ -414,16 +446,32 @@ public class SQLModule extends HashMap<String, SQLModule> implements MagicModule
 	 * 分页查询
 	 */
 	@Comment("执行分页查询,分页条件自动获取")
-	public Object page(@Comment("`SQL`语句") String sql) {
-		return page(new BoundSql(sql, this));
+	public Object page(@Comment("`SQL`语句或`xml`") String sqlOrXml, @Comment("变量信息")Map<String, Object> params) {
+		return page(new BoundSql(sqlOrXml, params, this));
+	}
+
+	/**
+	 * 分页查询,并传入变量信息
+	 */
+	@Comment("执行分页查询,并传入变量信息,分页条件自动获取")
+	public Object page(@Comment("`SQL`语句或`xml`") String sqlOrXml) {
+		return page(sqlOrXml, (Map<String, Object>) null);
 	}
 
 	/**
 	 * 分页查询(手动传入limit和offset参数)
 	 */
 	@Comment("执行分页查询,分页条件手动传入")
-	public Object page(@Comment("`SQL`语句") String sql, @Comment("限制条数") long limit, @Comment("跳过条数") long offset) {
-		BoundSql boundSql = new BoundSql(sql, this);
+	public Object page(@Comment("`SQL`语句或`xml`") String sqlOrXml, @Comment("限制条数") long limit, @Comment("跳过条数") long offset) {
+		return page(sqlOrXml, limit, offset, null);
+	}
+
+	/**
+	 * 分页查询(手动传入limit和offset参数)
+	 */
+	@Comment("执行分页查询,并传入变量信息,分页条件手动传入")
+	public Object page(@Comment("`SQL`语句或`xml`") String sqlOrXml, @Comment("限制条数") long limit, @Comment("跳过条数") long offset, @Comment("变量信息")Map<String, Object> params) {
+		BoundSql boundSql = new BoundSql(sqlOrXml, params, this);
 		return page(boundSql, new Page(limit, offset));
 	}
 
@@ -442,10 +490,18 @@ public class SQLModule extends HashMap<String, SQLModule> implements MagicModule
 	 * 分页查询(手动传入分页SQL语句)
 	 */
 	@Comment("执行分页查询,分页`SQL`语句手动传入")
-	public Object page(String countSql, String sql){
-		int count = selectInt(new BoundSql(countSql, this));
+	public Object page(@Comment("count语句") String countSqlOrXml, @Comment("查询语句") String sqlOrXml){
+		return page(countSqlOrXml, sqlOrXml, null);
+	}
+
+	/**
+	 * 分页查询(手动传入分页SQL语句)
+	 */
+	@Comment("执行分页查询,并传入变量信息,分页`SQL`countSqlOrXml")
+	public Object page(@Comment("count语句")String countSqlOrXml, @Comment("查询语句")String sqlOrXml, @Comment("变量信息") Map<String, Object> params){
+		int count = selectInt(new BoundSql(countSqlOrXml, params, this));
 		Page page = pageProvider.getPage(MagicScriptContext.get());
-		BoundSql boundSql = new BoundSql(sql, this);
+		BoundSql boundSql = new BoundSql(sqlOrXml, params, this);
 		return page(count, boundSql, page, null);
 	}
 
@@ -475,8 +531,16 @@ public class SQLModule extends HashMap<String, SQLModule> implements MagicModule
 	 * 查询int值
 	 */
 	@Comment("查询int值,适合单行单列int的结果")
-	public Integer selectInt(@Comment("`SQL`语句") String sql) {
-		return selectInt(new BoundSql(sql, this));
+	public Integer selectInt(@Comment("`SQL`语句或`xml`") String sqlOrXml) {
+		return selectInt(sqlOrXml, null);
+	}
+
+	/**
+	 * 查询int值
+	 */
+	@Comment("查询int值,并传入变量信息,适合单行单列int的结果")
+	public Integer selectInt(@Comment("`SQL`语句或`xml`") String sqlOrXml, @Comment("变量信息") Map<String, Object> params) {
+		return selectInt(new BoundSql(sqlOrXml, params, this));
 	}
 
 	@UnableCall
@@ -489,8 +553,16 @@ public class SQLModule extends HashMap<String, SQLModule> implements MagicModule
 	 * 查询Map
 	 */
 	@Comment("查询单条结果,查不到返回null")
-	public Map<String, Object> selectOne(@Comment("`SQL`语句") String sql) {
-		return selectOne(new BoundSql(sql, this));
+	public Map<String, Object> selectOne(@Comment("`SQL`语句或`xml`") String sqlOrXml) {
+		return selectOne(sqlOrXml, null);
+	}
+
+	/**
+	 * 查询Map,并传入变量信息
+	 */
+	@Comment("查询单条结果,并传入变量信息,查不到返回null")
+	public Map<String, Object> selectOne(@Comment("`SQL`语句或`xml`") String sqlOrXml, @Comment("变量信息") Map<String, Object> params) {
+		return selectOne(new BoundSql(sqlOrXml, params, this));
 	}
 
 	@UnableCall
@@ -511,9 +583,17 @@ public class SQLModule extends HashMap<String, SQLModule> implements MagicModule
 	 * 查询单行单列的值
 	 */
 	@Comment("查询单行单列的值")
-	public Object selectValue(@Comment("`SQL`语句") String sql) {
+	public Object selectValue(@Comment("`SQL`语句或`xml`") String sqlOrXml) {
+		return selectValue(sqlOrXml, null);
+	}
+
+	/**
+	 * 查询单行单列的值,并传入变量信息
+	 */
+	@Comment("查询单行单列的值,并传入变量信息")
+	public Object selectValue(@Comment("`SQL`语句或`xml`") String sqlOrXml, @Comment("变量信息")Map<String, Object> params) {
 		assertDatasourceNotNull();
-		BoundSql boundSql = new BoundSql(sql, this);
+		BoundSql boundSql = new BoundSql(sqlOrXml, params, this);
 		return boundSql.getCacheValue(this.sqlInterceptors, () -> {
 			Dialect dialect = dataSourceNode.getDialect(dialectAdapter);
 			BoundSql pageBoundSql = buildPageBoundSql(dialect, boundSql, 0, 1);

+ 81 - 0
magic-api/src/main/java/org/ssssssss/magicapi/modules/mybatis/ForeachSqlNode.java

@@ -0,0 +1,81 @@
+package org.ssssssss.magicapi.modules.mybatis;
+
+import org.apache.commons.lang3.StringUtils;
+import org.ssssssss.magicapi.script.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;
+
+    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;
+    }
+
+    @Override
+    public String getSql(Map<String, Object> paramMap, List<Object> parameters) {
+        // 提取集合
+        Object value = ScriptManager.executeExpression(this.collection, paramMap);
+        // 如果集合为空,则过滤该节点
+        if (value == null) {
+            return "";
+        }
+        // 开始拼接SQL,
+        String sql = StringUtils.defaultString(this.open);
+        // 如果集合是Collection对象或其子类,则转成数组
+        if (value instanceof Collection) {
+            value = ((Collection) value).toArray();
+        }
+        // 判断不是数组,则过滤子节点并返回
+        if (!value.getClass().isArray()) {
+            return "";
+        }
+        // 获取数组长度
+        int len = Array.getLength(value);
+        for (int i = 0; i < len; i++) {
+            // 存入item对象
+            paramMap.put(this.item, Array.get(value, i));
+            // 拼接子节点
+            sql += executeChildren(paramMap, parameters);
+            // 拼接分隔符
+            if (i + 1 < len) {
+                sql += StringUtils.defaultString(this.separator);
+            }
+        }
+        // 拼接结束SQL
+        return sql + StringUtils.defaultString(this.close);
+    }
+}

+ 33 - 0
magic-api/src/main/java/org/ssssssss/magicapi/modules/mybatis/IfSqlNode.java

@@ -0,0 +1,33 @@
+package org.ssssssss.magicapi.modules.mybatis;
+
+import org.ssssssss.magicapi.script.ScriptManager;
+import org.ssssssss.script.parsing.ast.literal.BooleanLiteral;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 对应XML中 <if>
+ * @author jmxd
+ * @version : 2020-05-18
+ */
+public class IfSqlNode extends SqlNode {
+    /** 判断表达式 */
+    private String test;
+
+    public IfSqlNode(String test) {
+        this.test = test;
+    }
+
+    @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);
+        }
+        return "";
+    }
+}

+ 101 - 0
magic-api/src/main/java/org/ssssssss/magicapi/modules/mybatis/MybatisParser.java

@@ -0,0 +1,101 @@
+package org.ssssssss.magicapi.modules.mybatis;
+
+import org.ssssssss.magicapi.exception.MagicAPIException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.ByteArrayInputStream;
+
+public class MybatisParser {
+
+	public static SqlNode parse(String xml) {
+		try {
+			xml = "<mybatis>" + xml + "</mybatis>";
+			DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+			Document document = documentBuilder.parse(new ByteArrayInputStream(xml.getBytes()));
+			SqlNode sqlNode = new TextSqlNode("");
+			parseNodeList(sqlNode, document.getDocumentElement().getChildNodes());
+			return sqlNode;
+		} catch (Exception e) {
+			throw new MagicAPIException("SQL解析错误", e);
+		}
+	}
+
+	private static void parseNodeList(SqlNode sqlNode, NodeList nodeList) {
+		for (int i = 0, len = nodeList.getLength(); i < len; i++) {
+			Node node = nodeList.item(i);
+			if (node.getNodeType() == Node.TEXT_NODE) {
+				sqlNode.addChildNode(new TextSqlNode(node.getNodeValue().trim()));
+			} else if (node.getNodeType() != Node.COMMENT_NODE) {
+				String nodeName = node.getNodeName();
+				SqlNode childNode;
+				if ("foreach".equalsIgnoreCase(nodeName)) {
+					childNode = parseForeachSqlNode(node);
+				} else if ("if".equalsIgnoreCase(nodeName)) {
+					childNode = new IfSqlNode(getNodeAttributeValue(node, "test"));
+				} else if ("trim".equalsIgnoreCase(nodeName)) {
+					childNode = parseTrimSqlNode(node);
+				} else if ("set".equalsIgnoreCase(nodeName)) {
+					childNode = parseSetSqlNode();
+				} else if ("where".equalsIgnoreCase(nodeName)) {
+					childNode = parseWhereSqlNode();
+				} else {
+					throw new UnsupportedOperationException("Unsupported tags :" + nodeName);
+				}
+				sqlNode.addChildNode(childNode);
+				if (node.hasChildNodes()) {
+					parseNodeList(childNode, node.getChildNodes());
+				}
+			}
+		}
+	}
+
+	/**
+	 * 解析foreach节点
+	 */
+	private static ForeachSqlNode parseForeachSqlNode(Node node) {
+		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"));
+		return foreachSqlNode;
+	}
+
+	/**
+	 * 解析trim节点
+	 */
+	private static TrimSqlNode parseTrimSqlNode(Node node) {
+		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 trimSqlNode;
+	}
+
+	/**
+	 * 解析set节点
+	 */
+	private static SetSqlNode parseSetSqlNode() {
+		SetSqlNode setSqlNode = new SetSqlNode();
+		return setSqlNode;
+	}
+
+	/**
+	 * 解析where节点
+	 */
+	private static WhereSqlNode parseWhereSqlNode() {
+		WhereSqlNode whereSqlNode = new WhereSqlNode();
+		return whereSqlNode;
+	}
+
+	private static String getNodeAttributeValue(Node node, String attributeKey) {
+		Node item = node.getAttributes().getNamedItem(attributeKey);
+		return item != null ? item.getNodeValue() : null;
+	}
+}

+ 13 - 0
magic-api/src/main/java/org/ssssssss/magicapi/modules/mybatis/SetSqlNode.java

@@ -0,0 +1,13 @@
+package org.ssssssss.magicapi.modules.mybatis;
+
+/**
+ * 对应XML中 <set>
+ * @author zhangxu
+ * @version : 2020-12-05
+ */
+public class SetSqlNode extends TrimSqlNode {
+    public SetSqlNode() {
+        this.prefix = "SET";
+        this.suffixOverrides = ",";
+    }
+}

+ 85 - 0
magic-api/src/main/java/org/ssssssss/magicapi/modules/mybatis/SqlNode.java

@@ -0,0 +1,85 @@
+package org.ssssssss.magicapi.modules.mybatis;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * sql节点
+ *
+ * @author jmxd
+ * @version : 2020-05-18
+ */
+public abstract class SqlNode {
+	/**
+	 * 提取#{}的正则
+	 */
+	final Pattern expressionRegx = Pattern.compile("#\\{(.*?)\\}");
+	/**
+	 * 提取${}的正则
+	 */
+	final Pattern replaceRegx = Pattern.compile("\\$\\{(.*?)\\}");
+	/**
+	 * 子节点
+	 */
+	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) {
+		String sql = "";
+		for (SqlNode node : nodes) {
+			sql += StringUtils.defaultString(node.getSql(paramMap, parameters)) + " ";
+		}
+		return sql;
+	}
+
+	public List<Object> getParameters() {
+		return parameters;
+	}
+
+	/**
+	 * 根据正则表达式提取参数
+	 *
+	 * @param pattern 正则表达式
+	 * @param sql     SQL
+	 */
+	public List<String> extractParameter(Pattern pattern, String sql) {
+		Matcher matcher = pattern.matcher(sql);
+		List<String> results = new ArrayList<>();
+		while (matcher.find()) {
+			results.add(matcher.group(1));
+		}
+		return results;
+	}
+
+}

+ 43 - 0
magic-api/src/main/java/org/ssssssss/magicapi/modules/mybatis/TextSqlNode.java

@@ -0,0 +1,43 @@
+package org.ssssssss.magicapi.modules.mybatis;
+
+import org.apache.commons.lang3.StringUtils;
+import org.ssssssss.magicapi.script.ScriptManager;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * 普通SQL节点
+ * @author jmxd
+ * @version : 2020-05-18
+ */
+public class TextSqlNode extends SqlNode {
+    /** SQL */
+    private String text;
+
+    public TextSqlNode(String text) {
+        this.text = text;
+    }
+
+    @Override
+    public String getSql(Map<String, Object> paramMap, List<Object> parameters) {
+        String sql = text;
+        if (StringUtils.isNotBlank(text)) {
+            // 提取#{}表达式
+            List<String> expressions = extractParameter(expressionRegx, text);
+            for (String expression : expressions) {
+                // 执行表达式
+                Object val = ScriptManager.executeExpression(expression, paramMap);
+                parameters.add(val);
+                sql = sql.replaceFirst(expressionRegx.pattern(), "?");
+            }
+            expressions = extractParameter(replaceRegx, text);
+            for (String expression : expressions) {
+                Object val = ScriptManager.executeExpression(expression, paramMap);
+                sql = sql.replaceFirst(replaceRegx.pattern(), Objects.toString(val, ""));
+            }
+        }
+        return sql + executeChildren(paramMap, parameters).trim();
+    }
+}

+ 75 - 0
magic-api/src/main/java/org/ssssssss/magicapi/modules/mybatis/TrimSqlNode.java

@@ -0,0 +1,75 @@
+package org.ssssssss.magicapi.modules.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.toUpperCase();
+    }
+
+    public void setPrefixOverrides(String prefixOverrides) {
+        this.prefixOverrides = prefixOverrides.toUpperCase();
+    }
+
+    @Override
+    public String getSql(Map<String, Object> paramMap, List<Object> parameters) {
+        StringBuilder sqlBuffer = new StringBuilder();
+        String childrenSql = executeChildren(paramMap, parameters);
+        // 如果子节点不为null,则转成数组
+        if (StringUtils.isNotEmpty(childrenSql)) {
+            String upperSql = childrenSql.toUpperCase().trim();
+            // 开始拼接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());
+                        upperSql = childrenSql.toUpperCase().trim();
+                        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();
+    }
+}

+ 13 - 0
magic-api/src/main/java/org/ssssssss/magicapi/modules/mybatis/WhereSqlNode.java

@@ -0,0 +1,13 @@
+package org.ssssssss.magicapi.modules.mybatis;
+
+/**
+ * 对应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";
+    }
+}

+ 9 - 0
magic-api/src/main/java/org/ssssssss/magicapi/script/ScriptManager.java

@@ -4,6 +4,8 @@ import org.ssssssss.script.MagicScript;
 import org.ssssssss.script.MagicScriptContext;
 import org.ssssssss.script.MagicScriptDebugContext;
 
+import java.util.Map;
+
 /**
  * 脚本管理
  *
@@ -27,4 +29,11 @@ public class ScriptManager {
 	public static Object executeExpression(String script, MagicScriptContext context) {
 		return executeScript("/* generated by execute expression */ return " + script, context);
 	}
+
+	/**
+	 * 执行脚本
+	 */
+	public static Object executeExpression(String script, Map<String, Object> paramMap) {
+		return executeExpression(script, new MagicScriptContext(paramMap));
+	}
 }