JDBC程式設計之預編譯SQL與防注入

2021-09-28 01:33:42 字數 3868 閱讀 2741

在jdbc程式設計中,常用statement、preparedstatement 和 callablestatement三種方式來執行查詢語句,其中 statement 用於通用查詢, preparedstatement 用於執行引數化查詢,而 callablestatement則是用於儲存過程。

1、statement 

該物件用於執行靜態的 sql 語句,並且返回執行結果。 此處的sql語句必須是完整的,有明確的資料指示。查的是哪條記錄?改的是哪條記錄?都要指示清楚。

通過呼叫 connection 物件的 createstatement 方法建立該物件 

查詢:resultset excutequery(string sql)——返回查詢結果的封裝物件resultset. 用next()遍歷結果集,getxx()獲取記錄資料。

修改、刪除、增加:int excuteupdate(string sql)——返回影響的資料表記錄數. 

2、preparedstatement 

sql 語句被預編譯並儲存在 preparedstatement 物件中。然後可以使用此物件多次高效地執行該語句。 

可以通過呼叫 connection 物件的 preparedstatement() 方法獲取 preparedstatement 物件 

preparedstatement 物件所執行的 sql 語句中,引數用問號(?)來表示,呼叫 preparedstatement 物件的 set***() 方法來設定這些引數. set***() 方法有兩個引數,第乙個引數是要設定的 sql 語句中的引數的索引(從 1 開始),第二個是設定的 sql 語句中的引數的值,注意用set***方式設定時,需要與資料庫中的字段型別對應,例如mysql中字段為varchar,就需要使用setstring方法,如果為date型別,就需要使用setdate方法來設定具體sql的引數。

簡單來說就是,預編譯的sql語句不是有具體數值的語句,而是用(?)來代替具體資料,然後在執行的時候再呼叫setxx()方法把具體的資料傳入。同時,這個語句只在第一次執行的時候編譯一次,然後儲存在快取中。之後執行時,只需從快取中抽取編譯過了的**以及新傳進來的具體資料,即可獲得完整的sql命令。這樣一來就省下了後面每次執行時語句的編譯時間。

使用預編譯分4步走:

1:定義預編譯的sql語句,其中待填入的引數用  ?  佔位。注意,?無關型別,不需要加分號之類。其具體資料型別在下面setxx()時決定。

2:建立預編譯statement,並把sql語句傳入。此時sql語句已與此preparedstatement繫結。所以第4步執行語句時無需再把sql語句作為引數傳入execute()。

3:填入具體引數。通過setxx(問號下標,數值)來為sql語句填入具體資料。注意:問號下標從1開始,setxx與數值型別有關,字串就是setstring(index,str).

4:執行預處理物件。主要有:

booleanexecute()

在此preparedstatement物件中執行 sql 語句,該語句可以是任何種類的 sql 語句。

resultset

executequery()

在此preparedstatement物件中執行 sql 查詢,並返回該查詢生成的resultset物件。

intexecuteupdate()

在此preparedstatement物件中執行 sql 語句,該語句必須是乙個 sql 資料操作語言(data manipulation language,dml)語句,比如insertupdatedelete語句;或者是無返回內容的 sql 語句,比如 ddl 語句。

注意,前面建立preparedstatement時已經把sql語句傳入了,此時執行不需再把sql語句傳入,這是與一般statement執行sql語句所不同之處。

比如:string sql="select sname from stu where sno=?"

preparedstatement prestmt = conn.preparestatement(sql);

prestmt.setstring(1,sno);

prestmt.executequery();

使用預編譯的好處:

1:preparedstatementstatement更快

使用preparedstatement最重要的一點好處是它擁有更佳的效能優勢,sql語句會預編譯在資料庫系統中。執行計畫同樣會被快取起來,它允許資料庫做引數化查詢。使用預處理語句比普通的查詢更快,因為它做的工作更少(資料庫對sql語句的分析,編譯,優化已經在第一次查詢前完成了)。

2:preparedstatement可以防止sql注入式攻擊

sql 注入攻擊:sql 注入是利用某些系統沒有對使用者輸入的資料進行充分的檢查,而在使用者輸入資料中注入非法的 sql 語句段或命令,從而利用系統的 sql 引擎完成惡意行為的做法。

1

strsql ="select * from users where name = '"+ username +"' and pw = '"+ password +"';"

惡意填入:

1

2

username ="1' or '1'='1";

password ="1' or '1'='1";

那麼最終sql語句變成了:

1

strsql ="select * from users where name = '1' or '1'='1' and pw = '1' or '1'='1';"

因為where條件恒為真,這就相當於執行:

1

strsql ="select * from users;"

因此可以達到無賬號密碼亦可登入**。

如果惡意使用者要是更壞一點,sql語句變成: 1

strsql ="select * from users where name = 'any_value' and pw = ''; drop table users"

這樣一來,雖然沒有登入,但是資料表都被刪除了。

使用preparedstatement的引數化的查詢可以阻止大部分的sql注入。在使用引數化查詢的情況下,資料庫系統不會將引數的內容視為sql指令的一部分來處理,而是在資料庫完成sql指令的編譯後,才套用引數執行,因此就算引數中含有破壞性的指令,也不會被資料庫所執行。因為對於引數化查詢來說,查詢sql語句的格式是已經規定好了的,需要查的資料也設定好了,缺的只是具體的那幾個資料而已。所以使用者能提供的只是資料,而且只能按需提供,無法更進一步做出影響資料庫的其他舉動來。

JDBC預編譯效能測試

從網上了解到的資料,preparestatement預設情況下是沒有開啟jdbc的預編譯功能的,也沒有開啟mysql的預編譯功能 也就是說,在效能上,與傳統的statement沒什麼差別。這簡直是顛覆了我的認知,我以為preparestatement是開啟了預編譯的,效能會有所提公升,就來進行測試,...

泛化程式設計與預編譯

為實現 的可移植性和可重用性,c語言引入預編譯指令。預編譯指令以符號 開始,可以出現在源程式的任何位置,其作用範圍是從出現位置到檔案尾。define 指令用於巨集定義 指令格式 define 符號常量名 替換文字 沒有分號 符號常量名也稱巨集名,習慣用大寫字母表示。替換文字可以是c語言允許的識別符號...

預編譯與編譯

一c c 源 從最初的文字變為可執行檔案主要進行三大步 預編譯階段 主要是編譯器執行 文字處理工作,並不會進行語法檢查 主要執行三大類預編譯命令 巨集定義 文字替換功能,將使用了巨集的地方採取巨集定義方式直接展開 條件編譯 文字剪下功能,根據設定的條件選擇性刪除一些 片段 包含檔案 文字插入功能 i...