天天看點

【Java學習筆記】——Statement & PrepareStatement

【前言】在上一篇文章中,我們說到了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注入。

【尾巴】

       遇到不懂的問題,上網去查,站在巨人的肩膀上,不失為一個很好的方法。但是,對于巨人們對問題的了解,我們還需要進一步的去消化,隻有這樣,我們才能夠成為巨人!