`
liyixing1
  • 浏览: 952858 次
  • 性别: Icon_minigender_1
  • 来自: 江西上饶
社区版块
存档分类
最新评论

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());
        }
      }
    }

  }
分享到:
评论

相关推荐

    ibatis动态SQL标签用法

    iBatis动态SQL标签用法 iBatis是Java持久层框架,提供了动态SQL标签来实现动态查询。动态SQL标签可以根据不同的条件生成不同的SQL语句,从而提高查询效率和灵活性。 动态SQL片段 iBatis提供了动态SQL片段的功能,...

    转ibatis动态sql - phoebus0501 - 博客园.mht

    转ibatis动态sql - phoebus0501 - 博客园.mht

    IBATIS动态sql - 紫晶幻治 - 51CTO技术博客.mht

    IBATIS动态sql - 紫晶幻治 - 51CTO技术博客.mht

    基于iBATIS动态SQL的应用研究.pdf

    iBATIS 的核心在于POJO(Plain Old Java Object)与SQL之间的映射关系,而不是自动生成SQL语句。这意味着开发者需要手动编写SQL,然后通过配置文件将SQL的参数和返回结果映射到对应的Java对象。 iBATIS 提供了灵活...

    iBatis的动态SQL语句

    ### iBatis中的动态SQL语句详解 #### 引言 在进行数据库操作时,我们经常需要根据不同的条件构建不同的SQL语句。这种需求在实际开发中极为常见,尤其是在处理复杂的查询逻辑时。iBatis(现在通常被称为MyBatis)...

    ibatis打印sql

    iBATIS支持动态SQL,即在运行时根据条件构建SQL。通过打印SQL,可以验证动态条件是否正确地被插入到最终执行的SQL中。 5. **性能优化**: 打印SQL还能帮助优化数据库查询性能。通过分析SQL语句,可以识别慢查询并...

    ibatis 开发指南 和 iBATIS-SqlMaps两本图书

    5. **动态SQL**:讲解如何使用iBATIS的动态元素来构建灵活的SQL语句,以应对复杂的查询需求。 6. **API使用**:介绍SqlSession、SqlSessionFactory、Executor等关键接口和类的使用方法。 7. **缓存机制**:解释...

    iBATIS-SqlMaps2入门代码文档

    ### iBATIS-SqlMaps2入门代码文档知识点详解 #### 一、简介 iBATIS-SqlMaps2是一款用于简化Java应用程序与数据库交互过程的框架。该框架通过配置文件定义了对象关系映射规则,使得开发者能够更加专注于业务逻辑而...

    iBATIS-SqlMaps-2-Tutorial_cn.pdf

    iBATIS-SqlMaps-2-Tutorial_cniBATIS-SqlMaps-2-Tutorial_cn.pdf.pdfiBATIS-SqlMaps-2-Tutorial_cn.pdfiBATIS-SqlMaps-2-Tutorial_cn.pdf

    ibatis-sqlmaps-2_cn

    此外,教程还会涵盖动态SQL,这是IBATIS的一大亮点,允许在运行时构建和修改SQL语句,提高了代码的复用性和可维护性。 在使用IBATIS的过程中,你可能会遇到参数映射和结果映射的问题。《ibatis-sqlmaps-2_cn》将...

    [iBATIS]sql转换工具

    [iBATIS]sql转换工具 简单哦~ 项目组自己写的哦~分享给大家了

    iBATIS-SqlMaps-中文教程

    5. **动态SQL**:iBATIS的一大亮点是其强大的动态SQL能力,可以实现条件查询、循环拼接SQL等复杂逻辑,无需编写大量Java代码。 6. **Mapper接口**:iBATIS 2.3版本引入了Mapper接口,将XML配置与Java代码更好地结合...

    ibatis的dynamicSQL中,关于prepend的使用

    在探讨ibatis中的动态SQL(Dynamic SQL)及`prepend`的使用时,我们首先需要对ibatis有一个基本的理解。ibatis是一种开源的数据访问层框架,它简化了Java应用程序与数据库之间的交互过程。通过使用XML配置文件来定义...

    ibatis动态注入

    iBATIS,作为一款优秀的持久层框架,提供了强大的动态SQL功能,解决了直接使用JDBC时编写复杂动态SQL的难题。本文将深入探讨iBATIS动态注入的相关知识点。 iBATIS动态SQL主要通过XML映射文件中的特定标签实现,允许...

    webwork+ibatis+sqlserver2000

    iBATIS允许直接在SQL语句中处理复杂的查询和事务管理,同时提供了一种动态和灵活的方式来映射结果集到Java对象。 【SQL Server 2000】:这是微软发布的关系型数据库管理系统,主要用于存储、管理和检索数据。SQL ...

    打log4j日志-ibatis的sql输出

    对于Ibatis,这是一个轻量级的持久层框架,它将SQL语句与Java代码分离,提供了动态SQL的能力。在默认情况下,Ibatis并不会自动打印执行的SQL语句,但通过配置,我们可以使Ibatis在运行时输出SQL,这对于调试和性能...

    iBATIS-SqlMaps-2-Tutorial_cn

    动态SQL是iBATIS的一大特色,它允许在SQL语句中使用条件判断、循环等逻辑,极大地增强了SQL的灵活性。 本书首先会引导读者理解iBATIS的基本架构,然后详细介绍如何创建和配置SqlMap,以及如何编写映射文件。映射...

    在ibatis日志信息中打印SQL语句的方法(个人总结)

    在使用iBatis(现为MyBatis)作为持久层框架进行开发时,有时我们需要调试SQL语句,以便查看执行的SQL、优化性能或者解决查询问题。本文将详细讲解如何在iBatis日志信息中打印SQL语句,以便更好地理解和优化数据库...

    iBATIS-SqlMaps-2_cn1.pdf

    iBATIS-SqlMaps ibatis入门教程,教你如何做配置ibatis

Global site tag (gtag.js) - Google Analytics