有的時候JDBC運行的不夠理想,這就促使我們寫一些與特定數據庫相關的存儲過程。作為一個替換方案,不妨試一下Statement的批處理特征,看看一次執行所有的SQL語句是否會帶來速度的提升。
存儲過程最簡單的形式就是整個過程只包含一組SQL語句。將這些語句放到一起能容易管理也可以提高運行速度。Statement類具有包含一串SQL語句的能力,因此它允許所有的SQL語句在一個數據庫會話中被執行,從而避免了向數據庫進行一連串的執行調用。
使用批處理功能涉及到兩個方法:
addBatch(String)方法
executeBatch方法
addBatch方法可以接受一段標准的SQL(如果你使用一個Statement)作為參數,也可以什麼參數都不帶(如果你使用一個PreparedStatement)。
executeBatch方法接著執行SQL語句,返回一個int型數組。這個數組包括每條語句影響到的行數。如果在一個批處理中使用是一個SELECT或者其它只返回結果的語句,那麼將會產生一個SQLException異常。
下面是一個簡單的java.sql.Statement的例子:
Statement stmt = conn.createStatement();
stmt.insert("DELETE FROM Users");
stmt.insert("INSERT INTO Users VALUES('rod', 37, 'circle')");
stmt.insert("INSERT INTO Users VALUES('jane', 33, 'triangle')");
stmt.insert("INSERT INTO Users VALUES('freddy', 29, 'square')");
int[] counts = stmt.executeBatch();
使用PreparedStatement會稍有不同。它只能處理一段SQL語句,但可以帶很多參數。下面的是使用PreparedStatement重寫的上面的例子:
//注意我們並沒有做任何刪除動作
PreparedStatement stmt = conn.prepareStatement( _
"INSERT INTO Users VALUES(?,?,?)" );
User[ ] users = ...;
for(int i=0; i<users.length; i++)
{
stmt.setInt(1, users[i].getName());
stmt.setInt(2, users[i].getAge());
stmt.setInt(3, users[i].getShape());
stmt.addBatch( );
}
int[ ] counts = stmt.executeBatch();
這是處理那些不知道具體執行次數的SQL代碼的一個好方法。沒有批處理,如果要添加50個用戶,其性能可能受到影響。如果誰寫了一段添加10000個用戶的腳本,其運行速度就難以忍受。增加批處理將有助於提升性能,在後一種情況裡,甚至可以改善代碼的可讀性。
使用PreparedStatement減少開發時間
JDBC(Java Database Connectivity,java數據庫連接)的API中的主要的四個類之一的java.sql.Statement要求開發者付出大量的時間和精力。在使用Statement獲取JDBC訪問時所具有的一個共通的問題是輸入適當格式的日期和時間戳:2002-02-05 20:56 或者 02/05/02 8:56 PM。通過使用java.sql.PreparedStatement,這個問題可以自動解決。一個PreparedStatement是從java.sql.Connection對象和所提供的SQL字符串得到的,SQL字符串中包含問號(?),這些問號標明變量的位置,然後提供變量的值,最後執行語句,例如:
Stringsql = "SELECT * FROM People p WHERE p.id = ? AND p.name = ?";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setInt(1,id);
ps.setString(2,name);
ResultSet rs = ps.execute();
使用PreparedStatement的另一個優點是字符串不是動態創建的。下面是一個動態創建字符串的例子:
Stringsql = "SELECT * FROM People p WHERE p.i = "+id;
Click here to find out more!
這允許JVM(JavaVirtual Machine,Java虛擬機)和驅動/數據庫緩存語句和字符串並提高性能。
PreparedStatement也提供數據庫無關性。當顯示聲明的SQL越少,那麼潛在的SQL語句的數據庫依賴性就越小。
由於PreparedStatement具備很多優點,開發者可能通常都使用它,只有在完全是因為性能原因或者是在一行SQL語句中沒有變量的時候才使用通常的Statement。
發出查詢和處理結果
在任何你想向數據庫運行一個SQL語句的時候, 你都需要一個Statement 或 PreparedStatement 實例。 一旦你擁有了一個Statement 或 PreparedStatement,你就可以 發出一個查詢。 這樣將返回一個ResultSet 實例, 在其內部包含整個結果。 Example 31-1 演示了這個過程。
Example 31-1. 在 JDBC 裡處理一個簡單的查詢
這個例子將發出一個簡單的查詢然後用一個 Statement打印出每行的第一個字段。
Statement st = db.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM mytable WHERE columnfoo = 500");
while (rs.next()) {
System.out.print("Column 1 returned ");
System.out.println(rs.getString(1));
}
rs.close();
st.close();
這個例子將使用 PreparedStatement 發出和前面一樣的查詢,並且在查詢中制作數值。
int foovalue = 500;
PreparedStatement st = db.prepareStatement("SELECT * FROM mytable WHERE columnfoo = ?");
st.setInt(1, foovalue);
ResultSet rs = st.executeQuery();
while (rs.next()) {
System.out.print("Column 1 returned ");
System.out.println(rs.getString(1));
}
rs.close();
st.close();
31.3.1. 使用 Statement或 PreparedStatement 接口
在使用Statement或 PreparedStatement接口時必須考慮下面的問題:
你可以將一個Statement或 PreparedStatement實例使用任意次。 你可以在打開一個聯接後馬上創建一個Statement 實例, 並且在聯接的生存期裡使用之。 你必須記住每個Statement或 PreparedStatement只能存在一個 ResultSet。
如果你需要在處理一個ResultSet的時候執行一個查詢, 你只需要創建並且使用另外一個Statement。
如果你使用了 threads (線程),並且有幾個使用數據庫, 你對每個線程必須使用一個獨立的Statement。 如果考慮使用線程, 請參考本文檔稍後的 Section 31.8 章節, 因為這些內容包含一些重要的信息。
在你用完 Statement 或者 PreparedStatement 之後,你應該關閉它。
31.3.2. 使用ResultSet(結果集)接口
使用ResultSet接口時必須考慮下面的問題:
在讀取任何數值的時候,你必須調用next()。 如果還有結果則返回真(true),但更重要的是,它為處理准備了數據行。
在 JDBC 規范裡,你對一個字段應該只訪問一次。 遵循這個規則是最安全的,不過目前 PostgreSQL 驅動將允許你對一個字段訪問任意次。
一旦你結束對一個 ResultSet 的處理,你必須對之調用 close()來關閉它。
一旦你使用那個創建ResultSet的 Statement做另一個查詢請求, 當前打開的 ResultSet 實例將自動關閉。
目前的 ResultSet 是只讀的。 你不能通過 ResultSet 來更新數據。 如果你想更新數據,那麼你就需要使用普通的方法來做∶ 通過發出一條 SQL 更新語句。這麼做是和 JDBC 規范兼容的,它並不要求驅動提供可更新的結果集的支持。
作者“ERDP技術架構”