Statement 和 PreparedStatement關係分析

什麼是預編譯?

當客戶發送一條SQL語句給服務器後,服務器總是需要校驗SQL語句的語法格式是否正確,
然後把SQL語句編譯成可執行的函數,最後纔是執行SQL語句。
其中校驗語法,和編譯所花的時間可能比執行SQL語句花的時間還要多。

注意:
可執行函數存儲在MySQL服務器中,並且當前連接斷開後,MySQL服務器會清除已經存儲的可執行函數。
如果我們需要執行多次insert語句,但只是每次插入的值不同,
MySQL服務器也是需要每次都去校驗SQL語句的語法格式,以及編譯,這就浪費了太多的時間。
如果使用預編譯功能,那麼只對SQL語句進行一次語法校驗和編譯,所以效率要高。

MySQL執行預編譯

MySQL執行預編譯分爲如三步:

1. 執行預編譯語句,例如:prepare showUsersByLikeName from 'select * from user where username like ?';
2. 設置變量,例如:set @username='%小明%';
3. 執行語句,例如:execute showUsersByLikeName using @username;

如果需要再次執行myfun,那麼就不再需要第一步,即不需要再編譯語句了:

1. 設置變量,例如:set @username='%小宋%';
2. 執行語句,例如:execute showUsersByLikeName using @username;

 

在PreparedStatement中開啓預編譯功能

設置MySQL連接URL參數:useServerPrepStmts=true,
jdbc:mysql://localhost:3306/mybatis?&useServerPrepStmts=true
這樣才能保證mysql驅動會先把SQL語句發送給服務器進行預編譯,然後在執行executeQuery()時只是把參數發送給服務器。

注意:

我們設置的是MySQL連接參數,目的是告訴MySQL JDBC的PreparedStatement使用預編譯功能(5.0.5之後的JDBC驅動版本需要手動開啓,而之前的默認是開啓的),不管我們是否使用預編譯功能,MySQL Server4.1版本以後都是支持預編譯功能的。

 

 

Statement 和 PreparedStatement之間的關係和區別.
    關係:PreparedStatement繼承自Statement,都是接口
    區別:PreparedStatement可以使用佔位符,是預編譯的,批處理比Statement效率高   

1. Statement

每次都會執行SQL語句,相關數據庫都要執行SQL語句的編譯。
Statement爲一條Sql語句生成執行計劃,
如果要執行兩條sql語句
select colume from table where colume=1;
select colume from table where colume=2;
會生成兩個執行計劃
一千個查詢就生成一千個執行計劃!

2. PreparedStatement

用於處理動態SQL語句,在執行前會有一個預編譯過程,這個過程是有時間開銷的,雖然相對數據庫的操作,該時間開銷可以忽略不計,  
但是PreparedStatement的預編譯結果會被緩存,下次執行相同的預編譯語句時,就不需要編譯,只要將參數直接傳入編譯過的語句執行代碼  
中就會得到執行,所以,對於批量處理可以大大提高效率。
   (1) PreparedStatement接口繼承Statement,PreparedStatement  實例包含已編譯的 SQL 語句,  
       所以其執行速度要快於 Statement 對象。
   (2)作爲 Statement 的子類,PreparedStatement 繼承了 Statement 的所有功能。  
      三種方法**execute**、 **executeQuery** 和 **executeUpdate** 已被更改以使之不再需要參數

3:應該儘可能以PreparedStatement代替Statement,其原因如下: 
(1)代碼的可讀性和可維護性。

當sql語句冗長的時候,Statement方式的 sql 拼接會很麻煩,容易出錯。

而PreparedStatement方式則不會出現上述問題,利用佔位符可以很方便的解決 sql 字符串的拼接問題,相對的對佔位符的賦值過程就多與Statement方式,但是這樣修改程序結構比較明瞭,易於維護。

(2)性能。

PreparedStatement在執行之前進行了一次預編譯,sql 語句執行的時候只需要將數據替換就可以了,不需要每次執行都進行編譯,尤其是批處理的時候優化效果比較明顯。而Statement處理的時候每次都要重新制定一次執行計劃,將sql 進行一次編譯,該過程會降低SQL效率。

(3)安全性。

在使用preparedStatement對象執行sql時候命令被數據庫編譯和解析,然後被放到命令緩衝區,然後每當執行同一個preparedStatement時候,他就被再解析一次,但不會在編譯,在緩衝區中可以發現預編譯的命令,並且可以重新使用。

所以用戶數據不會參與編譯,只要把數據替換佔位符即可,保證了數據庫的安全性。

而Statement只是單純的sql拼接,用戶數據會參與到編譯過程中去,對於用戶的惡意sql注入,會對數據庫帶來很大的危害。

 

羅列出PreparedStatement對象在初級開發中幾個常用的方法:

1). PreparedStatement.execute()方法:

該語句可以是任何種類的 SQL 語句 
execute 方法返回一個 boolean 值,以指示第一個結果的形式。必須調用 getResultSet 或 getUpdateCount 方法來檢索結果,並且必須調用 getMoreResults 移動到任何後面的結果。 
返回: 
如果第一個結果是 ResultSet 對象,則返回 true;如果第一個結果是更新計數或者沒有結果,則返回 false 
意思就是如果是查詢的話返回true,如果是更新或插入的話就返回false了;

2). PreparedStatement.executeQuery()方法: 
返回執行查詢語句後得到的ResultSet結果集,注意:該結果集永遠不能爲null。

3). PreparedStatement.executeUpdate()方法: 
返回一個int類型的值,該值代表執行INSERT、DELETE以及UPDATE語句後的更新行數。 
如果該值爲0,則代表SQL語句沒有執行成功。 
4).PreparedStatement.addBatch(); executeBatch()方法: 
將一組參數添加到此 PreparedStatement 對象的批處理命令中。緩存sql語句,然後批量執行! 
將一批命令提交給數據庫來執行,如果全部命令執行成功,則返回更新計數組成的數組。

 

總結:

  • PreparedStatement的預編譯是數據庫進行的,編譯後的函數key是緩存在PreparedStatement中的,編譯後的函數是緩存在數據庫服務器中的。預編譯前有檢查sql語句語法是否正確的操作。只有數據庫服務器支持預編譯功能時,JDBC驅動才能夠使用數據庫的預編譯功能,否則會報錯。預編譯在比較新的JDBC驅動版本中默認是關閉的,需要配置連接參數才能夠打開。在已經配置好了數據庫連接參數的情況下,Statement對於MySQL數據庫是不會對編譯後的函數進行緩存的,數據庫不會緩存函數,Statement也不會緩存函數的key,所以多次執行相同的一條sql語句的時候,還是會先檢查sql語句語法是否正確,然後編譯sql語句成函數,最後執行函數。
  • PreparedStatement源碼在設置參數的時候會對用戶參數參數進行特殊字符轉義處理。
  • 因爲PreparedStatement已經對sql模板進行了編譯,並且存儲了函數,所以PreparedStatement做的就是把參數進行轉義後直接傳入參數到數據庫,然後讓函數執行。這就是爲什麼PreparedStatement能夠防止sql注入攻擊的原因了。
  • PreparedStatement的預編譯還有注意的問題,在數據庫端存儲的函數和在PreparedStatement中存儲的key值,都是建立在數據庫連接的基礎上的,如果當前數據庫連接斷開了,數據庫端的函數會清空,建立在連接上的PreparedStatement裏面的函數key也會被清空,各個連接之間的預編譯都是互相獨立的。

在持久層框架中存在的問題

很多主流持久層框架(MyBatis,Hibernate)其實都沒有真正的用上預編譯,預編譯是要我們自己在參數列表上面配置的,如果我們不手動開啓,JDBC驅動程序5.0.5以後版本 默認預編譯都是關閉的。

所以我們要在參數列表中配置,例如:

jdbc:mysql://localhost:3306/mybatis?&useServerPrepStmts=true&cachePrepStmts=true

 

引用文章:

https://blog.csdn.net/marvel__dead/article/details/69486947

https://blog.csdn.net/weixin_36586564/article/details/71213206

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章