ibatis的动态sql

位于包
com.ibatis.sqlmap.engine.mapping.sql.dynamic
。ibatis的标签和mybatis的标签之间,区别已经很大了


ibatis在启动的时候,会把所有的sql对象解析出来,并初始化为对应的实际实现。Sql对象的实现类有很多,常见的有
StaticSql,这个是纯净态的,没有动态SQL
DynamicSql是包含了动态标签的SQL对象。

理解了动态SQL其他都是类似原理。

<select id="selectOutSales" resultClass="hashmap">
SELECT
t_tz_btscxx.BTSCID,
t_tz_btscxx.SZTZDID,
t_tz_btscxx.TZSXH,
t_tz_btscxx.RTSJ,
t_tz_btscxx.RTSL,
t_tz_btscxx.SCLXDM,
t_tz_btscxx.RTSCX,
t_tz_btscxx.ZZL,
t_tz_btscxx.SCZT,
t_tz_btscxx.SFWCTZ,
t_tz_btscxx.WCTZSJ,
t_tz_btscxx.CJRY,
t_tz_btscxx.CJRQ,
t_tz_btscxx.XGRY,
t_tz_btscxx.XGRQ,
t_tz_btscxx.SCBJ
FROM
t_tz_btscxx
<dynamic prepend="where">
<isNotEmpty prepend="and" property="BTSCID">
BTSCID like '%$BTSCID$%'
</isNotEmpty>
</dynamic>
</select>

如上配置的一个sql语句,第一层节点有三个,text,dynamic元素,text(这个属于XML的知识,具体从XML脑补)。

在调用该SQL的时候,会执行是DynamicSql的
process方法


private void process(StatementScope statementScope, Object parameterObject) {
SqlTagContext ctx = new SqlTagContext();
List localChildren = children;
processBodyChildren(statementScope, ctx, parameterObject, localChildren.iterator());

ParameterMap map = new ParameterMap(delegate);
map.setId(statementScope.getStatement().getId() + "-InlineParameterMap");
map.setParameterClass(((MappedStatement) statementScope.getStatement()).getParameterClass());
map.setParameterMappingList(ctx.getParameterMappings());

String dynSql = ctx.getBodyText();

// Processes $substitutions$ after DynamicSql
if (SimpleDynamicSql.isSimpleDynamicSql(dynSql)) {
dynSql = new SimpleDynamicSql(delegate, dynSql).getSql(statementScope, parameterObject);
}

statementScope.setDynamicSql(dynSql);
statementScope.setDynamicParameterMap(map);
}




比较重要的方法

//这是处理SQL内部各个节点的方法
private void processBodyChildren(StatementScope statementScope, SqlTagContext ctx, Object parameterObject, Iterator localChildren, PrintWriter out) {
//每个节点都需要被处理
while (localChildren.hasNext()) {
SqlChild child = (SqlChild) localChildren.next();
//纯文本节点,处理比较简单,就是把参数等解析出来。
if (child instanceof SqlText) {
SqlText sqlText = (SqlText) child;
String sqlStatement = sqlText.getText();
if (sqlText.isWhiteSpace()) {
out.print(sqlStatement);
} else if (!sqlText.isPostParseRequired()) {

// BODY OUT
out.print(sqlStatement);

ParameterMapping[] mappings = sqlText.getParameterMappings();
if (mappings != null) {
for (int i = 0, n = mappings.length; i < n; i++) {
ctx.addParameterMapping(mappings[i]);
}
}
} else {

IterateContext itCtx = ctx.peekIterateContext();

if(null != itCtx && itCtx.isAllowNext()){
itCtx.next();
itCtx.setAllowNext(false);
if(!itCtx.hasNext()) {
itCtx.setFinal(true);
}
}

if(itCtx!=null) {
StringBuffer sqlStatementBuffer = new StringBuffer(sqlStatement);
iteratePropertyReplace(sqlStatementBuffer, itCtx);
sqlStatement = sqlStatementBuffer.toString();
}

sqlText = PARAM_PARSER.parseInlineParameterMap(delegate.getTypeHandlerFactory(), sqlStatement);

ParameterMapping[] mappings = sqlText.getParameterMappings();
out.print(sqlText.getText());
if (mappings != null) {
for (int i = 0, n = mappings.length; i < n; i++) {
ctx.addParameterMapping(mappings[i]);
}
}
}
} else if (child instanceof SqlTag) {
//节点本身就是一个节点。每种节点都有自己的显示,每个节点实际上用的是SqlTag,一种Tag的子类。内部包含了SqlTagHandler,每个SqlTag都会初始化自己的处理器。对于dynamic使用的是DynamicTagHandler
SqlTag tag = (SqlTag) child;
SqlTagHandler handler = tag.getHandler();
int response = SqlTagHandler.INCLUDE_BODY;
do {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
//这句比较重要,对于条件标签,如果条件不满足,会返回SKIP_BODY,来跳过该元素处理
response = handler.doStartFragment(ctx, tag, parameterObject);
if (response != SqlTagHandler.SKIP_BODY) {

processBodyChildren(statementScope, ctx, parameterObject, tag.getChildren(), pw);
pw.flush();
pw.close();
StringBuffer body = sw.getBuffer();
response = handler.doEndFragment(ctx, tag, parameterObject, body);
handler.doPrepend(ctx, tag, parameterObject, body);

if (response != SqlTagHandler.SKIP_BODY) {
if (body.length() > 0) {
out.print(body.toString());
}
}

}
} while (response == SqlTagHandler.REPEAT_BODY);

ctx.popRemoveFirstPrependMarker(tag);

if(ctx.peekIterateContext()!= null && ctx.peekIterateContext().getTag() == tag) {
ctx.setAttribute(ctx.peekIterateContext().getTag(), null);
ctx.popIterateContext();
}

}
}
}


dynamic元素有三个属性
prepend="where" open="" close=""

prepend和open是在DynamicTagHandler的doStartFragment和doPrepend共同完成处理,close是在doEndFragment处理的。


public class DynamicTagHandler extends BaseTagHandler {

public int doStartFragment(SqlTagContext ctx, SqlTag tag, Object parameterObject) {
//DynamicTagHandler的doStartFragment只是负责把RemoveFirstPrependMarker标记对象创建和压入。
ctx.pushRemoveFirstPrependMarker(tag);
return BaseTagHandler.INCLUDE_BODY;
}

}



public void pushRemoveFirstPrependMarker(SqlTag tag) {

if(tag.getHandler() instanceof DynamicTagHandler) {
//对于DynamicTagHandler之类的条件标签,如果开启了prepend属性,需要设置标记为true。这会造成之后的条件语句的改变,即当出现过一个条件满足时,之后的条件的prepend才会有效,而DynamicTagHandler本身的prepend也才会生效。

对于只有条件,而没有对应的上级(select 标签元素等不是上级),只要满足了条件,那么就会追加prepend,而不管具体是不是第一次满足。
// this was added to retain default behavior
if(tag.isPrependAvailable()) {
removeFirstPrependStack.addFirst(
new RemoveFirstPrependMarker(tag,true));
} else {
removeFirstPrependStack.addFirst(
new RemoveFirstPrependMarker(tag,false));
}
} else if("true".equals(tag.getRemoveFirstPrepend())
|| "iterate".equals(tag.getRemoveFirstPrepend())){
// you must be specific about the removal otherwise it
// will function as ibatis has always functioned and add
// the prepend
removeFirstPrependStack.addFirst(
new RemoveFirstPrependMarker(tag,true));
} else if(!tag.isPrependAvailable() &&
!"true".equals(tag.getRemoveFirstPrepend()) &&
!"iterate".equals(tag.getRemoveFirstPrepend()) &&
tag.getParent() != null) {
// if no prepend or removeFirstPrepend is specified
// we need to look to the parent tag for default values
if("true".equals(tag.getParent().getRemoveFirstPrepend())
|| "iterate".equals(tag.getParent().getRemoveFirstPrepend())) {
removeFirstPrependStack.addFirst(
new RemoveFirstPrependMarker(tag,true));
}
} else {
removeFirstPrependStack.addFirst(
new RemoveFirstPrependMarker(tag,false));
}

}


由于dynamic包含了isNotEmpty,因此在dynamic处理之前,需要处理isNotEmpty,
isNotEmpty之前的处理是和dynamic类似的。


isNotEmpty的doPrepend方法是BaseTagHandler的,并没有改写

public void doPrepend(SqlTagContext ctx, SqlTag tag, Object parameterObject, StringBuffer bodyContent) {

//可以看到open属性是先于prepend属性处理的。但是每次sql字符插入都是从0位置插入,所以open先处理,但是当open和prepend都存在,prepend会插入到open之前的位置。
if (tag.isOpenAvailable() && !(tag.getHandler() instanceof IterateTagHandler)) {
if (bodyContent.toString().trim().length() > 0) {
bodyContent.insert(0, tag.getOpenAttr());
}
}

if (tag.isPrependAvailable()) {
if (bodyContent.toString().trim().length() > 0) {
//这里看到对于标记是true的,那么第一次不进行追加
//ctx.peekRemoveFirstPrependMarker(tag)取的是堆栈的第二个数据,而不是最顶部的数据。如上的配置,当处理IsNotEmpty的时候,取得是它的父级dynamic的设置。

//实际上ibatis的实现者把父级也看成和子元素一样的的平行级别处理了,
P---
C---1
C---2
变成了
C---1
C---2
P---
的结构,那么当C--1满足,C1不进行住家prepend,而C--2和P--则需要。

if (tag.getParent() != null && ctx.peekRemoveFirstPrependMarker(tag)) {
ctx.disableRemoveFirstPrependMarker();
}else {
bodyContent.insert(0, tag.getPrependAttr());
}
}
}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值