MyBatis 中為什么不建議使用 Where 1=1?
最近接手了一個老項目,“愉悅的心情”自然無以言表,做開發(fā)的朋友都懂,這里就不多說了,都是淚...
接手老項目,自然是要先熟悉一下業(yè)務(wù)代碼,然而在翻閱 mapper 文件時,發(fā)現(xiàn)了一個比較詭異的事情。這里給出簡化后的業(yè)務(wù)代碼:
- <?xml version="1.0" encoding="UTF-8"?>
 - <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 - <mapper namespace="com.example.demo.mapper.UserMapper">
 - <select id="list" resultType="com.example.demo.model.User">
 - select * from user
 - where 1=1
 - <if test="name!=null">
 - and name=#{name}
 - </if>
 - <if test="password!=null">
 - and password=#{password}
 - </if>
 - </select>
 - </mapper>
 
機智的小伙伴可能已經(jīng)看出了問題,在眾多 mapper 中發(fā)現(xiàn)了一個相同的想象,幾乎所有的 mapper 中都包含了一個無用的拼接 SQL:where 1=1。作為一個幾乎有代碼潔癖癥的人,自然是忍不住動手改造一番了。
錯誤的改造方式
既然是去掉 where 1=1,那最簡單的方式就是將它直接從代碼中刪除了,如下代碼所示:
- <?xml version="1.0" encoding="UTF-8"?>
 - <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 - <mapper namespace="com.example.demo.mapper.UserMapper">
 - <select id="list" resultType="com.example.demo.model.User">
 - select * from user
 - where
 - <if test="name!=null">
 - name=#{name}
 - </if>
 - <if test="password!=null">
 - and password=#{password}
 - </if>
 - </select>
 - </mapper>
 
以上代碼刪除了 1=1,并且把第一個 name 查詢中的 and 去掉了,以防 SQL 查詢報錯。
但這樣就沒問題了嗎?我們直接來看結(jié)果,當(dāng)包含參數(shù) name 查詢時,結(jié)果如下:
一切順理成章,完美的一塌糊涂。
然而,當(dāng)省略 name 參數(shù)時(因為 name 為非必要參數(shù),所以可以省略),竟然引發(fā)了以下異常:
又或者只有 password 查詢時,結(jié)果也是一樣:
都是報錯信息,那腫么辦呢?難不成把 1=1 恢復(fù)回去?
正確的改進(jìn)方式
其實不用,在 MyBatis 中早已經(jīng)想到了這個問題,我們可以將 SQL 中的 where 關(guān)鍵字換成 MyBatis 中的標(biāo)簽,并且給每個標(biāo)簽內(nèi)都加上 and 拼接符,這樣問題就解決了,如下代碼所示:
- <?xml version="1.0" encoding="UTF-8"?>
 - <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 - <mapper namespace="com.example.demo.mapper.UserMapper">
 - <select id="list" resultType="com.example.demo.model.User">
 - select * from user
 - <where>
 - <if test="name!=null">
 - and name=#{name}
 - </if>
 - <if test="password!=null">
 - and password=#{password}
 - </if>
 - </where>
 - </select>
 - </mapper>
 
代碼改造完成之后,接下來我們來測試一下所有的請求場景。
不傳任何參數(shù)的請求
此時我們可以不傳遞任何參數(shù)(查詢所有數(shù)據(jù)),如下圖所示:圖片生成的 SQL 語句如下:圖片
傳遞 1 個參數(shù)的請求
也可以傳遞 1 個參數(shù),根據(jù) name 進(jìn)行查詢,如下圖所示:圖片生成的 SQL 如下圖所示:圖片也可以只根據(jù) password 進(jìn)行查詢,如下圖所示:圖片生成的 SQL 如下圖所示:
傳遞 2 個參數(shù)的請求
也可以根據(jù) name 加 password 的方式進(jìn)行聯(lián)合查詢,如下圖所示:
生成的 SQL 如下圖所示:
用法解析
我們驚喜的發(fā)現(xiàn),在使用了標(biāo)簽之后,無論是任何查詢場景,傳一個或者傳多個參數(shù),或者直接不傳遞任何參數(shù),都可以輕松搞定。
首先,標(biāo)簽會判斷,如果沒有任何參數(shù),則不會在 SQL 語句中拼接 where 查詢,反之才會拼接 where 查詢;其次在查詢的標(biāo)簽中,每個標(biāo)簽都可以加 and 關(guān)鍵字,MyBatis 會自動將第一個條件前面的 and 關(guān)鍵字刪除掉,從而不會導(dǎo)致 SQL 語法錯誤,這一點官方文檔中也有說明,如下圖所示:
總結(jié)
在 MyBatis 中,建議盡量避免使用無意義的 SQL 拼接 where 1=1,我們可以使用標(biāo)簽來替代 where 1=1,這樣的寫既簡潔又優(yōu)雅,何樂而不為呢?






















 
 
 









 
 
 
 