欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解MyBatis的動(dòng)態(tài)SQL實(shí)現(xiàn)原理

 更新時(shí)間:2023年07月02日 16:50:39   作者:半夏之沫  
MyBatis提供了強(qiáng)大的動(dòng)態(tài)SQL語(yǔ)句生成功能,以應(yīng)對(duì)復(fù)雜的業(yè)務(wù)場(chǎng)景,本篇文章將結(jié)合MyBatis解析SQL語(yǔ)句的過程對(duì)MyBatis中對(duì)<if>,<where>,<foreach>等動(dòng)態(tài)SQL標(biāo)簽的支持進(jìn)行分析,需要的朋友可以參考下

前言

MyBatis版本:3.5.6

正文

一. XML文檔中的節(jié)點(diǎn)概念

在分析MyBatis如何支持SQL語(yǔ)句之前,本小節(jié)先分析XML文檔中的節(jié)點(diǎn)概念。XML文檔中的每個(gè)成分都是一個(gè)節(jié)點(diǎn),DOM對(duì)XML節(jié)點(diǎn)的規(guī)定如下所示。

  • 整個(gè)文檔是一個(gè)文檔節(jié)點(diǎn);
  • 每個(gè)XML標(biāo)簽是一個(gè)元素節(jié)點(diǎn);
  • 包含在元素節(jié)點(diǎn)中的文本是文本節(jié)點(diǎn)

以一個(gè)XML文檔進(jìn)行說明,如下所示。

<provinces>
    <province name="四川">
        <capital>成都</capital>
    </province>
    <province name="湖北">
        <capital>武漢</capital>
    </province>
</provinces>

如上所示,整個(gè)XML文檔是一個(gè)文檔節(jié)點(diǎn),這個(gè)文檔節(jié)點(diǎn)有一個(gè)子節(jié)點(diǎn),就是<provinces>元素節(jié)點(diǎn),<provinces>元素節(jié)點(diǎn)有五個(gè)子節(jié)點(diǎn),分別是:

  • 文本節(jié)點(diǎn);
  • <province>元素節(jié)點(diǎn);
  • 文本節(jié)點(diǎn),
  • <province>元素節(jié)點(diǎn);
  • 文本節(jié)點(diǎn)。

注意,在<provinces>元素節(jié)點(diǎn)的子節(jié)點(diǎn)中的文本節(jié)點(diǎn)的文本值均是\n,表示換行符。

同樣,<province>元素節(jié)點(diǎn)有三個(gè)子節(jié)點(diǎn),分別是:

  • 文本節(jié)點(diǎn);
  • <capital>元素節(jié)點(diǎn);
  • 文本節(jié)點(diǎn)。

這里的文本節(jié)點(diǎn)的文本值也是\n。

然后<capital>元素節(jié)點(diǎn)只有一個(gè)子節(jié)點(diǎn),為一個(gè)文本節(jié)點(diǎn)。節(jié)點(diǎn)的子節(jié)點(diǎn)之間互為兄弟節(jié)點(diǎn),例如<provinces>元素的五個(gè)子節(jié)點(diǎn)之間互為兄弟節(jié)點(diǎn),name為"四川"的<province>元素節(jié)點(diǎn)的上一個(gè)兄弟節(jié)點(diǎn)為文本節(jié)點(diǎn),下一個(gè)兄弟節(jié)點(diǎn)也為文本節(jié)點(diǎn)。

二. 動(dòng)態(tài)SQL解析流程說明

整體的一個(gè)解析流程如下所示。

也就是寫在映射文件中的一條SQL,會(huì)最終被解析為DynamicSqlSource或者RawSqlSource,前者表示動(dòng)態(tài)SQL,后者表示靜態(tài)SQL。

上圖中的MixedSqlNode,其通常的包含關(guān)系可以由下圖定義。

也就是映射文件中定義一條SQL語(yǔ)句的CRUD標(biāo)簽里的各種子元素,均會(huì)被解析為一個(gè)SqlNode,比如包含了${}的文本,會(huì)被解析為TextSqlNode,不包含${}的文本,會(huì)被解析為StaticTextSqlNode,<choose>標(biāo)簽會(huì)被解析為ChooseSqlNode等,同時(shí)又因?yàn)?lt;choose>標(biāo)簽中會(huì)再有<when>和<otherwise>子標(biāo)簽,所以ChooseSqlNode中又會(huì)持有這些子標(biāo)簽的SqlNode。

所以一條SQL最終就是由這條SQL對(duì)應(yīng)的CRUD標(biāo)簽解析成的各種SqlNode組合而成

三. MyBatis解析動(dòng)態(tài)SQL源碼分析

詳解MyBatis加載映射文件和動(dòng)態(tài)代理的實(shí)現(xiàn)中已經(jīng)知道,在XMLStatementBuilderparseStatementNode() 方法中,會(huì)解析映射文件中的<select>,<insert>,<update>和<delete>標(biāo)簽(后續(xù)統(tǒng)一稱為CURD標(biāo)簽),并生成MappedStatement然后緩存到Configuration中。

CURD標(biāo)簽的解析由XMLLanguageDriver完成,每個(gè)標(biāo)簽解析之后會(huì)生成一個(gè)SqlSource,可以理解為SQL語(yǔ)句,本小節(jié)將對(duì)XMLLanguageDriver如何完成CURD標(biāo)簽的解析進(jìn)行討論。

XMLLanguageDriver創(chuàng)建SqlSourcecreateSqlSource() 方法如下所示。

public SqlSource createSqlSource(Configuration configuration, 
        XNode script, Class<?> parameterType) {
    XMLScriptBuilder builder = new XMLScriptBuilder(
            configuration, script, parameterType);
    return builder.parseScriptNode();
}

如上所示,createSqlSource() 方法的入?yún)⒅校?strong>XNode就是CURD標(biāo)簽對(duì)應(yīng)的節(jié)點(diǎn),在createSqlSource() 方法中先是創(chuàng)建了一個(gè)XMLScriptBuilder,然后通過XMLScriptBuilder來生成SqlSource。先看一下XMLScriptBuilder的構(gòu)造方法,如下所示。

public XMLScriptBuilder(Configuration configuration, XNode context, 
                    Class<?> parameterType) {
    super(configuration);
    this.context = context;
    this.parameterType = parameterType;
    initNodeHandlerMap();
}

XMLScriptBuilder的構(gòu)造方法中,主要是將CURD標(biāo)簽對(duì)應(yīng)的節(jié)點(diǎn)緩存起來,然后初始化nodeHandlerMapnodeHandlerMap中存放著處理MyBatis提供的支持動(dòng)態(tài)SQL的標(biāo)簽的處理器,initNodeHandlerMap() 方法如下所示。

private void initNodeHandlerMap() {
    nodeHandlerMap.put("trim", new TrimHandler());
    nodeHandlerMap.put("where", new WhereHandler());
    nodeHandlerMap.put("set", new SetHandler());
    nodeHandlerMap.put("foreach", new ForEachHandler());
    nodeHandlerMap.put("if", new IfHandler());
    nodeHandlerMap.put("choose", new ChooseHandler());
    nodeHandlerMap.put("when", new IfHandler());
    nodeHandlerMap.put("otherwise", new OtherwiseHandler());
    nodeHandlerMap.put("bind", new BindHandler());
}

現(xiàn)在分析XMLScriptBuilderparseScriptNode() 方法,該方法會(huì)創(chuàng)建SqlSource,如下所示。

public SqlSource parseScriptNode() {
    // 解析動(dòng)態(tài)標(biāo)簽
    MixedSqlNode rootSqlNode = parseDynamicTags(context);
    SqlSource sqlSource;
    if (isDynamic) {
        // 創(chuàng)建DynamicSqlSource并返回
        sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
        // 創(chuàng)建RawSqlSource并返回
        sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
}

XMLScriptBuilderparseScriptNode() 方法中,會(huì)根據(jù)XMLScriptBuilder中的isDynamic屬性判斷是創(chuàng)建DynamicSqlSource還是RawSqlSource,在這里暫時(shí)不分析DynamicSqlSourceRawSqlSource的區(qū)別,但是可以推測(cè)在parseDynamicTags() 方法中會(huì)改變isDynamic屬性的值,即在parseDynamicTags() 方法中會(huì)根據(jù)CURD標(biāo)簽的節(jié)點(diǎn)生成一個(gè)MixedSqlNode,同時(shí)還會(huì)改變isDynamic屬性的值以指示當(dāng)前CURD標(biāo)簽中的SQL語(yǔ)句是否是動(dòng)態(tài)的。

MixedSqlNode是什么,isDynamic屬性值在什么情況下會(huì)變?yōu)?strong>true,帶著這些疑問,繼續(xù)看parseDynamicTags() 方法,如下所示。

protected MixedSqlNode parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<>();
    // 獲取節(jié)點(diǎn)的子節(jié)點(diǎn)
    NodeList children = node.getNode().getChildNodes();
    // 遍歷所有子節(jié)點(diǎn)
    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) {
            // 子節(jié)點(diǎn)為文本節(jié)點(diǎn)
            String data = child.getStringBody("");
            // 基于文本節(jié)點(diǎn)的值并創(chuàng)建TextSqlNode
            TextSqlNode textSqlNode = new TextSqlNode(data);
            // isDynamic()方法可以判斷文本節(jié)點(diǎn)值是否有${}占位符
            if (textSqlNode.isDynamic()) {
                // 文本節(jié)點(diǎn)值有${}占位符
                // 添加TextSqlNode到集合中
                contents.add(textSqlNode);
                // 設(shè)置isDynamic為true
                isDynamic = true;
            } else {
                // 文本節(jié)點(diǎn)值沒有占位符
                // 創(chuàng)建StaticTextSqlNode并添加到集合中
                contents.add(new StaticTextSqlNode(data));
            }
        } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) {
            // 子節(jié)點(diǎn)為元素節(jié)點(diǎn)
            // CURD節(jié)點(diǎn)的子節(jié)點(diǎn)中的元素節(jié)點(diǎn)只可能為<if>,<foreach>等動(dòng)態(tài)Sql標(biāo)簽節(jié)點(diǎn)
            String nodeName = child.getNode().getNodeName();
            // 根據(jù)動(dòng)態(tài)Sql標(biāo)簽節(jié)點(diǎn)的名稱獲取對(duì)應(yīng)的處理器
            NodeHandler handler = nodeHandlerMap.get(nodeName);
            if (handler == null) {
                throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
            }
            // 處理動(dòng)態(tài)Sql標(biāo)簽節(jié)點(diǎn)
            handler.handleNode(child, contents);
            // 設(shè)置isDynamic為true
            isDynamic = true;
        }
    }
    // 創(chuàng)建MixedSqlNode
    return new MixedSqlNode(contents);
}

按照正常執(zhí)行流程調(diào)用parseDynamicTags() 時(shí),入?yún)⑹?strong>CURD標(biāo)簽節(jié)點(diǎn),此時(shí)會(huì)遍歷CURD標(biāo)簽節(jié)點(diǎn)的所有子節(jié)點(diǎn),基于每個(gè)子節(jié)點(diǎn)都會(huì)創(chuàng)建一個(gè)SqlNode然后添加到SqlNode集合contents中,最后將contents作為入?yún)?chuàng)建MixedSqlNode并返回。

SqlNode是一個(gè)接口,在parseDynamicTags() 方法中,可以知道,TextSqlNode實(shí)現(xiàn)了SqlNode接口,StaticTextSqlNode實(shí)現(xiàn)了SqlNode接口,所以當(dāng)節(jié)點(diǎn)的子節(jié)點(diǎn)是文本節(jié)點(diǎn)時(shí),如果文本值包含有${}占位符,則創(chuàng)建TextSqlNode添加到contents中并設(shè)置isDynamictrue,如果文本值不包含${}占位符,則創(chuàng)建StaticTextSqlNode并添加到contents中。

如果CURD標(biāo)簽節(jié)點(diǎn)的子節(jié)點(diǎn)是元素節(jié)點(diǎn),由于CURD標(biāo)簽節(jié)點(diǎn)的元素節(jié)點(diǎn)只可能為<if>,<foreach>等動(dòng)態(tài)SQL標(biāo)簽節(jié)點(diǎn),所以直接會(huì)設(shè)置isDynamictrue,同時(shí)還會(huì)調(diào)用動(dòng)態(tài)SQL標(biāo)簽節(jié)點(diǎn)對(duì)應(yīng)的處理器來生成SqlNode并添加到contents中。這里以<if>標(biāo)簽節(jié)點(diǎn)對(duì)應(yīng)的處理器的handleNode() 方法為例進(jìn)行說明,如下所示。

public void handleNode(XNode nodeToHandle, List<SqlNode> targetContents) {
    // 遞歸調(diào)用parseDynamicTags()解析<if>標(biāo)簽節(jié)點(diǎn)
    MixedSqlNode mixedSqlNode = parseDynamicTags(nodeToHandle);
    String test = nodeToHandle.getStringAttribute("test");
    // 創(chuàng)建IfSqlNode
    IfSqlNode ifSqlNode = new IfSqlNode(mixedSqlNode, test);
    // 將IfSqlNode添加到contents中
    targetContents.add(ifSqlNode);
}

在<if>標(biāo)簽節(jié)點(diǎn)對(duì)應(yīng)的處理器的handleNode() 方法中,遞歸的調(diào)用了parseDynamicTags() 方法來解析<if>標(biāo)簽節(jié)點(diǎn),例如<where>,<foreach>等標(biāo)簽節(jié)點(diǎn)對(duì)應(yīng)的處理器的handleNode() 方法中也會(huì)遞歸調(diào)用parseDynamicTags() 方法,這是因?yàn)檫@些動(dòng)態(tài)SQL標(biāo)簽是可以嵌套使用的,比如<where>標(biāo)簽節(jié)點(diǎn)的子節(jié)點(diǎn)可以為<if>標(biāo)簽節(jié)點(diǎn)。通過上面的handleNode() 方法,大致可以知道MixedSqlNodeIfSqlNode也實(shí)現(xiàn)了SqlNode接口,下面看一下MixedSqlNodeIfSqlNode的實(shí)現(xiàn),如下所示。

public class MixedSqlNode implements SqlNode {
    private final List<SqlNode> contents;
    public MixedSqlNode(List<SqlNode> contents) {
        this.contents = contents;
    }
    @Override
    public boolean apply(DynamicContext context) {
        contents.forEach(node -> node.apply(context));
        return true;
    }
}
public class IfSqlNode implements SqlNode {
    private final ExpressionEvaluator evaluator;
    private final String test;
    private final SqlNode contents;
    public IfSqlNode(SqlNode contents, String test) {
        this.test = test;
        this.contents = contents;
        this.evaluator = new ExpressionEvaluator();
    }
    @Override
    public boolean apply(DynamicContext context) {
        if (evaluator.evaluateBoolean(test, context.getBindings())) {
            contents.apply(context);
            return true;
        }
        return false;
    }
}

其實(shí)到這里已經(jīng)逐漸清晰明了了,按照正常執(zhí)行流程調(diào)用parseDynamicTags() 方法時(shí),是為了將CURD標(biāo)簽節(jié)點(diǎn)的所有子節(jié)點(diǎn)根據(jù)子節(jié)點(diǎn)類型生成不同的SqlNode并放在MixedSqlNode中,然后將MixedSqlNode返回,但是CURD標(biāo)簽節(jié)點(diǎn)的子節(jié)點(diǎn)中如果存在動(dòng)態(tài)SQL標(biāo)簽節(jié)點(diǎn),因?yàn)檫@些動(dòng)態(tài)SQL標(biāo)簽節(jié)點(diǎn)也會(huì)有子節(jié)點(diǎn),所以此時(shí)會(huì)遞歸的調(diào)用parseDynamicTags() 方法,以解析動(dòng)態(tài)SQL標(biāo)簽節(jié)點(diǎn)的子節(jié)點(diǎn),同樣會(huì)將這些子節(jié)點(diǎn)生成SqlNode并放在MixedSqlNode中然后將MixedSqlNode返回,遞歸調(diào)用parseDynamicTags() 方法時(shí)得到的MixedSqlNode會(huì)保存在動(dòng)態(tài)SQL標(biāo)簽節(jié)點(diǎn)對(duì)應(yīng)的SqlNode中,比如IfSqlNode中就會(huì)將遞歸調(diào)用parseDynamicTags() 生成的MixedSqlNode賦值給IfSqlNodecontents字段。

不同的SqlNode都是可以包含彼此的,這是組合設(shè)計(jì)模式的應(yīng)用,SqlNode之間的關(guān)系如下所示。

SqlNode接口定義了一個(gè)方法,如下所示。

public interface SqlNode {
    boolean apply(DynamicContext context);
}

每個(gè)SqlNodeapply() 方法中,除了實(shí)現(xiàn)自己本身的邏輯外,還會(huì)調(diào)用自己所持有的所有SqlNodeapply() 方法,最終逐層調(diào)用下去,所有SqlNodeapply() 方法均會(huì)被執(zhí)行。

四. DynamicSqlSource和RawSqlSource源碼分析

回到XMLScriptBuilderparseScriptNode() 方法,該方法中會(huì)調(diào)用parseDynamicTags() 方法以解析CURD標(biāo)簽節(jié)點(diǎn)并得到MixedSqlNode,MixedSqlNode中含有被解析的CURD標(biāo)簽節(jié)點(diǎn)的所有子節(jié)點(diǎn)對(duì)應(yīng)的SqlNode,最后會(huì)基于MixedSqlNode創(chuàng)建DynamicSqlSource或者RawSqlSource,如果CURD標(biāo)簽中含有動(dòng)態(tài)SQL標(biāo)簽或者SQL語(yǔ)句中含有${}占位符,則創(chuàng)建DynamicSqlSource,否則創(chuàng)建RawSqlSource。下面分別對(duì)DynamicSqlSourceRawSqlSource的實(shí)現(xiàn)進(jìn)行分析。

1. DynamicSqlSource源碼分析

DynamicSqlSource的實(shí)現(xiàn)如下所示。

public class DynamicSqlSource implements SqlSource {
    private final Configuration configuration;
    private final SqlNode rootSqlNode;
    public DynamicSqlSource(Configuration configuration, SqlNode rootSqlNode) {
        // 構(gòu)造函數(shù)只是進(jìn)行了簡(jiǎn)單的賦值操作
        this.configuration = configuration;
        this.rootSqlNode = rootSqlNode;
    }
    @Override
    public BoundSql getBoundSql(Object parameterObject) {
        DynamicContext context = new DynamicContext(configuration, parameterObject);
        // 調(diào)用SqlNode的apply()方法完成Sql語(yǔ)句的生成
        rootSqlNode.apply(context);
        // SqlSourceBuilder可以將Sql語(yǔ)句中的#{}占位符替換為?
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
        Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass();
        // 將Sql語(yǔ)句中的#{}占位符替換為?,并生成一個(gè)StaticSqlSource
        SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings());
        // StaticSqlSource中保存有動(dòng)態(tài)生成好的Sql語(yǔ)句,并且#{}占位符全部替換成了?
        BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
        // 生成有序參數(shù)映射列表
        context.getBindings().forEach(boundSql::setAdditionalParameter);
        return boundSql;
    }
}

DynamicSqlSource的構(gòu)造函數(shù)只是進(jìn)行了簡(jiǎn)單的賦值操作,重點(diǎn)在于其getBoundSql() 方法,在getBoundSql() 方法中,先是調(diào)用DynamicSqlSource中的SqlNodeapply() 方法以完成動(dòng)態(tài)SQL語(yǔ)句的生成,此時(shí)生成的SQL語(yǔ)句中的占位符(如果有的話)為#{},然后再調(diào)用SqlSourceBuilderparse() 方法將SQL語(yǔ)句中的占位符從#{}替換為?并基于替換占位符后的SQL語(yǔ)句生成一個(gè)StaticSqlSource并返回,這里可以看一下StaticSqlSource的實(shí)現(xiàn),如下所示。

public class StaticSqlSource implements SqlSource {
    private final String sql;
    private final List<ParameterMapping> parameterMappings;
    private final Configuration configuration;
    public StaticSqlSource(Configuration configuration, String sql) {
        this(configuration, sql, null);
    }
    public StaticSqlSource(Configuration configuration, String sql, 
                           List<ParameterMapping> parameterMappings) {
        // 構(gòu)造函數(shù)只是進(jìn)行簡(jiǎn)單的賦值操作
        this.sql = sql;
        this.parameterMappings = parameterMappings;
        this.configuration = configuration;
    }
    @Override
    public BoundSql getBoundSql(Object parameterObject) {
        // 基于Sql語(yǔ)句創(chuàng)建一個(gè)BoundSql并返回
        return new BoundSql(configuration, sql, parameterMappings, parameterObject);
    }
}

所以分析到這里,可以知道DynamicSqlSourcegetBoundSql() 方法實(shí)際上會(huì)完成動(dòng)態(tài)SQL語(yǔ)句的生成和#{}占位符替換,然后基于生成好的SQL語(yǔ)句創(chuàng)建BoundSql并返回。BoundSql對(duì)象的類圖如下所示。

實(shí)際上,MyBatis中執(zhí)行SQL語(yǔ)句時(shí),如果映射文件中的SQL使用到了動(dòng)態(tài)SQL標(biāo)簽,那么MyBatis中的Executor(執(zhí)行器,后續(xù)文章中會(huì)進(jìn)行介紹)會(huì)調(diào)用MappedStatementgetBoundSql() 方法,然后在MappedStatementgetBoundSql() 方法中又會(huì)調(diào)用DynamicSqlSourcegetBoundSql() 方法,所以MyBatis中的動(dòng)態(tài)SQL語(yǔ)句會(huì)在這條語(yǔ)句實(shí)際要執(zhí)行時(shí)才會(huì)生成。

2. RawSqlSource源碼分析

現(xiàn)在看一下RawSqlSource的實(shí)現(xiàn),如下所示。

public class RawSqlSource implements SqlSource {
    private final SqlSource sqlSource;
    public RawSqlSource(Configuration configuration, SqlNode rootSqlNode, Class<?> parameterType) {
        // 先調(diào)用getSql()方法獲取Sql語(yǔ)句
        // 然后再執(zhí)行構(gòu)造函數(shù)
        this(configuration, getSql(configuration, rootSqlNode), parameterType);
    }
    public RawSqlSource(Configuration configuration, String sql, Class<?> parameterType) {
        SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration);
        Class<?> clazz = parameterType == null ? Object.class : parameterType;
        // 將Sql語(yǔ)句中的#{}占位符替換為?,生成一個(gè)StaticSqlSource并賦值給sqlSource
        sqlSource = sqlSourceParser.parse(sql, clazz, new HashMap<>());
    }
    private static String getSql(Configuration configuration, SqlNode rootSqlNode) {
        DynamicContext context = new DynamicContext(configuration, null);
        rootSqlNode.apply(context);
        return context.getSql();
    }
    @Override
    public BoundSql getBoundSql(Object parameterObject) {
        // 實(shí)際是調(diào)用StaticSqlSource的getBoundSql()方法
        return sqlSource.getBoundSql(parameterObject);
    }
}

RawSqlSource會(huì)在構(gòu)造函數(shù)中就將SQL語(yǔ)句生成好并替換#{}占位符,在SQL語(yǔ)句實(shí)際要執(zhí)行時(shí),就直接將生成好的SQL語(yǔ)句返回。所以MyBatis中,靜態(tài)SQL語(yǔ)句的執(zhí)行通常要快于動(dòng)態(tài)SQL語(yǔ)句的執(zhí)行,這在RawSqlSource類的注釋中也有提及,如下所示。

Static SqlSource. It is faster than {@link DynamicSqlSource} because mappings are calculated during startup.

總結(jié)

MyBatis會(huì)為映射文件中的每個(gè)CURD標(biāo)簽節(jié)點(diǎn)里的SQL語(yǔ)句生成一個(gè)SqlSource

  • 如果是靜態(tài)SQL語(yǔ)句,那么會(huì)生成RawSqlSource
  • 如果是動(dòng)態(tài)SQL語(yǔ)句,則會(huì)生成DynamicSqlSource。

MyBatis在生成SqlSource時(shí),會(huì)為CURD標(biāo)簽節(jié)點(diǎn)的每個(gè)子節(jié)點(diǎn)都生成一個(gè)SqlNode,無論子節(jié)點(diǎn)是文本值節(jié)點(diǎn)還是動(dòng)態(tài)SQL元素節(jié)點(diǎn),最終所有子節(jié)點(diǎn)對(duì)應(yīng)的SqlNode都會(huì)放在SqlSource中以供生成SQL語(yǔ)句使用。

如果是靜態(tài)SQL語(yǔ)句,那么在創(chuàng)建RawSqlSource時(shí)就會(huì)使用SqlNode完成SQL語(yǔ)句的生成以及將SQL語(yǔ)句中的#{}占位符替換為?,然后保存在RawSqlSource中,等到這條靜態(tài)SQL語(yǔ)句要被執(zhí)行時(shí),就直接返回這條靜態(tài)SQL語(yǔ)句。

如果是動(dòng)態(tài)SQL語(yǔ)句,在創(chuàng)建DynamicSqlSource時(shí)只會(huì)簡(jiǎn)單的將SqlNode保存下來,等到這條動(dòng)態(tài)SQL語(yǔ)句要被執(zhí)行時(shí),才會(huì)使用SqlNode完成SQL語(yǔ)句的生成以及將SQL語(yǔ)句中的#{}占位符替換為?,最后返回SQL語(yǔ)句。

所以MyBatis中,靜態(tài)SQL語(yǔ)句的獲取要快于動(dòng)態(tài)SQL語(yǔ)句

以上就是詳解MyBatis的動(dòng)態(tài)SQL實(shí)現(xiàn)原理的詳細(xì)內(nèi)容,更多關(guān)于MyBatis 動(dòng)態(tài)SQL實(shí)現(xiàn)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java設(shè)計(jì)模式之裝飾模式詳解

    Java設(shè)計(jì)模式之裝飾模式詳解

    這篇文章主要介紹了Java設(shè)計(jì)模式中的裝飾者模式,裝飾者模式即Decorator?Pattern,裝飾模式是在不必改變?cè)愇募褪褂美^承的情況下,動(dòng)態(tài)地?cái)U(kuò)展一個(gè)對(duì)象的功能,裝飾模式又名包裝模式。裝飾器模式以對(duì)客戶端透明的方式拓展對(duì)象的功能,是繼承關(guān)系的一種替代方案
    2022-07-07
  • java 在觀察者模式中使用泛型T的實(shí)例

    java 在觀察者模式中使用泛型T的實(shí)例

    下面小編就為大家?guī)硪黄猨ava 在觀察者模式中使用泛型T的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02
  • jmeter設(shè)置全局變量與正則表達(dá)式提取器過程圖解

    jmeter設(shè)置全局變量與正則表達(dá)式提取器過程圖解

    這篇文章主要介紹了jmeter設(shè)置全局變量與正則表達(dá)式提取器過程圖解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • SpringBoot整合Hashids實(shí)現(xiàn)數(shù)據(jù)ID加密隱藏的全過程

    SpringBoot整合Hashids實(shí)現(xiàn)數(shù)據(jù)ID加密隱藏的全過程

    這篇文章主要為大家詳細(xì)介紹了SpringBoot整合Hashids實(shí)現(xiàn)數(shù)據(jù)ID加密隱藏的全過程,文中的示例代碼講解詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2024-01-01
  • Java中Spring對(duì)事務(wù)的支持詳解

    Java中Spring對(duì)事務(wù)的支持詳解

    這篇文章主要介紹了Java中Spring對(duì)事務(wù)的支持詳解,Spring對(duì)事務(wù)的支持有兩種方式,一是自己編寫事務(wù),精確控制事務(wù)的邊界,二是采用聲明事務(wù)的方式,使用AOP來完成,需要的朋友可以參考下
    2023-07-07
  • SpringBoot整合ActiveMQ的詳細(xì)步驟

    SpringBoot整合ActiveMQ的詳細(xì)步驟

    昨天仔細(xì)研究了activeMQ消息隊(duì)列,也遇到了些坑,下面這篇文章主要給大家介紹了關(guān)于SpringBoot整合ActiveMQ的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-11-11
  • Java中public關(guān)鍵字用法詳細(xì)講解

    Java中public關(guān)鍵字用法詳細(xì)講解

    這篇文章主要給大家介紹了關(guān)于Java中public關(guān)鍵字用法的相關(guān)資料,public關(guān)鍵字是和訪問權(quán)限相關(guān)的,它所修飾的方法對(duì)所有類都是可以訪問的,需要的朋友可以參考下
    2023-09-09
  • Spring MVC整合 freemarker及使用方法

    Spring MVC整合 freemarker及使用方法

    Spring MVC是一種基于Java的實(shí)現(xiàn)了Web MVC設(shè)計(jì)模式的請(qǐng)求驅(qū)動(dòng)類型的輕量級(jí)Web框架,這篇文章主要介紹了Spring MVC整合 freemarker及使用方法,需要的朋友可以參考下
    2019-07-07
  • Java定義形式及可變參數(shù)實(shí)例解析

    Java定義形式及可變參數(shù)實(shí)例解析

    這篇文章主要介紹了Java定義形式及可變參數(shù)實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • 基于spring如何實(shí)現(xiàn)事件驅(qū)動(dòng)實(shí)例代碼

    基于spring如何實(shí)現(xiàn)事件驅(qū)動(dòng)實(shí)例代碼

    這篇文章主要給大家介紹了關(guān)于基于spring如何實(shí)現(xiàn)事件驅(qū)動(dòng)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04

最新評(píng)論