Statement和PreparedStatement深入学习总结
最近在看java安全編碼方面的書籍,在看到SQL注入漏洞的問題時,引發了我對Statement和PreparedStatement深入總結的欲望,廢話少說,下面咱們就正式開始。
當初始的SQL查詢被修改成另一個完全不同形式的查詢的時候,就會出現SQL注入漏洞。執行這一被修改過的查詢就可能會導致信息泄露或者數據被修改。防止SQL注入漏洞的主要方法是:凈化并驗證非受信輸入,同時采用參數化查詢的方法。
//創建數據庫連接的過程略String sql="select * from db_user where username = " + username + "and passord = " + password;Statement stmt = connect.createStatement();ResultSet rs = stmt.executeQuery(sql);采用以上方法實現數據查詢時,如果攻擊者能夠替代username和password中的任意字符串,它們可以使用類似下面這種方式實現SQL注入:
select * from db_user where username = '隨意' and password = '' or '1' = '1';因為’1’=’1’肯定成立,所以可以任何通過驗證.更有甚者:把[‘;drop table tb_name;]作為password傳入進來,則:
select * from db_user where username = '隨意' and password = '';drop table tb_name;有些數據庫是不會讓你成功的,但也有很多數據庫就可以使這些語句得到執行。而如果你使用預編譯語句。你傳入的任何內容就不會和原來的語句發生任何匹配的關系。(前提是數據庫本身支持預編譯,但上前可能沒有什么服務端數據庫不支持編譯了,只有少數的桌面數據庫,就是直接文件訪問的那些)只要全使用預編譯語句,你就用不著對傳入的數據做任何過濾。而如果使用普通的statement,有可能要對drop,等做費盡心機的判斷和過濾。
改進方法:
//創建數據庫連接的過程略String sql="select * from db_user where username = ? and passord = ?";PreparedStatement stmt = connect.preparedStatement();stmt.setString(1,username);stme.setString(2,password);ResultSet rs = stmt.executeQuery(sql);通過使用PreparedStatement類的set*()方法,可以進行強類型檢查。這樣可以減少SQL注入漏洞。
一、分情況使用Statement和PreparedStatement對象
JDBC驅動的最佳化是基于使用的是什么功能. 選擇PreparedStatement還是Statement取決于你要怎么使用它們. 對于只執行一次的SQL語句選擇Statement是最好的. 相反, 如果SQL語句被多次執行選用PreparedStatement是最好的.
PreparedStatement的第一次執行消耗是很高的. 它的性能體現在后面的重復執行. 使用PreparedStatement的方式來執行一個針對數據庫表的查詢. JDBC驅動會發送一個網絡請求到數據解析和優化這個查詢. 而執行時會產生另一個網絡請求. 在JDBC驅動中,減少網絡通訊是最終的目的. 如果我的程序在運行期間只需要一次請求, 那么就使用Statement. 對于Statement, 同一個查詢只會產生一次網絡到數據庫的通訊.
對于使用PreparedStatement池的情況下,當使用PreparedStatement池時, 如果一個查詢很特殊, 并且不太會再次執行到, 那么可以使用Statement. 如果一個查詢很少會被執行,但連接池中的Statement池可能被再次執行, 那么請使用PreparedStatement. 在不是Statement池的同樣情況下, 請使用Statement.
二、PreparedStatement的Batch功能
Update大量的數據時, 先構建一個INSERT語句再多次的執行, 會導致很多次的網絡連接.。要減少JDBC的調用次數改善性能, 可以使用PreparedStatement的AddBatch()方法一次性發送多個查詢給數據庫。
初始實現:PreparedStatement ps = conn.prepareStatement( "INSERT into db_user values (?, ?, ?)"); for (n = 0; n < 100; n++) { ps.setString(name[n]); ps.setLong(id[n]); ps.setInt(salary[n]); ps.executeUpdate(); } 改進實現://使用Batch功能 PreparedStatement ps = conn.prepareStatement( "INSERT into db_user values (?, ?, ?)"); for (n = 0; n < 100; n++) { ps.setString(username[n]); ps.setString(password[n]); ps.addBatch(); } ps.executeBatch();在初始實現中, PreparedStatement被用來多次執行INSERT語句。 在這里,執行了100次INSERT操作, 共有101次網絡往返。 其中,1次往返是預儲statement, 另外100次往返執行每個迭代。 在改進實現中, 當在100次INSERT操作中使用addBatch()方法時, 只有兩次網絡往返。 1次往返是預儲statement, 另一次是執行batch命令。雖然Batch命令會用到更多的數據庫的CPU周期, 但是通過減少網絡往返,性能得到提高。 記住, JDBC的性能最大的增進是減少JDBC驅動與數據庫之間的網絡通訊。
注:Oracel 10G的JDBC Driver限制最大Batch size是16383條,如果addBatch超過這個限制,那么executeBatch時就會出現“無效的批值”(Invalid Batch Value) 異常。因此在如果使用的是Oracle10G,在此bug減少前,Batch size需要控制在一定的限度。
超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術人生總結
以上是生活随笔為你收集整理的Statement和PreparedStatement深入学习总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Java并发编程】java并发框架Ex
- 下一篇: 具备白名单、黑名单的Filter基类扩展