自己动手实现mybatis动态sql
发现要坚持写博客真的是一件很困难的事情,各种原因都会导致顾不上博客。本来打算写自己动手实现orm,看看时间,还是先实现一个动态sql,下次有时间再补上orm完整的实现吧。
用过mybatis的人,估计对动态sql都不陌生,如果没有用过,就当看看热闹吧。我第一次接触mysql是在大四的时候,当时就觉得动态sql这东西很牛,很灵活,一直想搞明白怎么实现的,尽管当时已经能够写ioc,mvc和简单的orm框架(仿mybaits但是没有动态sql部分),但是仍然找不到mybatis核心的动态sql到底在哪实现的,怎么实现的,可能是那些代码太绕根本没法看懂,直到目前,我都没有勇气去看mybatis的动态sql部分,大概是天生对算法有莫名其妙的敬畏吧。
几年前因为想做一个配置平台,想用解析型语言替代java的实现,可以让配置人员在页面上方便的编写少量代码实现复杂的业务逻辑(包括数据库操作)。当时java已经有js解析引擎,但是大多数人都说效率太低,不知道我发什么疯就想到自己实现一个解析语言。不过实现自己的语言也是我一直的梦想,解析语言相对编译型语言入手简单,于是我就果断动手了,写完才知道,其实自己的实现估计还没有当时的js引擎效率高,那时的我真的是很年轻很简单。今天谈到的动态sql实现其实就是受到那时候解析语言的启发。
废话不多说直接开始聊动态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=")">
#{b}
</foreach>
</foreach>
</if>
<if test="a != null">
and a = #{a}
</if>
要实现解析出上面例子的sql,首先一个难点类似是test属性里的条件怎么判断真假,不过这个难点在struts2中学到的ognl表达式面前就比较小儿科了。不知道有么有朋友遇到过一个比较奇葩的现象,就是有时候明明在mybatis动态sql中写如下表达式,但是当n=0的时候居然是满足条件的也就是test里的值是false,0居然不能满足这个表达式的条件,这里就是ognl库的原因了。没办法它就是这么玩的,当成特殊情况记住就可以了
test="n != null and n !=''"
ognl表达式使用很方便如下
import java.util.HashMap;
import java.util.Map;
import ognl.Ognl;
public class OgnlTest {
//输出结果: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));
}
}
要实现解析上面例子的sql,第二个难点就是虽然这个sql披上一层xml的皮就是一个标准的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=")">
#{b}
</foreach>
</foreach>
</if>
<if test="a != null">
and a = #{a}
</if>
</sql>
但是要解析上面的xml和我们平时不一样,这个xml是标签和文本混合的,正常我们开发中应该很少会用到解析这种xml。不过我们常用的解析xml的工具dom4j其实可以很好的解析这种sql,只不过很少可能用到。Element类的content()方法就可以返回一个Node的集合,再通过遍历这个集合,判断每个Node的类型就可以了。解决了这两个重点,只需要加上一点技巧就可以解析这种动态sql了。
我用到的技巧是根据java语法格式得到的启发。比如java中有局部变量和全局变量,不考虑引用传递这种情况,如果全局变量int i = 1;方法里面传入这个全局变量,然后在方法里面修改,在方法里面看到的是改变后的值,但是在方法外面看到的仍然是1。这个现象其实学过java应该都知道。还有就是当方法调用的时候,方法里面可以看到全局变量,也可以看到局部变量,方法调用结束后局部变量会被清空释放(看垃圾搜集器高兴)。介绍了这些直接上代码了
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=")">
#{b}
</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#{b}\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;
//给输入的动态sql套一层xml标签
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();
} /**
* 使用递归解析动态sql
* @param ele1 待解析的xml标签
* @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 { // 解析一个节点,比如解析到了一个if节点,假如test判断为true这里就返回true
TempVal val = parserOneElement(currParams, globalParams, ele1, sb);
//得到解析的这个节点的抽象节点对象
BaseNode node = val.getNode();
/**
* 实际上这句之上的语句只是解析了xml的标签,并没有解析标签里的内容,这里
* 表示要解析内容之前,如果有前置操作做一点前置操作
*/
node.pre(currParams, globalParams, ele1, sb);
//判断是否还需要解析节点里的内容,例如if节点test结果为true
boolean flag = val.isContinue();
// 得到该节点下的所有子节点的集合,包含普通文本
List<Node> nodes = ele1.content();
if (flag && !nodes.isEmpty()) {
/**
* 这里表示要进一步解析节点里的内容了,可以把节点类比成一个方法的外壳
* 里面的内容类比成方法里的具体语句,开始解析节点的内容之前
* 先创建本节点下的局部参数的容器,最方便当然是map
*/
Map<String, Object> params = new HashMap<String, Object>();
/**
* 把外面传进来的局部参数,直接放入容器,由于本例中参数都是常用数据类型
* 不会存在引用类型所以,这里相当于是一个copy,为了不影响外面传入的对象
* 可以类比方法调用传入参数的情况
*/
params.putAll(currParams);
//循环所有子节点
for (int i = 0; i < nodes.size();) {
Node n = nodes.get(i);
//如果节点是普通文本
if (n instanceof Text) {
String text = ((Text) n).getStringValue();
if (StringUtils.isNotEmpty(text.trim())) {
//处理一下文本,如处理#{xx},直接替换${yy}为真实传入的值
sb.append(handText(text, params,globalParams));
}
i++;
} else if (n instanceof Element) {
Element e1 = (Element) n;
// 递归解析xml子元素
parserElement(e1, params, globalParams, sb);
// 如果循环标志不为true则解析下一个标签
// 这里表示需要重复解析这个循环标签,则i不变,反之继续处理下一个元素
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++;
}
}
}
//节点处理之后做一些啥事
node.after(currParams, globalParams, ele1, sb);
// 回收当前作用域参数
params.clear();
params = null;
} } /**
* 处理文本替换掉#{item}这种参数
* @param str
* @param params
* @return
* @throws Exception
*/
private String handText(String str, Map<String, Object> params,Map<String, Object> globalParams)
throws Exception {
//获取foreach这种标签中用于记录循环的变量
String indexStr = MapUtils.getString(params, Attrs.WHILE_INDEX);
Integer index = null;
if(StringUtils.isNotEmpty(indexStr)) {
index = MapUtils.getInteger(params, indexStr);
}
//匹配#{a}这种参数
String reg1 = "(#\\{)(\\w+)(\\})";
//匹配${a}这种参数
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类似的循环里,可能需要将参数#{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标签名
String eleName = ele.getName();
//解析一个节点后是否继续,如遇到if这种节点,就需要判断test里是否为空
boolean isContinue = false;
//声明一个抽象节点
BaseNode node = null;
if (StringUtils.isNotEmpty(eleName)) {
//使用节点工厂根据节点名得到一个节点对象比如是if节点还是foreach节点
node = NodeFactory.create(eleName);
//解析一下这个节点,返回是否还需要解析节点里的内容
isContinue = node.parse(currParams, globalParams, ele, sb);
}
return new TempVal(isContinue, ele, node); } public Map<String, Object> getParams() {
return currParams;
} /**
* 封装一个xml元素被解析后的结果
* @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; /**
* 抽象节点
* @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节点
* @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节点的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("判断操作参数"+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节点属性如下
collection 需要遍历的集合
item 遍历集合后每个元素存放的变量
index 遍历集合的索引数如0,1,2...
separator 遍历后以指定分隔符拼接
open 遍历后拼接开始的符号如 (
close 遍历后拼接结束的符号如 )
*/
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) {
/**
* 如果局部变量中存在当前循环变量的值,就表示已经不是第一次进入循环标签了,移除掉开始标记
* 并将局部变量值加1
*/
if(currParams.get(index) != null) {
currParams.remove(Attrs.WHILE_START);
currParams.put(index+"_", (Integer)currParams.get(index+"_") + 1);
} else { //第一次进入循环标签内
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)) {
//得到待循环的集合
collection = Ognl.getValue(collectionStr,allParams);
//如果集合属性不为空,但是条件为null则默认加上一个边界条件
if(StringUtils.isEmpty(conditionStr)) {
//这里只是用集合演示一下,也可以再加上数组,只不过改成.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;
} /**
* 如果是第一次进入循环标签,则拼上open的内容
*/
@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);
} } /**
* 如果是最后进入循环标签,则最后拼上close的内容
*/
@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; /**
* 节点工厂
*/
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); } }
/**
* 各种标记
* @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>
自己动手实现mybatis动态sql的更多相关文章
- mybatis实战教程(mybatis in action)之八:mybatis 动态sql语句
mybatis 的动态sql语句是基于OGNL表达式的.可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:1. if 语句 (简单的条件判断)2. c ...
- 9.mybatis动态SQL标签的用法
mybatis动态SQL标签的用法 动态 SQL MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么 ...
- Mybatis动态SQL单一基础类型参数用if标签
Mybatis动态SQL单一基础类型参数用if标签时,test中应该用 _parameter,如: 1 2 3 4 5 6 <select id="selectByName" ...
- 超全MyBatis动态SQL详解!( 看完SQL爽多了)
MyBatis 令人喜欢的一大特性就是动态 SQL. 在使用 JDBC 的过程中, 根据条件进行 SQL 的拼接是很麻烦且很容易出错的. MyBatis 动态 SQL 的出现, 解决了这个麻烦. My ...
- Mybatis动态SQL简单了解 Mybatis简介(四)
动态SQL概况 MyBatis 的强大特性之一便是它的动态 SQL 在Java开发中经常遇到条件判断,比如: if(x>0){ //执行一些逻辑........ } Mybatis应用中,S ...
- mybatis原理分析学习记录,mybatis动态sql学习记录
以下个人学习笔记,仅供参考,欢迎指正. MyBatis 是支持定制化 SQL.存储过程以及高级映射的持久层框架,其主要就完成2件事情: 封装JDBC操作 利用反射打通Java类与SQL语句之间的相互转 ...
- mybatis 动态sql和参数
mybatis 动态sql 名词解析 OGNL表达式 OGNL,全称为Object-Graph Navigation Language,它是一个功能强大的表达式语言,用来获取和设置Java对象的属性, ...
- MyBatis动态SQL之一使用 if 标签和 choose标签
bootstrap react https://segmentfault.com/a/1190000010383464 xml 中 < 转义 to thi tha <if test=&qu ...
- MyBatis动态SQL(认真看看, 以后写SQL就爽多了)
目录 0 一起来学习 mybatis 1 数据准备 2 if 标签 2.1 在 WHERE 条件中使用 if 标签 2.1.1 查询条件 2.1.2 动态 SQL 2.1.3 测试 2.2 在 UPD ...
随机推荐
- iOS布局
1.Masonry 创建constraint来定义布局的方式: 1.1. mas_makeConstraints : 你可以使用局部变量后者属性来保存以便下次应用它 1.2. mas_updateCo ...
- Ubuntu下通过makefile生成静态库和动态库简单实例
本文转自http://blog.csdn.net/fengbingchun/article/details/17994489 Ubuntu环境:14.04 首先创建一个test_makefile_gc ...
- 2301: [HAOI2011]Problem b ( 分块+莫比乌斯反演+容斥)
2301: [HAOI2011]Problem b Time Limit: 50 Sec Memory Limit: 256 MBSubmit: 6015 Solved: 2741[Submit] ...
- Another Eight Puzzle
Problem Description Fill the following 8 circles with digits 1~8,with each number exactly once . Con ...
- Centos6.8 安装tomcat8.5.11
1.下载 安装包 wget http://mirrors.aliyun.com/apache/tomcat/tomcat-8/v8.5.11/bin/apache-tomcat-8.5.11.tar. ...
- box-shadow + animation 实现loading
.loading{ width:3px; height:3px; border-radius:100%; margin-left:20px; box-shadow:0 -10px 0 1px #333 ...
- java 中 final 的用法
/* final可以修饰类,方法,变量 特点: final可以修饰类,该类不能被继承. final可以修饰方法,该方法不能被重写.(覆盖,复写) final可以修饰变量,该变量不能被重新赋值.因为这个 ...
- 经典面试题: 从输入URL到页面加载的过程发生了什么?
可以分为这几个大的过程: DNS解析 TCP连接 客户端发送HTTP请求 服务器处理请求并返回HTTP报文 浏览器解析渲染页面 结束 其中(1)DNS解析可以理解为主寻找这个IP地址的过程,其中如果找 ...
- WebService-axis2
WebService框架有好多,常用的cxf,axis2等,axis2的配置过程相对简单,不用编写接口,在实现.只需要一个Service服务类即可.配置过程大致如下: 1,导入jar包(这里我是把ax ...
- ALTER TABLE SWITCH' statement failed. The table x' is partitioned while index 'x' is not partitioned.
1.L_Monitoring有这么些字段,ID,Collecttime,PlateType,PlateNO以及其他一些这段.建立这个表的时候是个非分区表,其中ID是主键,并在Collecttime,P ...