mybatis中${}和#{}取值的區(qū)別分析
mybatis作為一個輕量級的ORM框架,應(yīng)用廣泛,其上手使用也比較簡單;一個成熟的框架,必然有精巧的設(shè)計,值得學(xué)習(xí)。
在使用mybatis框架時,在sql語句中獲取傳入的參數(shù)有如下兩種方式:
- ${paramName}
- #{paramName}
那如何理解這兩種傳參方式呢?如下帶你走近背后的奧義。
先來回顧下原生Jdbc查詢:
public static void main(String[] args) throws Exception { // sql語句 String sql = "select id,name from customer limit 2"; // 1.加載驅(qū)動, 此處使用的mysql驅(qū)動包是8.0版本, 若為5.0+版本, 請修改以下類路徑 Class.forName("com.mysql.cj.jdbc.Driver"); // 2.獲取數(shù)據(jù)庫連接 String url = "jdbc:mysql://localhost:3306/work?useSSL=false&useUnicode=true" + "&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true" + "&useLegacyDatetimeCode=false&serverTimezone=UTC"; Connection conn = DriverManager.getConnection(url,"root", "123456"); // 3、獲得可以執(zhí)行sql語句的對象 Statement st = conn.createStatement(); // 4、使用對象去執(zhí)行SQL語句 ResultSet rs = st.executeQuery(sql); // 5、處理sql語句返回的結(jié)果集 while(rs.next()){ // 獲得一行數(shù)據(jù) Integer id = rs.getInt("id"); String name = rs.getString("name"); System.out.println("sql查詢: id = " + id + " , name = " + name); } // 6、釋放資源 rs.close(); st.close(); conn.close(); }
控制臺打?。?/p>
sql查詢: id = 1 , name = 李白
sql查詢: id = 2 , name = 杜甫
了解Jdbc的人會知道,其中第3、4步兩條語句也可以換成如下兩條:
// 3.創(chuàng)建 PreparedStatement 對象去執(zhí)行sql PreparedStatement preparedStatement = conn.prepareStatement(sql); // 4.執(zhí)行sql語句 ResultSet rs = preparedStatement.executeQuery();
我們來比較下區(qū)別:
- 創(chuàng)建 PreparedStatement 對象時就把sql語句傳入,在執(zhí)行語句時就不用傳入sql了;而 Statement 則剛好相反
這就引出了預(yù)編譯的概念:
- 如果使用PreparedStatement 對象,那么在執(zhí)行第3步時,你既然已經(jīng)傳入了sql,則相當(dāng)于這條sql會被數(shù)據(jù)庫編譯(數(shù)據(jù)庫對sql語句的編譯也是相當(dāng)復(fù)雜的),所以在第4步執(zhí)行的時候就不用再傳入sql了,因為數(shù)據(jù)庫已經(jīng)知道你要執(zhí)行的sql了,你只需要傳入?yún)?shù)即可;
- 如果使用Statement對象,那容易理解,數(shù)據(jù)庫就沒有提前去解析你的sql,因為你創(chuàng)建對象時都沒有傳入;當(dāng)執(zhí)行sql時,數(shù)據(jù)庫再編譯與執(zhí)行。
看到這里,可能也僅僅只記住了一個預(yù)先編譯sql了,一個沒有預(yù)先編譯,并沒有了解到對于實際開發(fā)中的區(qū)別,以下將會舉例說明。
那是否PreparedStatement對象這種方式就一定比Statement對象方式好?
沒有那么絕對的事,大家要理解:
PreparedStatement對象的好處是,sql已經(jīng)提前編譯好,剩下的工作就是傳入?yún)?shù)即可,編譯好的sql可以復(fù)用,傳入不同的參數(shù),則數(shù)據(jù)庫就將相應(yīng)的參數(shù)填入編譯好的sql。而Statement對象就是每次都要傳入sql,丟給數(shù)據(jù)庫去編譯再執(zhí)行;但是創(chuàng)建PreparedStatement對象的開銷是比Statement對象大的。
回歸到日常開發(fā)中,以上的區(qū)別我們壓根也不用在意,事實上,百分之九十的場景我們使用的是PreparedStatement對象的方式,可能平時沒有感知到,因為這是框架已經(jīng)封裝了。再者,當(dāng)系統(tǒng)出現(xiàn)性能問題時,也絕對不會是因為這兩個對象的原因。
以上簡單回顧了下Jdbc中PreparedStatement與Statement對象;
可以預(yù)料,mybatis中${} 與 #{} 這兩種取值方式就是相當(dāng)于對應(yīng)著PreparedStatement和Statement對象的區(qū)別了。
- #{} 傳參,代表sql已經(jīng)預(yù)編譯好了,你傳入的參數(shù)真的就僅僅是參數(shù)!
- ${} 傳參,隨便你傳,傳完了之后我再統(tǒng)一編譯
那具體在使用中有什么不同呢?理解如下兩種場景:
1.看如下service和sql語句
@Override public List<Map<String, Object>> listUser() { String param = " and name = '李白'"; return indexMapper.listUser(param); } <select id="listUser" resultType="map"> select * from customer where 1 = 1 #{param} </select>
以上代碼能正常查詢嗎?
### Error querying database. Cause: java.sql.SQLSyntaxErrorException: You have
an error in your SQL syntax; check the manual that corresponds to your MySQL
server version for the right syntax to use near '' and name = \'李白\''
不能!會報sql語句規(guī)則錯誤,之前說了,#{} 取值代表sql已經(jīng)編譯好了,你傳入的僅僅是參數(shù)
對應(yīng)上面示例:
sql是指:select * from customer where 1 = 1
參數(shù)是指:and name = '李白'
此時sql可以正確運行,但是帶上傳入的參數(shù)就不行了,要理解你傳入的真的僅僅是參數(shù),不要和前面的sql混了。
但是這明顯不對,因為想表達(dá)的其實是這樣:
sql是指:select * from customer where 1 = 1 and name = ?
參數(shù)是指:'李白'
此時把參數(shù)替換進占位符是可以正常運行整個語句的。
所以此時應(yīng)該用 ${param},因為用${}就沒有提前編譯好哪些是屬于sql。
此示例表明:當(dāng)你傳入的參數(shù)不僅僅是參數(shù),其實是一小段sql,想和原sql拼接在一起時,那就得用${}傳參,相當(dāng)于拼接好了之后丟給
數(shù)據(jù)庫去解析整個語句;
sql中的問號代表參數(shù)占位符,這也是PreparedStatement對象特點之一,會將你傳入的參數(shù)一一替換進占位符
反之如下:
@Override public List<Map<String, Object>> listUser() { String param = "李白"; return indexMapper.listUser(param); } <select id="listUser" resultType="map"> select * from customer where 1 = 1 and name = #{param} </select>
這種情況使用 #{} 就是對的了,因為傳入的參數(shù)僅僅就是參數(shù),替換進sql語句中即可。
2.對參數(shù)類型的影響
@Override public List<Map<String, Object>> listUser() { String param = "李白"; return indexMapper.listUser(param); } <select id="listUser" resultType="map"> select * from customer where 1 = 1 and name = ${param} </select>
以上代碼能執(zhí)行成功嗎?
按理說,傳入的僅僅是參數(shù),不管是否預(yù)編譯都應(yīng)該能執(zhí)行,但是實際還是會報錯。
這是執(zhí)行時打印出的sql語句:
select * from customer where 1 = 1 and name = 李白
顯然,問題就在于參數(shù)沒有加單引號,name字段是字符串類型,傳入的也是字符串,偏偏mybatis轉(zhuǎn)換之后沒有加單引號。
所以當(dāng)傳入字符串類型參數(shù)時,應(yīng)該用 #{} 取值,此時會自動加上單引號。
再看下面這種語句:
@Override public List<Map<String, Object>> listUser() { String param = "name"; return indexMapper.listUser(param); } <select id="listUser" resultType="map"> select * from customer where 1 = 1 order by ${param} desc </select>
此時傳入的參數(shù)是要排序的字段名稱,之前說了,如果采用#{} 取值,則實際是會自動加上單引號的,但是order by后面的排序字段需要單引號嗎?
不需要,所以這種情況只能使用 ${} 取值。
你可能會發(fā)現(xiàn)此處用 #{} 取值也不會報錯,那是因為mysql支持這種寫法,但是查詢的結(jié)果并不對。
日常開發(fā)中,只要能理解上述兩種情形,那么就能正確使用 ${} 和 #{},由于這兩種方式取值原理的區(qū)別,也容易明白 #{} 這種方式是可以防止sql注入的。
總結(jié)
到此這篇關(guān)于mybatis中${}和#{}取值區(qū)別分析的文章就介紹到這了,更多相關(guān)mybatis ${}和#{}取值區(qū)別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis中#{}?和?${}?的區(qū)別和動態(tài)?SQL詳解
- mybatis中${}和#{}的區(qū)別以及底層原理分析
- MyBatis #{}和${} |與數(shù)據(jù)庫連接池使用詳解
- MyBatis中使用#{}和${}占位符傳遞參數(shù)的各種報錯信息處理方案
- Mybatis關(guān)于動態(tài)排序 #{} ${}問題
- mybatis中#{}和${}的區(qū)別詳解
- MyBatis中#{}和${}有哪些區(qū)別
- MyBatis中#{}占位符與${}拼接符的用法說明
- 詳解Mybatis中的 ${} 和 #{}區(qū)別與用法
- Mybatis之#{}與${}的區(qū)別使用詳解
- Mybatis中#{}與${}的區(qū)別詳解
- MyBatis中 #{} 和 ${} 的區(qū)別小結(jié)
相關(guān)文章
java 運行報錯has been compiled by a more recent version of the J
java 運行報錯has been compiled by a more recent version of the Java Runtime (class file version 54.0)2021-04-04SpringBoot 配合 SpringSecurity 實現(xiàn)自動登錄功能的代碼
這篇文章主要介紹了SpringBoot 配合 SpringSecurity 實現(xiàn)自動登錄功能的代碼,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-09-09Spring Boot緩存實戰(zhàn) EhCache示例
本篇文章主要介紹了Spring Boot緩存實戰(zhàn) EhCache示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08SpringBoot+阿里云OSS實現(xiàn)在線視頻播放的示例
這篇文章主要介紹了SpringBoot+阿里云OSS實現(xiàn)在線視頻播放的示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11利用Spring Social輕松搞定微信授權(quán)登錄的方法示例
這篇文章主要介紹了利用Spring Social輕松搞定微信授權(quán)登錄的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-12-12Java定時任務(wù)實現(xiàn)優(yōu)惠碼的示例代碼
在Java中實現(xiàn)定時任務(wù)來發(fā)放優(yōu)惠碼,我們可以使用多種方法,比如使用java.util.Timer類、ScheduledExecutorService接口,或者更高級的框架如Spring的@Scheduled注解,這篇文章主要介紹了Java定時任務(wù)實現(xiàn)優(yōu)惠碼的實例,需要的朋友可以參考下2024-07-07Java反射通過Getter方法獲取對象VO的屬性值過程解析
這篇文章主要介紹了Java反射通過Getter方法獲取對象VO的屬性值過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-02-02將BigDecimal轉(zhuǎn)成字符串為科學(xué)計數(shù)法的踩坑記錄
這篇文章主要介紹了將BigDecimal轉(zhuǎn)成字符串為科學(xué)計數(shù)法的踩坑記錄,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-06-06