2016-12-21 更新
已经加入GitHub托管: [url]https://github.com/leitelyaya/mybatis-3[/url] 注意3.2.x
另外已修复ForEach污染全局变量的问题
实际使用效果嘛,[b]临时表数据越少越好[/b],如果不少,请放弃此优化
特感谢这篇博文讲解参数使用:[url]http://blog.youkuaiyun.com/isea533/article/details/44002219[/url]
------------------------------------------------------------------------------------------------------------------------------------------
应用系统在设计多功能过滤时往往想到使用多表联结查询,如过滤字段增多,数据所关联的表增大时,数据量大,导致查询时间过长,本着exists优化多表的原则,为Mybatis提供一个方便的编码方式。
E.g. 订单查询列表页,不输入条件时查询所有,输入订单类条件时过滤订单,输入订单收货人、发票、商品信息时,过滤相关的订单
原始SQL:
用MyBatis则是:
理想模式下使用exists代替联结优化:
MyBatis下写得就繁琐了:
当查询条件多的时候,exists外层if的查询就难写了,无奈没有现成检测子查询下所有的if语句的结果返回是不符合的情况
理想处理如下:
自定义一个节点,判断上述情况
第一步,更新DTD,使得配置文件验证通过
第二步,配置新解析器,解析自定义节点
新的DTD,在项目资源目录下增加
org.apache.ibatis.builder.xml.mybatis-3-mapper.dtd
注意,项目资源获取优先级大于MyBatis包中的DTD,故而设定为同一个路径,同一个名字,会自动载入改好的文件
更改默认mapper语言解析器:
创建解析器:
org.apache.ibatis.scripting.ExtendingXMLLanguageDriver
org.apache.ibatis.scripting.xmltags.ExtendingXMLScriptBuilder
添加match_any的标签解析器:
org.apache.ibatis.scripting.xmltags.MatchAnySqlNode
~_~ 简单粗暴搞定..
已经加入GitHub托管: [url]https://github.com/leitelyaya/mybatis-3[/url] 注意3.2.x
另外已修复ForEach污染全局变量的问题
实际使用效果嘛,[b]临时表数据越少越好[/b],如果不少,请放弃此优化
特感谢这篇博文讲解参数使用:[url]http://blog.youkuaiyun.com/isea533/article/details/44002219[/url]
------------------------------------------------------------------------------------------------------------------------------------------
应用系统在设计多功能过滤时往往想到使用多表联结查询,如过滤字段增多,数据所关联的表增大时,数据量大,导致查询时间过长,本着exists优化多表的原则,为Mybatis提供一个方便的编码方式。
E.g. 订单查询列表页,不输入条件时查询所有,输入订单类条件时过滤订单,输入订单收货人、发票、商品信息时,过滤相关的订单
原始SQL:
select *
from order
left join receiver
using (xx)
left join invoice
using (xx)
left join goods
using (xx)
where
order...= xx
and receiver... = xx
and invoice... = xx
and goods... = xx
用MyBatis则是:
select *
from order
left join receiver
using (xx)
left join invoice
using (xx)
left join goods
using (xx)
...
<where>
<if test="order...">
and order...= #{order.xx}
</if>
<if test="receiver..."
and receiver...= #{receiver.xx}
</if>
<if test="invoice..."
and invoice...= #{invoice.xx}
</if>
<if test="goods..."
and goods...= #{goods.xx}
</if>
</where>
理想模式下使用exists代替联结优化:
select *
from order
where
order...= xx
and exists (
select 1 from receiver where order.xx=receiver.xx
and receiver... = xx
)
and exists (
select 1 from invoice where order.xx=invoice.xx
and invoice... = xx
)
and exists (
select 1 from goods where order.xx=goods.xx
and goods... = xx
)
MyBatis下写得就繁琐了:
select *
from order
<where>
<if test="order...">
order...= xx
</if>
<if test="receiver.a!=null && receiver.b!=null && receiver.c!=null ...">
and exists (
select 1 from receiver where order.xx=receiver.xx
<if test="receiver.a!=null">
and receiver.a = #{receiver.a}
</if>
<if test="receiver.b!=null">
and receiver.b = #{receiver.b}
</if>
<if test="receiver.c!=null">
and receiver.c = #{receiver.c}
</if>
)
...
</where>
当查询条件多的时候,exists外层if的查询就难写了,无奈没有现成检测子查询下所有的if语句的结果返回是不符合的情况
理想处理如下:
select *
from order
<where>
<if test="order...">
order...= xx
</if>
<match_any>
and exists (
select 1 from receiver where order.xx=receiver.xx
<if test="receiver...">
and receiver... = xx
</if>
<if test="receiver...">
and receiver... = xx
</if>
<if test="receiver...">
and receiver... = xx
</if>
)
</match_any>
...
</where>
自定义一个节点,判断上述情况
第一步,更新DTD,使得配置文件验证通过
第二步,配置新解析器,解析自定义节点
新的DTD,在项目资源目录下增加
org.apache.ibatis.builder.xml.mybatis-3-mapper.dtd
注意,项目资源获取优先级大于MyBatis包中的DTD,故而设定为同一个路径,同一个名字,会自动载入改好的文件
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright 2009-2016 the original author or authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!ELEMENT mapper (cache-ref | cache | resultMap* | parameterMap* | sql* | insert* | update* | delete* | select* )+>
<!ATTLIST mapper
namespace CDATA #IMPLIED
>
<!ELEMENT cache-ref EMPTY>
<!ATTLIST cache-ref
namespace CDATA #REQUIRED
>
<!ELEMENT cache (property*)>
<!ATTLIST cache
type CDATA #IMPLIED
eviction CDATA #IMPLIED
flushInterval CDATA #IMPLIED
size CDATA #IMPLIED
readOnly CDATA #IMPLIED
blocking CDATA #IMPLIED
>
<!ELEMENT parameterMap (parameter+)?>
<!ATTLIST parameterMap
id CDATA #REQUIRED
type CDATA #REQUIRED
>
<!ELEMENT parameter EMPTY>
<!ATTLIST parameter
property CDATA #REQUIRED
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
mode (IN | OUT | INOUT) #IMPLIED
resultMap CDATA #IMPLIED
scale CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT resultMap (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST resultMap
id CDATA #REQUIRED
type CDATA #REQUIRED
extends CDATA #IMPLIED
autoMapping (true|false) #IMPLIED
>
<!ELEMENT constructor (idArg*,arg*)>
<!ELEMENT id EMPTY>
<!ATTLIST id
property CDATA #IMPLIED
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT result EMPTY>
<!ATTLIST result
property CDATA #IMPLIED
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT idArg EMPTY>
<!ATTLIST idArg
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
>
<!ELEMENT arg EMPTY>
<!ATTLIST arg
javaType CDATA #IMPLIED
column CDATA #IMPLIED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
>
<!ELEMENT collection (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST collection
property CDATA #REQUIRED
column CDATA #IMPLIED
javaType CDATA #IMPLIED
ofType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
typeHandler CDATA #IMPLIED
notNullColumn CDATA #IMPLIED
columnPrefix CDATA #IMPLIED
resultSet CDATA #IMPLIED
foreignColumn CDATA #IMPLIED
autoMapping (true|false) #IMPLIED
fetchType (lazy|eager) #IMPLIED
>
<!ELEMENT association (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST association
property CDATA #REQUIRED
column CDATA #IMPLIED
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
select CDATA #IMPLIED
resultMap CDATA #IMPLIED
typeHandler CDATA #IMPLIED
notNullColumn CDATA #IMPLIED
columnPrefix CDATA #IMPLIED
resultSet CDATA #IMPLIED
foreignColumn CDATA #IMPLIED
autoMapping (true|false) #IMPLIED
fetchType (lazy|eager) #IMPLIED
>
<!ELEMENT discriminator (case+)>
<!ATTLIST discriminator
column CDATA #IMPLIED
javaType CDATA #REQUIRED
jdbcType CDATA #IMPLIED
typeHandler CDATA #IMPLIED
>
<!ELEMENT case (constructor?,id*,result*,association*,collection*, discriminator?)>
<!ATTLIST case
value CDATA #REQUIRED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
>
<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>
<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
alias CDATA #REQUIRED
type CDATA #REQUIRED
>
<!ELEMENT select (#PCDATA | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ATTLIST select
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
resultMap CDATA #IMPLIED
resultType CDATA #IMPLIED
resultSetType (FORWARD_ONLY | SCROLL_INSENSITIVE | SCROLL_SENSITIVE) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
fetchSize CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
useCache (true|false) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
resultOrdered (true|false) #IMPLIED
resultSets CDATA #IMPLIED
>
<!ELEMENT insert (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ATTLIST insert
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
useGeneratedKeys (true|false) #IMPLIED
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>
<!ELEMENT selectKey (#PCDATA | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ATTLIST selectKey
resultType CDATA #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
keyColumn CDATA #IMPLIED
order (BEFORE|AFTER) #IMPLIED
databaseId CDATA #IMPLIED
>
<!ELEMENT update (#PCDATA | selectKey | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ATTLIST update
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
keyProperty CDATA #IMPLIED
useGeneratedKeys (true|false) #IMPLIED
keyColumn CDATA #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>
<!ELEMENT delete (#PCDATA | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ATTLIST delete
id CDATA #REQUIRED
parameterMap CDATA #IMPLIED
parameterType CDATA #IMPLIED
timeout CDATA #IMPLIED
flushCache (true|false) #IMPLIED
statementType (STATEMENT|PREPARED|CALLABLE) #IMPLIED
databaseId CDATA #IMPLIED
lang CDATA #IMPLIED
>
<!-- Dynamic -->
<!ELEMENT include (property+)?>
<!ATTLIST include
refid CDATA #REQUIRED
>
<!ELEMENT bind EMPTY>
<!ATTLIST bind
name CDATA #REQUIRED
value CDATA #REQUIRED
>
<!ELEMENT sql (#PCDATA | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ATTLIST sql
id CDATA #REQUIRED
lang CDATA #IMPLIED
databaseId CDATA #IMPLIED
>
<!ELEMENT trim (#PCDATA | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ATTLIST trim
prefix CDATA #IMPLIED
prefixOverrides CDATA #IMPLIED
suffix CDATA #IMPLIED
suffixOverrides CDATA #IMPLIED
>
<!ELEMENT where (#PCDATA | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ELEMENT set (#PCDATA | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ELEMENT foreach (#PCDATA | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ATTLIST foreach
collection CDATA #REQUIRED
item CDATA #IMPLIED
index CDATA #IMPLIED
open CDATA #IMPLIED
close CDATA #IMPLIED
separator CDATA #IMPLIED
>
<!ELEMENT choose (when* , otherwise?)>
<!ELEMENT when (#PCDATA | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ATTLIST when
test CDATA #REQUIRED
>
<!ELEMENT otherwise (#PCDATA | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ELEMENT if (#PCDATA | include | trim | where | set | foreach | choose | if | bind | match_any)*>
<!ATTLIST if
test CDATA #REQUIRED
>
<!ELEMENT match_any (#PCDATA | include | trim | where | set | foreach | choose | if | bind)*>
更改默认mapper语言解析器:
<settings>
...
<setting name="defaultScriptingLanguage" value="extending"/>
</settings>
<typeAliases>
<typeAlias type="org.apache.ibatis.scripting.ExtendingXMLLanguageDriver" alias="extending"/>
</typeAliases>
创建解析器:
org.apache.ibatis.scripting.ExtendingXMLLanguageDriver
public class ExtendingXMLLanguageDriver extends XMLLanguageDriver implements LanguageDriver {
@Override
public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
return new ExtendingXMLScriptBuilder(configuration, script, parameterType).parseScriptNode();
}
}
org.apache.ibatis.scripting.xmltags.ExtendingXMLScriptBuilder
public class ExtendingXMLScriptBuilder extends BaseBuilder {
private XNode context;
private boolean isDynamic;
private Class<?> parameterType;
public ExtendingXMLScriptBuilder(Configuration configuration, XNode context) {
this(configuration, context, null);
}
public ExtendingXMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) {
super(configuration);
this.context = context;
this.parameterType = parameterType;
}
public SqlSource parseScriptNode() {
List<SqlNode> contents = parseDynamicTags(context);
MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
SqlSource sqlSource = null;
if (isDynamic) {
sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
} else {
sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
}
return sqlSource;
}
private List<SqlNode> parseDynamicTags(XNode node) {
List<SqlNode> contents = new ArrayList<SqlNode>();
NodeList children = node.getNode().getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
XNode child = node.newXNode(children.item(i));
if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
String data = child.getStringBody("");
TextSqlNode textSqlNode = new TextSqlNode(data);
if (textSqlNode.isDynamic()) {
contents.add(textSqlNode);
isDynamic = true;
} else {
contents.add(new StaticTextSqlNode(data));
}
} else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlers.get(nodeName);
if (handler == null) {
throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
}
handler.handleNode(child, contents);
isDynamic = true;
}
}
return contents;
}
private Map<String, NodeHandler> nodeHandlers = new HashMap<String, NodeHandler>() {
private static final long serialVersionUID = 7123056019193266281L;
{
put("trim", new TrimHandler());
put("where", new WhereHandler());
put("set", new SetHandler());
put("foreach", new ForEachHandler());
put("if", new IfHandler());
put("choose", new ChooseHandler());
put("when", new IfHandler());
put("otherwise", new OtherwiseHandler());
put("bind", new BindHandler());
put("match_any", new MatchAnyHandler());
}
};
private interface NodeHandler {
void handleNode(XNode nodeToHandle, List<SqlNode> targetContents);
}
private class BindHandler implements NodeHandler {
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
final String name = nodeToHandle.getStringAttribute("name");
final String expression = nodeToHandle.getStringAttribute("value");
final VarDeclSqlNode node = new VarDeclSqlNode(name, expression);
targetContents.add(node);
}
}
private class TrimHandler implements NodeHandler {
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
List<SqlNode> contents = parseDynamicTags(nodeToHandle);
MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
String prefix = nodeToHandle.getStringAttribute("prefix");
String prefixOverrides = nodeToHandle.getStringAttribute("prefixOverrides");
String suffix = nodeToHandle.getStringAttribute("suffix");
String suffixOverrides = nodeToHandle.getStringAttribute("suffixOverrides");
TrimSqlNode trim = new TrimSqlNode(configuration, mixedSqlNode, prefix, prefixOverrides, suffix, suffixOverrides);
targetContents.add(trim);
}
}
private class WhereHandler implements NodeHandler {
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
List<SqlNode> contents = parseDynamicTags(nodeToHandle);
MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
WhereSqlNode where = new WhereSqlNode(configuration, mixedSqlNode);
targetContents.add(where);
}
}
private class SetHandler implements NodeHandler {
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
List<SqlNode> contents = parseDynamicTags(nodeToHandle);
MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
SetSqlNode set = new SetSqlNode(configuration, mixedSqlNode);
targetContents.add(set);
}
}
private class ForEachHandler implements NodeHandler {
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
List<SqlNode> contents = parseDynamicTags(nodeToHandle);
MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
String collection = nodeToHandle.getStringAttribute("collection");
String item = nodeToHandle.getStringAttribute("item");
String index = nodeToHandle.getStringAttribute("index");
String open = nodeToHandle.getStringAttribute("open");
String close = nodeToHandle.getStringAttribute("close");
String separator = nodeToHandle.getStringAttribute("separator");
ForEachSqlNode forEachSqlNode = new ForEachSqlNode(configuration, mixedSqlNode, collection, index, item, open, close, separator);
targetContents.add(forEachSqlNode);
}
}
private class IfHandler implements NodeHandler {
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
List<SqlNode> contents = parseDynamicTags(nodeToHandle);
MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
String test = nodeToHandle.getStringAttribute("test");
IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
targetContents.add(ifSqlNode);
}
}
private class MatchAnyHandler implements NodeHandler {
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
List<SqlNode> contents = parseDynamicTags(nodeToHandle);
List<SqlNode> dynamicContents = new ArrayList<>();
for (SqlNode node : contents) {
if (node instanceof IfSqlNode ||
node instanceof ChooseSqlNode ||
node instanceof ForEachHandler) {
dynamicContents.add(node);
}
}
MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
MixedSqlNode dynamicSqlNode = new MixedSqlNode(dynamicContents);
MatchAnySqlNode matchAnySqlNode = new MatchAnySqlNode(configuration, mixedSqlNode, dynamicSqlNode);
targetContents.add(matchAnySqlNode);
}
}
private class OtherwiseHandler implements NodeHandler {
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
List<SqlNode> contents = parseDynamicTags(nodeToHandle);
MixedSqlNode mixedSqlNode = new MixedSqlNode(contents);
targetContents.add(mixedSqlNode);
}
}
private class ChooseHandler implements NodeHandler {
public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
List<SqlNode> whenSqlNodes = new ArrayList<SqlNode>();
List<SqlNode> otherwiseSqlNodes = new ArrayList<SqlNode>();
handleWhenOtherwiseNodes(nodeToHandle, whenSqlNodes, otherwiseSqlNodes);
SqlNode defaultSqlNode = getDefaultSqlNode(otherwiseSqlNodes);
ChooseSqlNode chooseSqlNode = new ChooseSqlNode(whenSqlNodes, defaultSqlNode);
targetContents.add(chooseSqlNode);
}
private void handleWhenOtherwiseNodes(XNode chooseSqlNode, List<SqlNode> ifSqlNodes, List<SqlNode> defaultSqlNodes) {
List<XNode> children = chooseSqlNode.getChildren();
for (XNode child : children) {
String nodeName = child.getNode().getNodeName();
NodeHandler handler = nodeHandlers.get(nodeName);
if (handler instanceof IfHandler) {
handler.handleNode(child, ifSqlNodes);
} else if (handler instanceof OtherwiseHandler) {
handler.handleNode(child, defaultSqlNodes);
}
}
}
private SqlNode getDefaultSqlNode(List<SqlNode> defaultSqlNodes) {
SqlNode defaultSqlNode = null;
if (defaultSqlNodes.size() == 1) {
defaultSqlNode = defaultSqlNodes.get(0);
} else if (defaultSqlNodes.size() > 1) {
throw new BuilderException("Too many default (otherwise) elements in choose statement.");
}
return defaultSqlNode;
}
}
}
添加match_any的标签解析器:
org.apache.ibatis.scripting.xmltags.MatchAnySqlNode
public class MatchAnySqlNode implements SqlNode {
private final Configuration configuration;
private final SqlNode dynamicContents;
private SqlNode contents;
public MatchAnySqlNode(Configuration configuration, SqlNode contents, SqlNode dynamicContents) {
this.contents = contents;
this.dynamicContents = dynamicContents;
this.configuration = configuration;
}
public boolean apply(DynamicContext context) {
DynamicContext oldContext = context;
context = new FilterContext(oldContext);
dynamicContents.apply(context);
String sqlS = context.getSql();
context = new FilterContext(oldContext);
contents.apply(context);
String sqlN = context.getSql();
context = oldContext;
if (sqlS.trim().length() > 0) {
context.appendSql(sqlN);
return true;
}
return true;
}
private class FilterContext extends DynamicContext {
private DynamicContext delegate;
StringBuffer buffer;
public FilterContext(DynamicContext delegate) {
super(configuration, null);
buffer = new StringBuffer();
this.delegate = delegate;
}
@Override
public Map<String, Object> getBindings() {
return delegate.getBindings();
}
@Override
public void bind(String name, Object value) {
delegate.bind(name, value);
}
@Override
public void appendSql(String sql) {
buffer.append(sql);
}
@Override
public String getSql() {
return buffer.toString();
}
}
}
~_~ 简单粗暴搞定..