欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java連接數(shù)據(jù)庫JDBC技術(shù)之prepareStatement的詳細(xì)介紹

 更新時(shí)間:2020年07月17日 16:58:46   作者:nnzhuilian  
這篇文章主要介紹了Java連接數(shù)據(jù)庫JDBC技術(shù)之prepareStatement的詳細(xì)介紹,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、prepareStatement 的用法和解釋

1.PreparedStatement是預(yù)編譯的,對于批量處理可以大大提高效率. 也叫JDBC存儲過程

2.使用 Statement 對象。在對數(shù)據(jù)庫只執(zhí)行一次性存取的時(shí)侯,用 Statement 對象進(jìn)行處理。PreparedStatement 對象的開銷比Statement大,對于一次性操作并不會帶來額外的好處。

3.statement每次執(zhí)行sql語句,相關(guān)數(shù)據(jù)庫都要執(zhí)行sql語句的編譯,preparedstatement是預(yù)編譯得, preparedstatement支持批處理

4、

Code Fragment 1:

String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERE COF_NAME LIKE ′Colombian′";
stmt.executeUpdate(updateString);

Code Fragment 2:

PreparedStatement updateSales = con.prepareStatement("UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? ");
updateSales.setInt(1, 75);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate();

片斷2和片斷1的區(qū)別在于,后者使用了PreparedStatement對象,而前者是普通的Statement對象。PreparedStatement對象不僅包含了SQL語句,而且大多數(shù)情況下這個(gè)語句已經(jīng)被預(yù)編譯過,因而當(dāng)其執(zhí)行時(shí),只需DBMS運(yùn)行SQL語句,而不必先編譯。當(dāng)你需要執(zhí)行Statement對象多次的時(shí)候,PreparedStatement對象將會大大降低運(yùn)行時(shí)間,當(dāng)然也加快了訪問數(shù)據(jù)庫的速度。
這種轉(zhuǎn)換也給你帶來很大的便利,不必重復(fù)SQL語句的句法,而只需更改其中變量的值,便可重新執(zhí)行SQL語句。選擇PreparedStatement對象與否,在于相同句法的SQL語句是否執(zhí)行了多次,而且兩次之間的差別僅僅是變量的不同。如果僅僅執(zhí)行了一次的話,它應(yīng)該和普通的對象毫無差異,體現(xiàn)不出它預(yù)編譯的優(yōu)越性。

5.執(zhí)行許多SQL語句的JDBC程序產(chǎn)生大量的Statement和PreparedStatement對象。通常認(rèn)為PreparedStatement對象比Statement對象更有效,特別是如果帶有不同參數(shù)的同一SQL語句被多次執(zhí)行的時(shí)候。PreparedStatement對象允許數(shù)據(jù)庫預(yù)編譯SQL語句,這樣在隨后的運(yùn)行中可以節(jié)省時(shí)間并增加代碼的可讀性。

然而,在Oracle環(huán)境中,開發(fā)人員實(shí)際上有更大的靈活性。當(dāng)使用Statement或PreparedStatement對象時(shí),Oracle數(shù)據(jù)庫會緩存SQL語句以便以后使用。在一些情況下,由于驅(qū)動器自身需要額外的處理和在Java應(yīng)用程序和Oracle服務(wù)器間增加的網(wǎng)絡(luò)活動,執(zhí)行PreparedStatement對象實(shí)際上會花更長的時(shí)間。

然而,除了緩沖的問題之外,至少還有一個(gè)更好的原因使我們在企業(yè)應(yīng)用程序中更喜歡使用PreparedStatement對象,那就是安全性。傳遞給PreparedStatement對象的參數(shù)可以被強(qiáng)制進(jìn)行類型轉(zhuǎn)換,使開發(fā)人員可以確保在插入或查詢數(shù)據(jù)時(shí)與底層的數(shù)據(jù)庫格式匹配。

當(dāng)處理公共Web站點(diǎn)上的用戶傳來的數(shù)據(jù)的時(shí)候,安全性的問題就變得極為重要。傳遞給PreparedStatement的字符串參數(shù)會自動被驅(qū)動器忽略。最簡單的情況下,這就意味著當(dāng)你的程序試著將字符串“D'Angelo”插入到VARCHAR2中時(shí),該語句將不會識別第一個(gè)“,”,從而導(dǎo)致悲慘的失敗。幾乎很少有必要?jiǎng)?chuàng)建你自己的字符串忽略代碼。

在Web環(huán)境中,有惡意的用戶會利用那些設(shè)計(jì)不完善的、不能正確處理字符串的應(yīng)用程序。特別是在公共Web站點(diǎn)上,在沒有首先通過PreparedStatement對象處理的情況下,所有的用戶輸入都不應(yīng)該傳遞給SQL語句。此外,在用戶有機(jī)會修改SQL語句的地方,如HTML的隱藏區(qū)域或一個(gè)查詢字符串上,SQL語句都不應(yīng)該被顯示出來。
在執(zhí)行SQL命令時(shí),我們有二種選擇:可以使用PreparedStatement對象,也可以使用Statement對象。無論多少次地使用同一個(gè)SQL命令,PreparedStatement都只對它解析和編譯一次。當(dāng)使用Statement對象時(shí),每次執(zhí)行一個(gè)SQL命令時(shí),都會對它進(jìn)行解析和編譯。 

第一:
prepareStatement會先初始化SQL,先把這個(gè)SQL提交到數(shù)據(jù)庫中進(jìn)行預(yù)處理,多次使用可提高效率。  
Statement不會初始化,沒有預(yù)處理,沒次都是從0開始執(zhí)行SQL

第二:
prepareStatement可以替換變量  
在SQL語句中可以包含?,可以用ps=conn.prepareStatement("select * from Cust where ID=?");  
int sid=1001;  
ps.setInt(1, sid);  
rs = ps.executeQuery();  
可以把?替換成變量。  
而Statement只能用  
int sid=1001;  
Statement stmt = conn.createStatement();  
ResultSet rs = stmt.executeQuery("select * from Cust where ID="+sid);  
來實(shí)現(xiàn)。

二、深入理解statement 和prepareStatement

1、使用Statement而不是PreparedStatement對象

JDBC驅(qū)動的最佳化是基于使用的是什么功能. 選擇PreparedStatement還是Statement取決于你要怎么使用它們. 對于只執(zhí)行一次的SQL語句選擇Statement是最好的. 相反, 如果SQL語句被多次執(zhí)行選用PreparedStatement是最好的.

PreparedStatement的第一次執(zhí)行消耗是很高的. 它的性能體現(xiàn)在后面的重復(fù)執(zhí)行. 例如, 假設(shè)我使用Employee ID, 使用prepared的方式來執(zhí)行一個(gè)針對Employee表的查詢. JDBC驅(qū)動會發(fā)送一個(gè)網(wǎng)絡(luò)請求到數(shù)據(jù)解析和優(yōu)化這個(gè)查詢. 而執(zhí)行時(shí)會產(chǎn)生另一個(gè)網(wǎng)絡(luò)請求.在JDBC驅(qū)動中,減少網(wǎng)絡(luò)通訊是最終的目的. 如果我的程序在運(yùn)行期間只需要一次請求, 那么就使用Statement. 對于Statement, 同一個(gè)查詢只會產(chǎn)生一次網(wǎng)絡(luò)到數(shù)據(jù)庫的通訊.

對于使用PreparedStatement池的情況下, 本指導(dǎo)原則有點(diǎn)復(fù)雜. 當(dāng)使用PreparedStatement池時(shí), 如果一個(gè)查詢很特殊, 并且不太會再次執(zhí)行到, 那么可以使用Statement. 如果一個(gè)查詢很少會被執(zhí)行,但連接池中的Statement池可能被再次執(zhí)行, 那么請使用PreparedStatement. 在不是Statement池的同樣情況下, 請使用Statement.

2、使用PreparedStatement的Batch功能

Update大量的數(shù)據(jù)時(shí), 先Prepare一個(gè)INSERT語句再多次的執(zhí)行, 會導(dǎo)致很多次的網(wǎng)絡(luò)連接. 要減少JDBC的調(diào)用次數(shù)改善性能, 你可以使用PreparedStatement的AddBatch()方法一次性發(fā)送多個(gè)查詢給數(shù)據(jù)庫. 例如, 讓我們來比較一下下面的例子.

例 1: 多次執(zhí)行PreparedStatement,多次數(shù)據(jù)庫請求(網(wǎng)絡(luò)請求)

PreparedStatement ps = conn.prepareStatement(  
   "INSERT into employees values (?, ?, ?)");  
  
for (n = 0; n < 100; n++) {  
  
  ps.setString(name[n]);  
  ps.setLong(id[n]);  
  ps.setInt(salary[n]);  
  ps.executeUpdate();  
}  

例 2: 使用Batch,以此請求執(zhí)行多條

PreparedStatement ps = conn.prepareStatement(  
   "INSERT into employees values (?, ?, ?)");  
  
for (n = 0; n < 100; n++) {  
  
  ps.setString(name[n]);  
  ps.setLong(id[n]);  
  ps.setInt(salary[n]);  
  ps.addBatch();  
}  
ps.executeBatch();  

  在例 1中, PreparedStatement被用來多次執(zhí)行INSERT語句. 在這里, 執(zhí)行了100次INSERT操作, 共有101次網(wǎng)絡(luò)往返.

   其中,1次往返是預(yù)儲PreparedStatement, 另外100次往返執(zhí)行每個(gè)迭代.

在例2中, 當(dāng)在100次INSERT操作中使用addBatch()方法時(shí), 只有兩次網(wǎng)絡(luò)往返.

   1次往返是預(yù)儲PreparedStatement, 另一次是執(zhí)行batch命令. 雖然Batch命令會用到更多的數(shù)據(jù)庫的CPU周期, 但是通過減少網(wǎng)絡(luò)往返,性能得到提高.記住, JDBC的性能最大的增進(jìn)是減少JDBC驅(qū)動與數(shù)據(jù)庫之間的網(wǎng)絡(luò)通訊.次數(shù)

 注:Oracel 10G的JDBC Driver限制最大Batch size是16383條,如果addBatch超過這個(gè)限制,那么executeBatch時(shí)就會出現(xiàn)“無效的批值”(Invalid Batch Value) 異常。因此在如果使用的是Oracle10G,在此bug減少前,Batch size需要控制在一定的限度。

       同樣mysql 5.5.28 批量執(zhí)行的數(shù)據(jù)最大限度是多少不清楚,但自己試了1w,2w,3w 都沒問題,記得在url 后面添加:rewriteBatchedStatements=true 表示批量插入,如果不添加的話即使使用addbatch() ,executeBatch() 在后臺入庫的地方還是不會一次請求入庫而是多次請求入庫。

3、選擇合適的光標(biāo)類型

的光標(biāo)類型以最大限度的適用你的應(yīng)用程序. 本節(jié)主要討論三種光標(biāo)類型的性能問題.
對于從一個(gè)表中順序讀取所有記錄的情況來說, Forward-Only型的光標(biāo)提供了最好的性能. 獲取表中的數(shù)據(jù)時(shí), 沒有哪種方法比使用Forward-Only型的光標(biāo)更快. 但不管怎樣, 當(dāng)程序中必須按無次序的方式處理數(shù)據(jù)行時(shí), 這種光標(biāo)就無法使用了.

對于程序中要求與數(shù)據(jù)庫的數(shù)據(jù)同步以及要能夠在結(jié)果集中前后移動光標(biāo), 使用JDBC的Scroll-Insensitive型光標(biāo)是較理想的選擇. 此類型的光標(biāo)在第一次請求時(shí)就獲取了所有的數(shù)據(jù)(當(dāng)JDBC驅(qū)動采用'lazy'方式獲取數(shù)據(jù)時(shí)或許是很多的而不是全部的數(shù)據(jù))并且儲存在客戶端. 因此, 第一次請求會非常慢, 特別是請求長數(shù)據(jù)時(shí)會理嚴(yán)重. 而接下來的請求并不會造成任何網(wǎng)絡(luò)往返(當(dāng)使用'lazy'方法時(shí)或許只是有限的網(wǎng)絡(luò)交通) 并且處理起來很快. 因?yàn)榈谝淮握埱笏俣群苈? Scroll-Insensitive型光標(biāo)不應(yīng)該被使用在單行數(shù)據(jù)的獲取上. 當(dāng)有要返回長數(shù)據(jù)時(shí), 開發(fā)者也應(yīng)避免使用Scroll-Insensitive型光標(biāo), 因?yàn)檫@樣可能會造成內(nèi)存耗盡. 有些Scroll-Insensitive型光標(biāo)的實(shí)現(xiàn)方式是在數(shù)據(jù)庫的臨時(shí)表中緩存數(shù)據(jù)來避免性能問題, 但多數(shù)還是將數(shù)據(jù)緩存在應(yīng)用程序中.

Scroll-Sensitive型光標(biāo), 有時(shí)也稱為Keyset-Driven光標(biāo), 使用標(biāo)識符, 像數(shù)據(jù)庫的ROWID之類. 當(dāng)每次在結(jié)果集移動光標(biāo)時(shí), 會重新該標(biāo)識符的數(shù)據(jù). 因?yàn)槊看握埱蠖紩芯W(wǎng)絡(luò)往返, 性能可能會很慢. 無論怎樣, 用無序方式的返回結(jié)果行對性能的改善是沒有幫助的.

現(xiàn)在來解釋一下這個(gè), 來看這種情況. 一個(gè)程序要正常的返回1000行數(shù)據(jù)到程序中. 在執(zhí)行時(shí)或者第一行被請求時(shí), JDBC驅(qū)動不會執(zhí)行程序提供的SELECT語句. 相反, 它會用鍵標(biāo)識符來替換SELECT查詢, 例如, ROWID. 然后修改過的查詢都會被驅(qū)動程序執(zhí)行,跟著會從數(shù)據(jù)庫獲取所有1000個(gè)鍵值. 每一次對一行結(jié)果的請求都會使JDBC驅(qū)動直接從本地緩存中找到相應(yīng)的鍵值, 然后構(gòu)造一個(gè)包含了'WHERE ROWID=?'子句的最佳化查詢, 再接著執(zhí)行這個(gè)修改過的查詢, 最后從服務(wù)器取得該數(shù)據(jù)行.

當(dāng)程序無法像Scroll-Insensitive型光標(biāo)一樣提供足夠緩存時(shí), Scroll-Sensitive型光標(biāo)可以被替代用來作為動態(tài)的可滾動的光標(biāo). 

4、使用有效的getter方法

JDBC提供多種方法從ResultSet中取得數(shù)據(jù), 像getInt(), getString(), 和getObject()等等. 而getObject()方法是最泛化了的, 提供了最差的性能。 這是因?yàn)镴DBC驅(qū)動必須對要取得的值的類型作額外的處理以映射為特定的對象. 所以就對特定的數(shù)據(jù)類型使用相應(yīng)的方法.

要更進(jìn)一步的改善性能, 應(yīng)在取得數(shù)據(jù)時(shí)提供字段的索引號, 例如, getString(1), getLong(2), 和getInt(3)等來替代字段名. 如果沒有指定字段索引號, 網(wǎng)絡(luò)交通不會受影響, 但會使轉(zhuǎn)換和查找的成本增加. 例如, 假設(shè)你使用getString("foo") ... JDBC驅(qū)動可能會將字段名轉(zhuǎn)為大寫(如果需要), 并且在到字段名列表中逐個(gè)比較來找到"foo"字段. 如果可以, 直接使用字段索引, 將為你節(jié)省大量的處理時(shí)間.

例如, 假設(shè)你有一個(gè)100行15列的ResultSet, 字段名不包含在其中. 你感興趣的是三個(gè)字段 EMPLOYEENAME (字串型), EMPLOYEENUMBER (長整型), 和SALARY (整型). 如果你指定getString(“EmployeeName”), getLong(“EmployeeNumber”), 和getInt(“Salary”), 查詢旱每個(gè)字段名必須被轉(zhuǎn)換為metadata中相對應(yīng)的大小寫, 然后才進(jìn)行查找. 如果你使用getString(1), getLong(2), 和getInt(15). 性能就會有顯著改善.

5、獲取自動生成的鍵值

有許多數(shù)據(jù)庫提供了隱藏列為表中的每行記錄分配一個(gè)唯一鍵值. 很典型, 在查詢中使用這些字段類型是取得記錄值的最快的方式, 因?yàn)檫@些隱含列通常反應(yīng)了數(shù)據(jù)在磁盤上的物理位置. 在JDBC3.0之前, 應(yīng)用程序只可在插入數(shù)據(jù)后通過立即執(zhí)行一個(gè)SELECT語句來取得隱含列的值.

例 3: JDBC3.0之前

//插入行  
int rowcount = stmt.executeUpdate (  
   "insert into LocalGeniusList (name) values ('Karen')");  
// 現(xiàn)在為新插入的行取得磁盤位置 - rowid  
ResultSet rs = stmt.executeQuery (  
   "select rowid from LocalGeniusList where name = 'Karen'");  

這種取得隱含列的方式有兩個(gè)主要缺點(diǎn). 第一, 取得隱含列是在一個(gè)獨(dú)立的查詢中, 它要透過網(wǎng)絡(luò)送到服務(wù)器后再執(zhí)行. 第二, 因?yàn)椴皇侵麈I, 查詢條件可能不是表中的唯一性ID. 在后面一個(gè)例子中, 可能返回了多個(gè)隱含列的值, 程序無法知道哪個(gè)是最后插入的行的值.

(譯者:由于不同的數(shù)據(jù)庫支持的程度不同,返回rowid的方式各有差異。在SQL Server中,返回最后插入的記錄的id可以用這樣的查詢語句:SELECT @IDENTITY )
JDBC3.0規(guī)范中的一個(gè)可選特性提供了一種能力, 可以取得剛剛插入到表中的記錄的自動生成的鍵值. 

例 4: JDBC3.0之后

int rowcount = stmt.executeUpdate (  
   "insert into LocalGeniusList (name) values ('Karen')",  
// 插入行并返回鍵值  
Statement.RETURN_GENERATED_KEYS);  
ResultSet rs = stmt.getGeneratedKeys ();  
// 得到生成的鍵值  

現(xiàn)在, 程序中包含了一個(gè)唯一性ID, 可以用來作為查詢條件來快速的存取數(shù)據(jù)行, 甚至于表中沒有主鍵的情況也可以.
這種取得自動生成的鍵值的方式給JDBC的開發(fā)者提供了靈活性, 并且使存取數(shù)據(jù)的性能得到提升.

6、選擇合適的數(shù)據(jù)類型

接收和發(fā)送某些數(shù)據(jù)可能代價(jià)昂貴. 當(dāng)你設(shè)計(jì)一個(gè)schema時(shí), 應(yīng)選擇能被最有效地處理的數(shù)據(jù)類型. 例如, 整型數(shù)就比浮點(diǎn)數(shù)或?qū)崝?shù)處理起來要快一些. 浮點(diǎn)數(shù)的定義是按照數(shù)據(jù)庫的內(nèi)部規(guī)定的格式, 通常是一種壓縮格式. 數(shù)據(jù)必須被解壓和轉(zhuǎn)換到另外種格式, 這樣它才能被數(shù)據(jù)的協(xié)議處理.

7、獲取ResultSet

由于數(shù)據(jù)庫系統(tǒng)對可滾動光標(biāo)的支持有限, 許多JDBC驅(qū)動程序并沒有實(shí)現(xiàn)可滾動光標(biāo). 除非你確信數(shù)據(jù)庫支持可滾動光標(biāo)的結(jié)果集, 否則不要調(diào)用rs.last()和rs.getRow()方法去找出數(shù)據(jù)集的最大行數(shù). 因?yàn)镴DBC驅(qū)動程序模擬了可滾動光標(biāo), 調(diào)用rs.last()導(dǎo)致了驅(qū)動程序透過網(wǎng)絡(luò)移到了數(shù)據(jù)集的最后一行. 取而代之, 你可以用ResultSet遍歷一次計(jì)數(shù)或者用SELECT查詢的COUNT函數(shù)來得到數(shù)據(jù)行數(shù).
通常情況下,請不要寫那種依賴于結(jié)果集行數(shù)的代碼, 因?yàn)轵?qū)動程序必須獲取所有的數(shù)據(jù)集以便知道查詢會返回多少行數(shù)據(jù).

三、preparestatement 防止sql注入

在JDBC應(yīng)用中,如果你已經(jīng)是稍有水平開發(fā)者,你就應(yīng)該始終以PreparedStatement代替Statement.也就是說,在任何時(shí)候都不要使用Statement.基于以下的原因:

1、代碼的可讀性和可維護(hù)性.雖然用PreparedStatement來代替Statement會使代碼多出幾行,但這樣的代碼無論從可讀性還是可維護(hù)性上來說.都比直接用Statement的代碼高很多檔次:
stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");
perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");
perstmt.setString(1,var1);perstmt.setString(2,var2);perstmt.setString(3,var3);perstmt.setString(4,var4);
perstmt.executeUpdate();
不用我多說,對于第一種方法.別說其他人去讀你的代碼,就是你自己過一段時(shí)間再去讀,都會覺得傷心.

2、PreparedStatement盡最大可能提高性能.每一種數(shù)據(jù)庫都會盡最大努力對預(yù)編譯語句提供最大的性能優(yōu)化.因?yàn)轭A(yù)編譯語句有可能被重復(fù)調(diào)用.所以語句在被DB的編譯器編譯后的執(zhí)行代碼被緩存下來,那么下次調(diào)用時(shí)只要是相同的預(yù)編譯語句就不需要編譯,只要將參數(shù)直接傳入編譯過的語句執(zhí)行代碼中(相當(dāng)于一個(gè)涵數(shù))就會得到執(zhí)行.這并不是說只有一個(gè) Connection中多次執(zhí)行的預(yù)編譯語句被緩存,而是對于整個(gè)DB中,只要預(yù)編譯的語句語法和緩存中匹配.那么在任何時(shí)候就可以不需要再次編譯而可以直接執(zhí)行.而statement的語句中,即使是相同一操作,而由于每次操作的數(shù)據(jù)不同所以使整個(gè)語句相匹配的機(jī)會極小,幾乎不太可能匹配.比如:insert into tb_name (col1,col2) values ('11','22');insert into tb_name (col1,col2) values ('11','23');即使是相同操作但因?yàn)閿?shù)據(jù)內(nèi)容不一樣,所以整個(gè)個(gè)語句本身不能匹配,沒有緩存語句的意義.事實(shí)是沒有數(shù)據(jù)庫會對普通語句編譯后的執(zhí)行代碼緩存.這樣每執(zhí)行一次都要對傳入的語句編譯一次.
當(dāng)然并不是所以預(yù)編譯語句都一定會被緩存,數(shù)據(jù)庫本身會用一種策略,比如使用頻度等因素來決定什么時(shí)候不再緩存已有的預(yù)編譯結(jié)果.以保存有更多的空間存儲新的預(yù)編譯語句.

3、最重要的一點(diǎn)是極大地提高了安全性.
即使到目前為止,仍有一些人連基本的惡義SQL語法都不知道.String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";如果我們把[' or '1' = '1]作為varpasswd傳入進(jìn)來.用戶名隨意,看看會成為什么?
select * from tb_name = '隨意' and passwd = '' or '1' = '1';因?yàn)?1'='1'肯定成立,所以可以任何通過驗(yàn)證.更有甚者:把[';drop table tb_name;]作為varpasswd傳入進(jìn)來,則:select * from tb_name = '隨意' and passwd = '';drop table tb_name;有些數(shù)據(jù)庫是不會讓你成功的,但也有很多數(shù)據(jù)庫就可以使這些語句得到執(zhí)行.
而如果你使用預(yù)編譯語句.你傳入的任何內(nèi)容就不會和原來的語句發(fā)生任何匹配的關(guān)系.(前提是數(shù)據(jù)庫本身支持預(yù)編譯,但上前可能沒有什么服務(wù)端數(shù)據(jù)庫不支持編譯了,只有少數(shù)的桌面數(shù)據(jù)庫,就是直接文件訪問的那些)只要全使用預(yù)編譯語句,你就用不著對傳入的數(shù)據(jù)做任何過慮.而如果使用普通的statement, 有可能要對drop,;等做費(fèi)盡心機(jī)的判斷和過慮.
上面的幾個(gè)原因,還不足讓你在任何時(shí)候都使用PreparedStatement嗎?

總結(jié): 上面是三篇文章,三篇文章詳細(xì)介紹了statement 和preparestatement 兩個(gè)對象的使用以及效率、安全問題。在實(shí)際項(xiàng)目中如果能夠使用preparestatement  還是建議使用preparestatement  原因有3:

1)、上面說了 如果sql中只有數(shù)值在變則效率高
2)、preparestatement 具有防sql注入
3)、代碼可讀性比較好

實(shí)例:下面這個(gè)比喻很好,很明確的說明了批量添加,并且從中也可以看出在批量添加的時(shí)候PreparedStatement為什么比Statement快的原因~

Statement和PreparedStatement的區(qū)別就不多廢話了,直接說PreparedStatement最重要的addbatch()結(jié)構(gòu)的使用.

PreparedStatement  的addBatch和executeBatch實(shí)現(xiàn)批量添加

1.建立鏈接   

   Connection    connection =getConnection();

2.不自動 Commit (瓜子不是一個(gè)一個(gè)吃,全部剝開放桌子上,然后一口舔了)

connection.setAutoCommit(false);   

3.預(yù)編譯SQL語句,只編譯一回哦,效率高啊.(發(fā)明一個(gè)剝瓜子的方法,以后不要總想怎么剝瓜子好.就這樣剝.)
PreparedStatement statement = connection.prepareStatement("INSERT INTO TABLEX VALUES(?, ?)");   

4.來一個(gè)剝一個(gè),然后放桌子上

//記錄1
statement.setInt(1, 1); 
statement.setString(2, "Cujo"); 
statement.addBatch();  

//記錄2
statement.setInt(1, 2); 
statement.setString(2, "Fred"); 
statement.addBatch();  

//記錄3
statement.setInt(1, 3); 
statement.setString(2, "Mark"); 
statement.addBatch();  

//批量執(zhí)行上面3條語句. 一口吞了,很爽
int [] counts = statement.executeBatch();  

//Commit it 咽下去,到肚子(DB)里面
connection.commit();

statement 對象的addBatch 和 executeBatch 來實(shí)現(xiàn)批量添加
stmt.addBatch("update TABLE1 set 題目="盛夏話足部保健1"  where");
stmt.addBatch("update TABLE1 set 題目="夏季預(yù)防中暑膳食1" where"); 
stmt.addBatch("INSERT INTO TABLE1 VALUES("11","12","13","","")"); 
stmt.addBatch("INSERT INTO TABLE1 VALUES("12","12","13","","")"); 
stmt.addBatch("INSERT INTO TABLE1 VALUES("13","12","13","","")"); 
stmt.addBatch("INSERT INTO TABLE1 VALUES("14","12","13","","")"); 
stmt.addBatch("INSERT INTO TABLE1 VALUES("15","12","13","","")"); 
stmt.addBatch("INSERT INTO TABLE1 VALUES("16","12","13","","")"); 
stmt.addBatch("INSERT INTO TABLE1 VALUES("17","12","13","","")"); 
stmt.addBatch("INSERT INTO TABLE1 VALUES("18","12","13","","")"); 
int [] updateCounts=stmt.executeBatch(); 
cn.commit();

實(shí)例:批量添加

public static void insertData(List<Map<String,String>> list,Logger log){  
    //獲取的數(shù)據(jù)  
    List <Map<String,String>> nlist= list;  
    String upsql="update   hrd_staff  set position =?  where id=?";  
    Iterator<Map<String,String>> iter= nlist.iterator();  
    Connection con= Utils.getCon();  
    int count=0;  
    try {  
        //在皮臉添加的時(shí)候注意事務(wù)提交方式  
        con.setAutoCommit(false);  
        //PreparedStatement方法的使用  
        PreparedStatement pstm = con.prepareStatement(upsql);  
        while(iter.hasNext()){  
            count++;  
            Map<String,String> map= iter.next();  
            String jon_name= map.get("job_name");  
            String uid= map.get("uid");  
            pstm.setString(1,jon_name);  
            pstm.setString(2,uid);  
            //添加到緩存中  
            pstm.addBatch();  
            // 如果數(shù)據(jù)量很大,不能一次性批量添加所以我們要分批次添加,這里就是300條一次  
            if(count%300==0){  
                //持久化  
                int []res=pstm.executeBatch();  
                //提交事務(wù),持久化數(shù)據(jù)  
                con.commit();  
                pstm.clearBatch();  
                log.info("300整除插入結(jié)果: "+res.length);  
            }  
        }  
        //小于300條的在這里持久化  
        int []ress= pstm.executeBatch();  
        //事務(wù)提交持久化  
        con.commit();  
        pstm.clearBatch();  
        log.info("插入數(shù)據(jù)結(jié)果:"+ress.length);  
    } catch (SQLException e) {  
        try {  
            con.rollback();  
        } catch (SQLException e1) {  
            // TODO Auto-generated catch block  
            e1.printStackTrace();  
        }  
        e.printStackTrace();  
    }finally{  
        try {  
            if(null!=con){  
            con.close();  
            con.setAutoCommit(true);  
            }  
        } catch (SQLException e) {  
            // TODO Auto-generated catch block  
            e.printStackTrace();  
        }  
    }  
}  

 這里除了下面說的url中的批量設(shè)置外,我們也要注意事務(wù)的設(shè)置,不能設(shè)置為自動提交,要批量添加后在提交事務(wù)

總結(jié):

addBatch() 就是把你的處理內(nèi)容添加到批處理單元中。即添加到了batch中。你可以循環(huán)加入很多,數(shù)據(jù)庫都不會處理,直到調(diào)用如下代碼executeBatch() 此時(shí),數(shù)據(jù)庫把剛才加到batch中的命令批量處理。

使用批量插入的好處: , 當(dāng)在100次INSERT操作中使用addBatch()方法時(shí), 只有兩次網(wǎng)絡(luò)往返. 1次往返是預(yù)儲statement, 另一次是執(zhí)行batch命令. 雖然Batch命令會用到更多的數(shù)據(jù)庫的CPU周期, 但是通過減少網(wǎng)絡(luò)往返,性能得到提高. 記住, JDBC的性能最大的增進(jìn)是減少JDBC驅(qū)動與數(shù)據(jù)庫之間的網(wǎng)絡(luò)通訊. 如果沒有使用批處理則網(wǎng)絡(luò)往返101次這樣會耗很多時(shí)間,自然效率也就一般

這里要注意:在mysql 下使用批量執(zhí)行的時(shí)候要在,url 后面添加手動設(shè)置支持批量添加 實(shí)例如下:

 String url="jdbc:mysql://localhost:3306/music?rewriteBatchedStatements=true";

// 默認(rèn)情況下rewriteBatchedStatements 的值為false 也就是批量添加功能是關(guān)閉的,如果使用則要手動開啟!

還有就是事務(wù)的設(shè)置,不能使自動提交,要批量添加后才提交?。?!

到此這篇關(guān)于Java連接數(shù)據(jù)庫JDBC技術(shù)之prepareStatement的詳細(xì)介紹的文章就介紹到這了,更多相關(guān)Java數(shù)據(jù)庫JDBC技術(shù)prepareStatement內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實(shí)現(xiàn)學(xué)生管理系統(tǒng)詳解流程

    Java實(shí)現(xiàn)學(xué)生管理系統(tǒng)詳解流程

    這篇文章主要為大家詳細(xì)介紹了如何利用Java語言實(shí)現(xiàn)學(xué)生管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • SpringBoot使用Redisson實(shí)現(xiàn)延遲執(zhí)行的完整示例

    SpringBoot使用Redisson實(shí)現(xiàn)延遲執(zhí)行的完整示例

    這篇文章主要介紹了SpringBoot使用Redisson實(shí)現(xiàn)延遲執(zhí)行的完整示例,文中通過代碼示例講解的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-06-06
  • java如何調(diào)用Groovy腳本

    java如何調(diào)用Groovy腳本

    這篇文章主要介紹了java如何調(diào)用Groovy腳本問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • 老生常談Java動態(tài)編譯(必看篇)

    老生常談Java動態(tài)編譯(必看篇)

    下面小編就為大家?guī)硪黄仙U凧ava動態(tài)編譯(必看篇)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05
  • SpringBoot @RequestParam、@PathVaribale、@RequestBody實(shí)戰(zhàn)案例

    SpringBoot @RequestParam、@PathVaribale、@RequestBody實(shí)戰(zhàn)案例

    這篇文章主要介紹了SpringBoot @RequestParam、@PathVaribale、@RequestBody實(shí)戰(zhàn)案例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • 基于Java swing組件實(shí)現(xiàn)簡易計(jì)算器

    基于Java swing組件實(shí)現(xiàn)簡易計(jì)算器

    這篇文章主要介紹了基于Java swing組件實(shí)現(xiàn)簡易計(jì)算器,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-04-04
  • SpringBoot中的Logging詳解

    SpringBoot中的Logging詳解

    這篇文章主要介紹了SpringBoot中的Logging詳解,log配置可能是被忽視的一個(gè)環(huán)節(jié),一般的項(xiàng)目中日志配置好了基本上很少去改動,我們常規(guī)操作是log.info來記錄日志內(nèi)容,很少會有人注意到springBoot中日志的配置,需要的朋友可以參考下
    2023-09-09
  • 詳解Java List的擴(kuò)容機(jī)制原理及應(yīng)用

    詳解Java List的擴(kuò)容機(jī)制原理及應(yīng)用

    在Java中,List是一種非常常用的數(shù)據(jù)結(jié)構(gòu),用于存儲有序的元素集合,本文將分析Java List的擴(kuò)容機(jī)制原理,并通過示例代碼和測試代碼來加強(qiáng)闡述內(nèi)容,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-08-08
  • Java中@ConditionalOnProperty注解使用

    Java中@ConditionalOnProperty注解使用

    在Spring?Boot中,@ConditionalOnProperty注解是一種方便的工具,用于根據(jù)應(yīng)用程序配置文件中的屬性值來控制Bean的創(chuàng)建和加載,本文就來介紹一下Java中@ConditionalOnProperty注解使用,感興趣的可以了解一下
    2023-11-11
  • JAVA操作elastic?search的詳細(xì)過程

    JAVA操作elastic?search的詳細(xì)過程

    Elasticsearch?Rest?High?Level?Client?是?Elasticsearch?官方提供的一個(gè)?Java?客戶端庫,用于與?Elasticsearch?進(jìn)行交互,本文介紹JAVA操作elastic?search的詳細(xì)過程,感興趣的朋友一起看看吧
    2024-08-08

最新評論