【前言】在上一篇文章中,我們說到了JDBC在操作資料庫的同時,需要建立執行對象。對象分為:Statement和PrepareStatement兩種,那麼這兩者之間,又有何種淵源呢?
剛一開始,不了解的時候,去網上看,上邊将PrepareStatement的逼格吹得很高,比如說:在JDBC應用中,如果你已經是稍有水準的開發者,你就應該始終以PrepareStatement代替Statement。也就是說,在任何時候都不要使用Statement。看到這裡,小編内心不禁暗暗的下定決心,以後也要使用PrepareStatement。
那麼,在真實的開發中,它們兩者又是孰優孰劣呢?
上一篇,在JDBC建立對象并執行SQL語句的時候,我們可以知道,兩者在這方面的差別:
<span style="font-family:Microsoft YaHei;font-size:14px;"> //建立語句執行對象
//需要執行的sql語句
String sql = "select user_name,password from t_user where user_id=?";
//使用Statement對象
Statement st=conn.createStatement();
//使用PrepareStatement對象
PreparedStatement pst = conn.createStatement(sql);
//執行語句
//使用Statement對象
st.executeQuery("sql");
//使用PrepareStatement對象
pst.executeQuery(); </span>
可以看到,兩者在使用時,使用sql的位置不同。PrepareStatement采用了預編譯的方式,而Statement采用的是執行時建立。
在JDBC的API中,Statement要求開發者付出大量的時間和精力,在使用Statement擷取JDBC通路時,所具有的一個共同的問題就是輸入适當的格式的日期和時間戳。而這個問題,通過采用PrepareStatement可以自動解決。同時,PrepareStatement的另外一個優點字元串不是動态建立的。
OK,說到這裡,我們可以初步斷定,網上把PrepareStatement的逼格吹得那麼高,還是有一定的緣由的。那麼,為什麼要說:在JDBC應用中,如果你已經是稍有水準的開發者,你就應該始終以PrepareStatement代替Statement。難道使用PrepareStatement僅僅就是為了逼格高一些嗎?顯然不是吧,下邊小編就給您好好的唠唠。
JDBC驅動的最佳化基于使用的是什麼功能。選擇PrepareStatement還是Statement取決于你要怎樣使用它們。對于執行一次的SQL語句,選擇Statement是最好的;相反,如果SQL語句需要被多次執行,那麼我們可以選用PrepareStatement。
1、PrepareStatement 提高性能
PrepareStatement屬于預編譯的方式,每一種資料庫都會盡最大努力對預編譯語句提供最大的性能優化。因為預編譯語句有可能被重複調用,是以sql語句在被資料庫的編譯器編譯後,執行代碼被緩存下來,那麼下次調用的時候,隻要相同的預編譯語句就不需要編譯,隻要将參數直接傳入編譯過的語句執行代碼中,就會執行。當然,這并不是說隻有一個Connection中多次執行的預編譯語句被緩存,而是對整個資料庫中。隻要預編譯的語句和緩存中相比對,那麼在任何時候就可以 再次進行編譯就可以直接執行。
而在Statement的語句中,即使是相同的操作,由于每次操作的資料不同,是以整個語句相比對的機會極小,幾乎不太可能完全比對。比如:
<span style="font-family:Microsoft YaHei;font-size:14px;"> insert into tb_name (col1,col2) values ('11','22');
insert into tb_name (col1,col2) values ('11','23');</span>
即使是相同的操作,但是因為插入的資料内容不同,是以整個語句本身并不能比對,沒有緩存語句的意義。
當然,并不是所有的預編譯語句都一定會被緩存,資料庫本身會使用一定的政策。比如使用通路頻率等名額,來決定什麼時候不在緩存已有的預編譯結果,以儲存更多的存儲空間來存儲新的預編譯語句。
2、提高代碼的可讀性和可維護性
一個資料庫插入的Demo
<span style="font-family:Microsoft YaHei;font-size:14px;"> //執行一個插入操作
//Statement
Statement st;
st.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");
//PrepareStatement
PreparedStatement pst;
pst = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");
pst.setString(1,var1);
pst.setString(2,var2);
pst.setString(3,var3);
pst.setString(4,var4);
pst.executeUpdate();</span>
雖然,使用PrepareStatement會比Statement多幾行代碼,但是這樣的改變,無論是從代碼可讀性還是可維護性上來講,都是值得的。
3、安全性
PrepareStatement防止了SQL注入,提高了程式的安全性。
<span style="font-family:Microsoft YaHei;font-size:14px;">String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";</span>
在這裡,如果我們把[‘or’1'='1]作為varpasswd傳入進來,使用者名随意填寫,那麼就會産生SQL注入問題。
<span style="font-family:Microsoft YaHei;font-size:14px;">select * from tb_name = '随意' and passwd = '' or '1' = '1';</span>
因為‘1’=‘1’肯定成立,是以可以通過任何驗證者。
更進一步,把[';drop table tb_name;]作為varpasswd傳入進來
<span style="font-family:Microsoft YaHei;font-size:14px;">select * from tb_name = '随意' and passwd = '';drop table tb_name;</span>
在某些資料庫上,這些語句完全可以執行。
當然,如果使用預編譯語句,傳入的任何内容就不會和原來的語句發生任何比對的關系。使用PrepareStatement,預編譯語句的話,就不用對傳入的資料做任何考慮;如果使用Statement,就需要對drop等做判斷,防止SQL注入。
【尾巴】
遇到不懂的問題,上網去查,站在巨人的肩膀上,不失為一個很好的方法。但是,對于巨人們對問題的了解,我們還需要進一步的去消化,隻有這樣,我們才能夠成為巨人!