Mybatis參數預編譯


Mybatis參數預編譯

一、數據庫預編譯介紹

1.數據庫SQL語句編譯特性:

數據庫接受到sql語句之后,需要詞法和語義解析,優化sql語句,制定執行計划。這需要花費一些時間。但是很多情況,我們的一條sql語句可能會反復執行,或者每次執行的時候只有個別的值不同(比如query的where子句值不同,update的set子句值不同,insert的values值不同)。

2.減少編譯的方法

如果每次都需要經過上面的詞法語義解析、語句優化、制定執行計划等,則效率就明顯不行了。為了解決上面的問題,於是就有了預編譯,預編譯語句就是將這類語句中的值用占位符替代,可以視為將sql語句模板化或者說參數化。一次編譯、多次運行,省去了解析優化等過程。

3.緩存預編譯

預編譯語句被DB的編譯器編譯后的執行代碼被緩存下來,那么下次調用時只要是相同的預編譯語句就不需要編譯,只要將參數直接傳入編譯過的語句執行代碼中(相當於一個涵數)就會得到執行。
並不是所以預編譯語句都一定會被緩存,數據庫本身會用一種策略(內部機制)。

4.預編譯的實現方法

預編譯是通過PreparedStatement和占位符來實現的。

二、預編譯作用

1.預編譯階段可以優化 sql 的執行

預編譯之后的 sql 多數情況下可以直接執行,DBMS 不需要再次編譯,越復雜的sql,編譯的復雜度將越大,預編譯階段可以合並多次操作為一個操作。可以提升性能。

2.防止SQL注入

使用預編譯,而其后注入的參數將不會再進行SQL編譯。也就是說其后注入進來的參數系統將不會認為它會是一條SQL語句,而默認其是一個參數,參數中的or或者and 等就不是SQL語法保留字了。

三、預編譯開啟

1.數據庫是否默認開啟預編譯和JDBC版本有關。

也可以配置jdbc鏈接時強制開啟預編譯和緩存:useServerPrepStmts和cachePrepStmts參數。預編譯和預編譯緩存一定要同時開啟或同時關閉。否則會影響執行效率

conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/prepare_stmt_test?user=root&password=root&useServerPrepStmts=true&cachePrepStmts=true");  

2.mysql的預編譯

  • 開啟了預編譯緩存后,connection之間,預編譯的結果是獨立的,是無法共享的,一個connection無法得到另外一個connection的預編譯緩存結果。
  • 經過試驗,mysql的預編譯功能對性能影響不大,但在jdbc中使用PreparedStatement是必要的,可以有效地防止sql注入。
  • 相同PreparedStatement的對象 ,可以不用開啟預編譯緩存。
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/prepare_stmt_test?user=root&password=root&useServerPrepStmts=true");  
PreparedStatement stmt = conn.prepareStatement(sql);  
stmt.setString(1, "aaa");  
ResultSet rs1 = stmt.executeQuery();//第一次執行  
s1.close();  
stmt.setString(1, "ddd");  
ResultSet rs2 = stmt.executeQuery();//第二次執行  
rs2.close();  
stmt.close(); 
//查看mysql日志
1 Prepare          select * from users where name = ?
    1 Execute          select * from users where name = 'aaa'
    1 Execute          select * from users where name = 'ddd'

四、mybatis是如何實現預編譯的

mybatis 默認情況下,將對所有的 sql 進行預編譯。mybatis底層使用PreparedStatement,過程是先將帶有占位符(即”?”)的sql模板發送至mysql服務器,由服務器對此無參數的sql進行編譯后,將編譯結果緩存,然后直接執行帶有真實參數的sql。核心是通過#{ } 實現的
在預編譯之前,#{ } 解析為一個 JDBC 預編譯語句(prepared statement)的參數標記符?。

//sqlMap 中如下的 sql 語句
select * from user where name = #{name};
//解析成為預編譯語句
select * from user where name = ?;

如果${ },SQL 解析階段將會進行變量替換。不能實現預編譯。

select * from user where name = '${name}'
//傳遞的參數為 "ruhua" 時,解析為如下,然后發送數據庫服務器進行編譯。
select * from user where name = "ruhua";


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM