日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

sql if 和insert_拼多多面试:Mybatis是如何实现SQL语句复用功能的?

發布時間:2023/12/4 数据库 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 sql if 和insert_拼多多面试:Mybatis是如何实现SQL语句复用功能的? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在工作中,往往有這樣的需求,對于同一個sql條件查詢,首先需要統計記錄條數,用以計算pageCount,然后再對結果進行分頁查詢顯示,看下面一個例子。

<sql id="studentProperties"><!--sql片段-->select stud_id as studId, name, email, dob, phonefrom students</sql><select id="countAll" resultType="int">select count(1) from (<include refid="studentProperties"></include><!--復用-->) tmp</select><select id="findAll" resultType="Student" parameterType="map">select * from (<include refid="studentProperties"></include><!--復用-->) tmp limit #{offset}, #{pagesize}</select>

這就是sqlFragment,它可以為select|insert|update|delete標簽服務,可以定義很多sqlFragment,然后使用include標簽引入多個sqlFragment。在工作中,也是比較常用的一個功能,它的優點很明顯,復用sql片段,它的缺點也很明顯,不能完整的展現sql邏輯,如果一個標簽,include了四至五個sqlFragment,其可讀性就非常差了。

sqlFragment里的內容是可以隨意寫的,它不需要是一個完整的sql,它可以是“,phone”這么簡單的文本。

1.sqlFragment的解析過程

sqlFragment存儲于Configuration內部。

protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");

解析sqlFragment的過程非常簡單。

org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XNode)方法部分源碼。

// 解析sqlFragment sqlElement(context.evalNodes("/mapper/sql")); // 為select|insert|update|delete提供服務 buildStatementFromContext(context.evalNodes("select|insert|update|delete"));

sqlFragment存儲于Map<String, XNode>結構當中。其實最關鍵的,是它如何為select|insert|update|delete提供服務的。

2.select|insert|update|delete標簽中,解析include標簽的過程

org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode()方法源碼。

// Include Fragments before parsing XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); // 重點關注的方法 includeParser.applyIncludes(context.getNode());// Parse selectKey after includes and remove them. processSelectKeyNodes(id, parameterTypeClass, langDriver);// Parse the SQL (pre: <selectKey> and <include> were parsed and removed) SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);

注釋“pre: <selectKey> and <include> were parsed and removed”,含義為解析完,并移除。為什么要移除呢?秘密都隱藏在applyIncludes()方法內部了。

org.apache.ibatis.builder.xml.XMLIncludeTransformer.applyIncludes(Node, Properties)方法源碼。

/*** Recursively apply includes through all SQL fragments.* @param source Include node in DOM tree* @param variablesContext Current context for static variables with values*/private void applyIncludes(Node source, final Properties variablesContext) {if (source.getNodeName().equals("include")) {// new full context for included SQL - contains inherited context and new variables from current include nodeProperties fullContext;String refid = getStringAttribute(source, "refid");// replace variables in include refid valuerefid = PropertyParser.parse(refid, variablesContext);Node toInclude = findSqlFragment(refid);Properties newVariablesContext = getVariablesContext(source, variablesContext);if (!newVariablesContext.isEmpty()) {// merge contextsfullContext = new Properties();fullContext.putAll(variablesContext);fullContext.putAll(newVariablesContext);} else {// no new context - use inherited fullyfullContext = variablesContext;}// 遞歸調用applyIncludes(toInclude, fullContext);if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {toInclude = source.getOwnerDocument().importNode(toInclude, true);}// 將include節點,替換為sqlFragment節點source.getParentNode().replaceChild(toInclude, source);while (toInclude.hasChildNodes()) {// 將sqlFragment的子節點(也就是文本節點),插入到sqlFragment的前面toInclude.getParentNode().insertBefore(toInclude.getFirstChild(), toInclude);}// 移除sqlFragment節點toInclude.getParentNode().removeChild(toInclude);} else if (source.getNodeType() == Node.ELEMENT_NODE) {NodeList children = source.getChildNodes();for (int i=0; i<children.getLength(); i++) {// 遞歸調用applyIncludes(children.item(i), variablesContext);}} else if (source.getNodeType() == Node.ATTRIBUTE_NODE && !variablesContext.isEmpty()) {// replace variables in all attribute valuessource.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));} else if (source.getNodeType() == Node.TEXT_NODE && !variablesContext.isEmpty()) {// replace variables ins all text nodessource.setNodeValue(PropertyParser.parse(source.getNodeValue(), variablesContext));}}

上面是對源碼的解讀,為了便于理解,我們接下來采用圖示的辦法,演示其過程。

3.圖示過程演示

①解析節點

<select id="countAll" resultType="int">select count(1) from (<include refid="studentProperties"></include>) tmp</select>

②include節點替換為sqlFragment節點

<select id="countAll" resultType="int">select count(1) from (<sql id="studentProperties">select stud_id as studId, name, email, dob, phonefrom students</sql> ) tmp</select>

③將sqlFragment的子節點(文本節點)insert到sqlFragment節點的前面。注意,對于dom來說,文本也是一個節點,叫TextNode。

<select id="countAll" resultType="int">select count(1) from (select stud_id as studId, name, email, dob, phonefrom students<sql id="studentProperties">select stud_id as studId, name, email, dob, phonefrom students</sql> ) tmp</select>

④移除sqlFragment節點

<select id="countAll" resultType="int">select count(1) from (select stud_id as studId, name, email, dob, phonefrom students ) tmp</select>

⑤最終結果如圖所示

如此一來,TextNode1 + TextNode2 + TextNode3,就組成了一個完整的sql。遍歷select的三個子節點,分別取出TextNode的value,append到一起,就是最終完整的sql。

這也是為什么要移除<selectKey> and <include>節點的原因。

這就是Mybatis的sqlFragment,以上示例,均為靜態sql,即static sql。

最后

感謝大家看到最后,如文章有不足,歡迎大家在評論區支持,給予意見。如果覺得我的文章對你有幫助,那就給我一個贊同吧。

每天都會分享java相關技術文章或行業資訊。歡迎大家關注和轉發文章。

總結

以上是生活随笔為你收集整理的sql if 和insert_拼多多面试:Mybatis是如何实现SQL语句复用功能的?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。