自己動手實(shí)現(xiàn)mybatis動態(tài)sql的方法
發(fā)現(xiàn)要堅持寫博客真的是一件很困難的事情,各種原因都會導(dǎo)致顧不上博客。本來打算寫自己動手實(shí)現(xiàn)orm,看看時間,還是先實(shí)現(xiàn)一個動態(tài)sql,下次有時間再補(bǔ)上orm完整的實(shí)現(xiàn)吧。
用過mybatis的人,估計對動態(tài)sql都不陌生,如果沒有用過,就當(dāng)看看熱鬧吧。我第一次接觸mysql是在大四的時候,當(dāng)時就覺得動態(tài)sql這東西很牛,很靈活,一直想搞明白怎么實(shí)現(xiàn)的,盡管當(dāng)時已經(jīng)能夠?qū)慽oc,mvc和簡單的orm框架(仿mybaits但是沒有動態(tài)sql部分),但是仍然找不到mybatis核心的動態(tài)sql到底在哪實(shí)現(xiàn)的,怎么實(shí)現(xiàn)的,可能是那些代碼太繞根本沒法看懂,直到目前,我都沒有勇氣去看mybatis的動態(tài)sql部分,大概是天生對算法有莫名其妙的敬畏吧。
幾年前因?yàn)橄胱鲆粋€配置平臺,想用解析型語言替代java的實(shí)現(xiàn),可以讓配置人員在頁面上方便的編寫少量代碼實(shí)現(xiàn)復(fù)雜的業(yè)務(wù)邏輯(包括數(shù)據(jù)庫操作)。當(dāng)時java已經(jīng)有js解析引擎,但是大多數(shù)人都說效率太低,不知道我發(fā)什么瘋就想到自己實(shí)現(xiàn)一個解析語言。不過實(shí)現(xiàn)自己的語言也是我一直的夢想,解析語言相對編譯型語言入手簡單,于是我就果斷動手了,寫完才知道,其實(shí)自己的實(shí)現(xiàn)估計還沒有當(dāng)時的js引擎效率高,那時的我真的是很年輕很簡單。今天談到的動態(tài)sql實(shí)現(xiàn)其實(shí)就是受到那時候解析語言的啟發(fā)。
廢話不多說直接開始聊動態(tài)sql,請看下面例子,先聲明這里的例子并不是一個正確的sql的寫法,只是想寫一個盡量復(fù)雜的嵌套結(jié)構(gòu),如果把這種復(fù)雜的情況實(shí)現(xiàn)了,那么簡單一點(diǎn)的就更加不在話下了。
delete from pl_pagewidget
<if test="widgetcodes != null">
where pagewidgetcode in
<foreach collection="widgetcodes" item="item" index="index" open="(" separator="," close=")">
<if test="index == 0">
#{item}
</if>
<foreach collection="bs" item="b" index="index1" open="(" separator="," close=")">
#
</foreach>
</foreach>
</if>
<if test="a != null">
and a = #{a}
</if>
要實(shí)現(xiàn)解析出上面例子的sql,首先一個難點(diǎn)類似是test屬性里的條件怎么判斷真假,不過這個難點(diǎn)在struts2中學(xué)到的ognl表達(dá)式面前就比較小兒科了。不知道有么有朋友遇到過一個比較奇葩的現(xiàn)象,就是有時候明明在mybatis動態(tài)sql中寫如下表達(dá)式,但是當(dāng)n=0的時候居然是滿足條件的也就是test里的值是false,0居然不能滿足這個表達(dá)式的條件,這里就是ognl庫的原因了。沒辦法它就是這么玩的,當(dāng)成特殊情況記住就可以了
test="n != null and n !=''"
ognl表達(dá)式使用很方便如下
import java.util.HashMap;
import java.util.Map;
import ognl.Ognl;
public class OgnlTest {
//輸出結(jié)果:false
public static void main(String[] args) throws Exception {
String con1 = "n != null and n != ''";
Map<String,Object> root = new HashMap<>();
root.put("n", 0);
System.out.println(Ognl.getValue(con1,root));
}
}
要實(shí)現(xiàn)解析上面例子的sql,第二個難點(diǎn)就是雖然這個sql披上一層xml的皮就是一個標(biāo)準(zhǔn)的sql,如下
<sql>
delete from pl_pagewidget
<if test="widgetcodes != null">
where pagewidgetcode in
<foreach collection="widgetcodes" item="item" index="index" open="(" separator="," close=")">
<if test="index == 0">
#{item}
</if>
<foreach collection="bs" item="b" index="index1" open="(" separator="," close=")">
#
</foreach>
</foreach>
</if>
<if test="a != null">
and a = #{a}
</if>
</sql>
但是要解析上面的xml和我們平時不一樣,這個xml是標(biāo)簽和文本混合的,正常我們開發(fā)中應(yīng)該很少會用到解析這種xml。不過我們常用的解析xml的工具dom4j其實(shí)可以很好的解析這種sql,只不過很少可能用到。Element類的content()方法就可以返回一個Node的集合,再通過遍歷這個集合,判斷每個Node的類型就可以了。解決了這兩個重點(diǎn),只需要加上一點(diǎn)技巧就可以解析這種動態(tài)sql了。
我用到的技巧是根據(jù)java語法格式得到的啟發(fā)。比如java中有局部變量和全局變量,不考慮引用傳遞這種情況,如果全局變量int i = 1;方法里面?zhèn)魅脒@個全局變量,然后在方法里面修改,在方法里面看到的是改變后的值,但是在方法外面看到的仍然是1。這個現(xiàn)象其實(shí)學(xué)過java應(yīng)該都知道。還有就是當(dāng)方法調(diào)用的時候,方法里面可以看到全局變量,也可以看到局部變量,方法調(diào)用結(jié)束后局部變量會被清空釋放(看垃圾搜集器高興)。介紹了這些直接上代碼了
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.Text;
import org.dom4j.io.SAXReader;
import com.rd.sql.Attrs;
import com.rd.sql.BaseNode;
import com.rd.sql.NodeFactory;
public class SqlParser {
private Map<String,Object> currParams = new HashMap<String,Object>();
/**
delete from pl_pagewidget
<if test="widgetcodes != null">
where pagewidgetcode in
<foreach collection="widgetcodes" item="item" index="index" open="(" separator="," close=")">
<if test="index == 0">
#{item}
</if>
<foreach collection="bs" item="b" index="index1" open="(" separator="," close=")">
#
</foreach>
</foreach>
</if>
<if test="a != null">
and a = #{a}
</if>
*/
public static void main(String[] args) throws Exception {
Map<String, Object> map = new HashMap<String, Object>();
map.put("widgetcodes", Arrays.asList("1", "2"));
map.put("bs", Arrays.asList("3", "4"));
map.put("a", 1);
SqlParser parser = new SqlParser();
System.out
.println(parser.parser("delete from pl_pagewidget\n"
+ "\t<if test=\"widgetcodes != null\">\n"
+ "\t\twhere pagewidgetcode in\n"
+ "\t\t<foreach collection=\"widgetcodes\" item=\"item\" index=\"index\" open=\"(\" separator=\",\" close=\")\">\n"
+ "\t\t <if test=\"index == 0\">\n"
+ "\t\t #{item}\n"
+ "\t\t </if>\n"
+ "\t\t <foreach collection=\"bs\" item=\"b\" index=\"index1\" open=\"(\" separator=\",\" close=\")\">\n"
+ "\t\t\t#\n" + "\t\t </foreach>\n"
+ "\t\t</foreach>\n" + "\t</if>\n"
+ "\t<if test=\"a != null\">\n"
+ "\t\tand a = #{a}\n" + "\t</if>\n", map));
System.out.println(parser.getParams());
}
public String parser(String xml, Map<String, Object> params)
throws Exception {
// xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"+xml;
//給輸入的動態(tài)sql套一層xml標(biāo)簽
xml = "<sql>"+xml+"</sql>";
SAXReader reader = new SAXReader(false);
Document document = reader.read(new StringReader(xml));
Element element = document.getRootElement();
Map<String, Object> currParams = new HashMap<String, Object>();
StringBuilder sb = new StringBuilder();
//開始解析
parserElement(element, currParams, params, sb);
return sb.toString();
}
/**
* 使用遞歸解析動態(tài)sql
* @param ele1 待解析的xml標(biāo)簽
* @param currParams
* @param globalParams
* @param sb
* @throws Exception
*/
private void parserElement(Element ele1, Map<String, Object> currParams,
Map<String, Object> globalParams, StringBuilder sb)
throws Exception {
// 解析一個節(jié)點(diǎn),比如解析到了一個if節(jié)點(diǎn),假如test判斷為true這里就返回true
TempVal val = parserOneElement(currParams, globalParams, ele1, sb);
//得到解析的這個節(jié)點(diǎn)的抽象節(jié)點(diǎn)對象
BaseNode node = val.getNode();
/**
* 實(shí)際上這句之上的語句只是解析了xml的標(biāo)簽,并沒有解析標(biāo)簽里的內(nèi)容,這里
* 表示要解析內(nèi)容之前,如果有前置操作做一點(diǎn)前置操作
*/
node.pre(currParams, globalParams, ele1, sb);
//判斷是否還需要解析節(jié)點(diǎn)里的內(nèi)容,例如if節(jié)點(diǎn)test結(jié)果為true
boolean flag = val.isContinue();
// 得到該節(jié)點(diǎn)下的所有子節(jié)點(diǎn)的集合,包含普通文本
List<Node> nodes = ele1.content();
if (flag && !nodes.isEmpty()) {
/**
* 這里表示要進(jìn)一步解析節(jié)點(diǎn)里的內(nèi)容了,可以把節(jié)點(diǎn)類比成一個方法的外殼
* 里面的內(nèi)容類比成方法里的具體語句,開始解析節(jié)點(diǎn)的內(nèi)容之前
* 先創(chuàng)建本節(jié)點(diǎn)下的局部參數(shù)的容器,最方便當(dāng)然是map
*/
Map<String, Object> params = new HashMap<String, Object>();
/**
* 把外面?zhèn)鬟M(jìn)來的局部參數(shù),直接放入容器,由于本例中參數(shù)都是常用數(shù)據(jù)類型
* 不會存在引用類型所以,這里相當(dāng)于是一個copy,為了不影響外面?zhèn)魅氲膶ο?
* 可以類比方法調(diào)用傳入?yún)?shù)的情況
*/
params.putAll(currParams);
//循環(huán)所有子節(jié)點(diǎn)
for (int i = 0; i < nodes.size();) {
Node n = nodes.get(i);
//如果節(jié)點(diǎn)是普通文本
if (n instanceof Text) {
String text = ((Text) n).getStringValue();
if (StringUtils.isNotEmpty(text.trim())) {
//處理一下文本,如處理#{xx},直接替換${yy}為真實(shí)傳入的值
sb.append(handText(text, params,globalParams));
}
i++;
} else if (n instanceof Element) {
Element e1 = (Element) n;
// 遞歸解析xml子元素
parserElement(e1, params, globalParams, sb);
// 如果循環(huán)標(biāo)志不為true則解析下一個標(biāo)簽
// 這里表示需要重復(fù)解析這個循環(huán)標(biāo)簽,則i不變,反之繼續(xù)處理下一個元素
boolean while_flag = MapUtils.getBoolean(params,
Attrs.WHILE_FLAG, false);
if (!while_flag
|| !NodeFactory.isWhile(n.getName())
|| e1.attributeValue(Attrs.INDEX) == null
|| !e1.attributeValue(Attrs.INDEX).equals(
params.get(Attrs.WHILE_INDEX))) {
i++;
}
}
}
//節(jié)點(diǎn)處理之后做一些啥事
node.after(currParams, globalParams, ele1, sb);
// 回收當(dāng)前作用域參數(shù)
params.clear();
params = null;
}
}
/**
* 處理文本替換掉#{item}這種參數(shù)
* @param str
* @param params
* @return
* @throws Exception
*/
private String handText(String str, Map<String, Object> params,Map<String, Object> globalParams)
throws Exception {
//獲取foreach這種標(biāo)簽中用于記錄循環(huán)的變量
String indexStr = MapUtils.getString(params, Attrs.WHILE_INDEX);
Integer index = null;
if(StringUtils.isNotEmpty(indexStr)) {
index = MapUtils.getInteger(params, indexStr);
}
//匹配#{a}這種參數(shù)
String reg1 = "(#\\{)(\\w+)(\\})";
//匹配${a}這種參數(shù)
String reg2 = "(\\$\\{)(\\w+)(\\})";
Pattern p1 = Pattern.compile(reg1);
Matcher m1 = p1.matcher(str);
Pattern p2 = Pattern.compile(reg2);
Matcher m2 = p2.matcher(str);
String whileList = MapUtils.getString(params, Attrs.WHILE_LIST);
Map<String,Object> allParams = getAllParams(params, globalParams);
while(m1.find()) {
String tmpKey = m1.group(2);
String key = whileList == null?tmpKey:(whileList+"_"+tmpKey);
key = index == null?key:(key+index);
String reKey = "#{"+key+"}";
//如果在foreach類似的循環(huán)里,可能需要將參數(shù)#{xx}替換成#{xx_0},#{xx_1}
str = str.replace(m1.group(0), reKey);
currParams.put(key, allParams.get(tmpKey));
}
while(m2.find()) {
String tmpKey = m2.group(2);
Object value = allParams.get(tmpKey);
if(value != null) {
str = str.replace(m2.group(0), getValue(value));
}
}
return str;
}
private String getValue(Object value) {
String result = "";
if(value instanceof Date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
result = sdf.format((Date)value);
} else {
result = String.valueOf(value);
}
return result;
}
private Map<String, Object> getAllParams(Map<String, Object> currParams,
Map<String, Object> globalParams) {
Map<String,Object> allParams = new HashMap<String,Object>();
allParams.putAll(globalParams);
allParams.putAll(currParams);
return allParams;
}
// 解析一個xml元素
private TempVal parserOneElement(Map<String, Object> currParams,
Map<String, Object> globalParams, Element ele, StringBuilder sb)
throws Exception {
//獲取xml標(biāo)簽名
String eleName = ele.getName();
//解析一個節(jié)點(diǎn)后是否繼續(xù),如遇到if這種節(jié)點(diǎn),就需要判斷test里是否為空
boolean isContinue = false;
//聲明一個抽象節(jié)點(diǎn)
BaseNode node = null;
if (StringUtils.isNotEmpty(eleName)) {
//使用節(jié)點(diǎn)工廠根據(jù)節(jié)點(diǎn)名得到一個節(jié)點(diǎn)對象比如是if節(jié)點(diǎn)還是foreach節(jié)點(diǎn)
node = NodeFactory.create(eleName);
//解析一下這個節(jié)點(diǎn),返回是否還需要解析節(jié)點(diǎn)里的內(nèi)容
isContinue = node.parse(currParams, globalParams, ele, sb);
}
return new TempVal(isContinue, ele, node);
}
public Map<String, Object> getParams() {
return currParams;
}
/**
* 封裝一個xml元素被解析后的結(jié)果
* @author rongdi
*/
final static class TempVal {
private boolean isContinue;
private Element ele;
private BaseNode node;
public TempVal(boolean isContinue, Element ele, BaseNode node) {
this.isContinue = isContinue;
this.ele = ele;
this.node = node;
}
public boolean isContinue() {
return isContinue;
}
public void setContinue(boolean isContinue) {
this.isContinue = isContinue;
}
public Element getEle() {
return ele;
}
public void setEle(Element ele) {
this.ele = ele;
}
public BaseNode getNode() {
return node;
}
public void setNode(BaseNode node) {
this.node = node;
}
}
}
import org.dom4j.Element;
import java.util.HashMap;
import java.util.Map;
/**
* 抽象節(jié)點(diǎn)
* @author rongdi
*/
public abstract class BaseNode {
public abstract boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele,StringBuilder sb) throws Exception;
public void pre(Map<String, Object> currParams,Map<String, Object> globalParams,Element ele,StringBuilder sb) throws Exception {
}
public void after(Map<String, Object> currParams,Map<String, Object> globalParams,Element ele,StringBuilder sb) throws Exception {
}
protected Map<String, Object> getAllParams(Map<String, Object> currParams,
Map<String, Object> globalParams) {
Map<String,Object> allParams = new HashMap<String,Object>();
allParams.putAll(globalParams);
allParams.putAll(currParams);
return allParams;
}
}
import java.util.Map;
import ognl.Ognl;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
/**
* if節(jié)點(diǎn)
* @author rongdi
*/
public class IfNode extends BaseNode{
@Override
public boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele,StringBuilder sb) throws Exception {
//得到if節(jié)點(diǎn)的test屬性
String testStr = ele.attributeValue("test");
boolean test = false;
try {
if(StringUtils.isNotEmpty(testStr)) {
//合并全局變量和局部變量
Map<String, Object> allParams = getAllParams(currParams,globalParams);
//使用ognl判斷true或者false
test = (Boolean) Ognl.getValue(testStr,allParams);
}
} catch (Exception e) {
e.printStackTrace();
throw new Exception("判斷操作參數(shù)"+testStr+"不合法");
}
if(ele.content() != null && ele.content().size()==0) {
test = true;
}
return test;
}
}
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import ognl.Ognl;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.dom4j.Element;
/**
foreach節(jié)點(diǎn)屬性如下
collection 需要遍歷的集合
item 遍歷集合后每個元素存放的變量
index 遍歷集合的索引數(shù)如0,1,2...
separator 遍歷后以指定分隔符拼接
open 遍歷后拼接開始的符號如 (
close 遍歷后拼接結(jié)束的符號如 )
*/
public class ForeachNode extends BaseNode {
@Override
public boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele, StringBuilder sb) throws Exception {
String conditionStr = null;
String collectionStr = ele.attributeValue("collection");
String itemStr = ele.attributeValue("item");
String index = ele.attributeValue("index");
String separatorStr = ele.attributeValue("separator");
String openStr = ele.attributeValue("open");
String closeStr = ele.attributeValue("close");
if(StringUtils.isEmpty(index)) {
index = "index";
}
if(StringUtils.isEmpty(separatorStr)) {
separatorStr = ",";
}
if(StringUtils.isNotEmpty(openStr)) {
currParams.put(Attrs.WHILE_OPEN,openStr);
}
if(StringUtils.isNotEmpty(closeStr)) {
currParams.put(Attrs.WHILE_CLOSE,closeStr);
}
if(StringUtils.isNotEmpty(collectionStr)) {
currParams.put(Attrs.WHILE_LIST,collectionStr);
}
currParams.put(Attrs.WHILE_SEPARATOR,separatorStr);
if(index != null) {
/**
* 如果局部變量中存在當(dāng)前循環(huán)變量的值,就表示已經(jīng)不是第一次進(jìn)入循環(huán)標(biāo)簽了,移除掉開始標(biāo)記
* 并將局部變量值加1
*/
if(currParams.get(index) != null) {
currParams.remove(Attrs.WHILE_START);
currParams.put(index+"_", (Integer)currParams.get(index+"_") + 1);
} else { //第一次進(jìn)入循環(huán)標(biāo)簽內(nèi)
currParams.put(Attrs.WHILE_START,true);
currParams.put(index+"_", 0);
}
currParams.put(index, (Integer)currParams.get(index+"_"));
}
boolean condition = true;
Map<String, Object> allParams = getAllParams(currParams,globalParams);
Object collection = null;
if(StringUtils.isNotEmpty(collectionStr)) {
//得到待循環(huán)的集合
collection = Ognl.getValue(collectionStr,allParams);
//如果集合屬性不為空,但是條件為null則默認(rèn)加上一個邊界條件
if(StringUtils.isEmpty(conditionStr)) {
//這里只是用集合演示一下,也可以再加上數(shù)組,只不過改成.length而已
if(collection instanceof List) {
conditionStr = index+"_<"+collectionStr+".size()";
} else if(collection instanceof Map){
Map map = (Map)collection;
Set set = map.entrySet();
List list = new ArrayList(set);
allParams.put("_list_", list);
conditionStr = index+"_<_list_"+".size()";
}
}
}
currParams.remove(Attrs.WHILE_END);
if(StringUtils.isNotEmpty(conditionStr)) {
//計算條件的值
condition = (Boolean)Ognl.getValue(conditionStr,allParams);
Map<String,Object> tempMap = new HashMap<>();
tempMap.putAll(allParams);
tempMap.put(index+"_",(Integer)currParams.get(index+"_") + 1);
currParams.put(Attrs.WHILE_END,!(Boolean)Ognl.getValue(conditionStr,tempMap));
}
boolean flag = true;
currParams.put(Attrs.WHILE_INDEX, index);
currParams.put(Attrs.WHILE_FLAG, true);
if(condition) {
try {
if(StringUtils.isNotEmpty(itemStr) && StringUtils.isNotEmpty(collectionStr)) {
Object value = null;
int idx = Integer.parseInt(currParams.get(index+"_").toString());
if(collection instanceof List) {
value = ((List)collection).get(idx);
currParams.put(itemStr, value);
} else if(collection instanceof Map){
Map map = (Map)collection;
Set<Map.Entry<String,Object>> set = map.entrySet();
List<Map.Entry<String,Object>> list = new ArrayList(set);
currParams.put(itemStr, list.get(idx).getValue());
currParams.put(index, list.get(idx).getKey());
}
}
} catch (Exception e) {
throw new Exception("從集合或者映射取值"+currParams.get(index)+"錯誤"+e.getMessage());
}
} else {
flag = false;
destroyVars(currParams, index, itemStr);
}
return flag;
}
/**
* 如果是第一次進(jìn)入循環(huán)標(biāo)簽,則拼上open的內(nèi)容
*/
@Override
public void pre(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele, StringBuilder sb) throws Exception {
super.pre(currParams, globalParams, ele, sb);
boolean start = MapUtils.getBoolean(currParams,Attrs.WHILE_START,false);
if(start) {
String open = MapUtils.getString(currParams,Attrs.WHILE_OPEN);
sb.append(open);
}
}
/**
* 如果是最后進(jìn)入循環(huán)標(biāo)簽,則最后拼上close的內(nèi)容
*/
@Override
public void after(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele, StringBuilder sb) throws Exception {
super.after(currParams, globalParams, ele, sb);
boolean end = MapUtils.getBoolean(currParams,Attrs.WHILE_END,false);
String separator = MapUtils.getString(currParams,Attrs.WHILE_SEPARATOR);
if(!end && StringUtils.isNotEmpty(separator)) {
sb.append(separator);
}
if(end) {
String close = MapUtils.getString(currParams,Attrs.WHILE_CLOSE);
if(sb.toString().endsWith(separator)) {
sb.deleteCharAt(sb.length() - 1);
}
sb.append(close);
}
}
//釋放臨時變量
private void destroyVars(Map<String, Object> currParams, String index,String varStr) {
currParams.remove(Attrs.WHILE_INDEX);
currParams.remove(Attrs.WHILE_FLAG);
currParams.remove(Attrs.WHILE_SEPARATOR);
currParams.remove(Attrs.WHILE_START);
currParams.remove(Attrs.WHILE_END);
currParams.remove(Attrs.WHILE_LIST);
}
}
import org.dom4j.Element;
import java.util.Map;
public class SqlNode extends BaseNode{
@Override
public boolean parse(Map<String, Object> currParams, Map<String, Object> globalParams, Element ele,StringBuilder sb) throws Exception {
return true;
}
}
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 節(jié)點(diǎn)工廠
*/
public class NodeFactory {
private static Map<String,BaseNode> nodeMap = new ConcurrentHashMap<String,BaseNode>();
private final static List<String> whileList = Arrays.asList("foreach");
static {
nodeMap.put("if", new IfNode());
nodeMap.put("sql", new SqlNode());
nodeMap.put("foreach", new ForeachNode());
}
public static boolean isWhile(String elementName) {
return whileList.contains(elementName);
}
public static void addNode(String nodeName,BaseNode node) {
nodeMap.put(nodeName, node);
}
public static BaseNode create(String nodeName) {
return nodeMap.get(nodeName);
}
}
/**
* 各種標(biāo)記
* @author rongdi
*/
public class Attrs {
public final static String TRANSACTIONAL = "transactional";
public final static String WHILE_START = "while-start";
public final static String WHILE_END = "while-end";
public final static String WHILE_OPEN = "while-open";
public final static String WHILE_CLOSE = "while-close";
public final static String WHILE_SEPARATOR = "while-separator";
public final static String WHILE_INDEX = "while-index";
public final static String WHILE_FLAG = "while-flag";
public final static String WHILE_LIST = "while-list";
public final static String WHEN_FLAG = "when-flag";
public static final String PROCESS_VAR = "process-var";
public final static String RESULT_FLAG = "result-flag";
public final static String RETURN_FLAG = "return-flag";
public final static String CONSOLE_VAR= "console-var";
public final static String DO = "do";
public final static String INDEX = "index";
public final static String CONDITION = "condition";
public final static String NAME= "name";
public final static String VALUE= "value";
public static final String TYPE = "type";
public static final String FORMAT = "format";
public static final String IF = "if";
public static final String ELSE = "else";
public final static String FILE= "file";
public static final String DATE = "date";
public static final String NOW = "now";
public static final String DECIMAL = "decimal";
public static final String ID = "id";
public static final String PARAMS = "params";
public static final String TARGET = "target";
public static final String SINGLE = "single";
public static final String PAGING = "paging";
public static final String DESC = "desc";
public static final String BREAK = "break";
public static final String CONTINUE = "continue";
public static final String COLLECTION = "collection";
public static final String VAR = "var";
public static final String EXECUTOR = "executor-1";
public static final String ROLLBACK_FLAG = "rollback-flag";
public static final String SERVICE = "service";
public static final String REF = "ref";
public static final String BIZS = "bizs";
public static final String TITLES = "titles";
public static final String COLUMNS = "columns";
public static final String CURRUSER = "currUser";
public static final String CURRPERM = "currPerm";
public static final String TASK_EXECUTOR = "taskExecutor";
public static final String DELIMITER = "delimiter";
public static final String OPERNAME = "operName";
}
currParams.remove(varStr);
currParams.remove(index);
currParams.remove(index+"_");
}
}
附上pom文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rd</groupId>
<artifactId>parser</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>myparser</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>opensymphony</groupId>
<artifactId>ognl</artifactId>
<version>2.6.11</version>
</dependency>
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>
<testResources>
<testResource>
<directory>${project.basedir}/src/test/java</directory>
</testResource>
<testResource>
<directory>${project.basedir}/src/test/resources</directory>
</testResource>
</testResources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
以上這篇自己動手實(shí)現(xiàn)mybatis動態(tài)sql的方法就是小編分享給大家的全部內(nèi)容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
詳解java.lang.reflect.Modifier.isInterface()方法
這篇文章主要介紹了詳解java.lang.reflect.Modifier.isInterface()方法的相關(guān)資料,這里提供實(shí)例幫助大家理解這個方法的使用,需要的朋友可以參考下2017-09-09
獲取系統(tǒng)參數(shù)System.getProperties()與配置文件參數(shù)@Value(“${key}“)
這篇文章主要介紹了獲取系統(tǒng)參數(shù)System.getProperties()與配置文件參數(shù)@Value("${key}"),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05
Java中Optional.of()方法及源碼解析(非常詳細(xì)!)
這篇文章主要給大家介紹了關(guān)于Java中Optional.of()方法及源碼解析的相關(guān)資料,Java中java.util .Optional類的of()方法用于獲得該Optional類中具有指定類型的指定值的一個實(shí)例,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-06-06
mybatis-plus插入一條數(shù)據(jù),獲取插入數(shù)據(jù)自動生成的主鍵問題
這篇文章主要介紹了mybatis-plus插入一條數(shù)據(jù),獲取插入數(shù)據(jù)自動生成的主鍵問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12
ThreadPoolExecutor中的submit()方法詳細(xì)講解
在使用線程池的時候,發(fā)現(xiàn)除了execute()方法可以執(zhí)行任務(wù)外,還發(fā)現(xiàn)有一個方法submit()可以執(zhí)行任務(wù),本文就詳細(xì)的介紹一下ThreadPoolExecutor中的submit()方法,具有一定的參考價值,感興趣的可以了解一下2022-04-04
源碼解析Spring 數(shù)據(jù)庫異常抽理知識點(diǎn)總結(jié)
在本篇文章里小編給大家分享了關(guān)于源碼解析Spring 數(shù)據(jù)庫異常抽理知識點(diǎn)內(nèi)容,對此有需要的朋友們學(xué)習(xí)參考下。2019-05-05
從內(nèi)存方面解釋Java中String與StringBuilder的性能差異
我們通常會發(fā)現(xiàn)使用StringBuffer或StringBuilder創(chuàng)建出來的字符串在拼接時回避String要來得快,尤其是StringBuilder,本文就從內(nèi)存方面解釋Java中String與StringBuilder的性能差異,需要的朋友可以參考下2016-05-05

