標籤:
1. 資料庫編程基本流程
(1)載入資料庫廠商的驅動程式
例如:對於Mysql資料庫,Class.forName("com.mysql.jdbc.Driver");
(2)獲得資料庫連接對象
Connection connection = DriverManager.getConnection(url,username,password);
(3)建立資料庫執行語句對象
Statement statement = connection.createStatement();
(4)執行SQL語句,獲得結果集對象
ResultSet result = statement.executeQuery("SELECT * FROM tb_test");
(5)關閉資料庫連接
使用完ResultSet、Statement以及Connection對象後,需要及時調用它們的close方法來釋放資源。關閉Statement對象時,會自動釋放由其獲得的所有ResultSet對象,關閉Connection對象也會自動釋放由其獲得的Statement對象。因此,調用Connection的close方法就可以釋放與其相關的所有資源。
由於資料庫連接使用完後必須關閉,因此通常將調用close方法放在finally子句中。
2. JDBC擷取自增主鍵
很多時候,我們插入一個記錄的主鍵是自增的列,我們可能緊接著需要獲得這個主鍵,JDBC提供了統一的介面:
statement.executeUpdate("INSERT....",Statement.RETURN_GENERATED_KEYS);
ResultSet result = statement.getGeneratedKeys();
其中,參數Statement.RETURN_GENERATED_KEYS指定返回剛插入的鍵,然後調用getGeneratedKeys方法返回這些鍵。
3. 執行事務
事務表示具有原子性的SQL語句的組合,這些SQL語句要麼全部執行成功,要麼就全部不執行。
預設情況下,我們建立的Connection對象處於自動認可模式,即執行了一條SQL更新語句,就立即提交更新資料庫。執行事務時,我們需要設定Connection對象不自動認可:connection.setAutoCommit(false);
然後我們就可以執行多條SQL更新語句,這些對資料庫的更新只有在調用Connection對象的commit方法後才會生效,如果執行這些SQL語句中出現了錯誤,就調用rollback方法復原資料庫。
4. 資料庫連接池
當在多線程環境下進行資料庫編程時,就需要注意同步的問題。
使用一個全域的Connection對象若不進行同步就會導致並發的各種問題,而對這個全域Connection進行同步,通常效率又是很低的。我們可以使用局部的Connection對象,每次執行資料庫任務時都建立一個新的串連,使用完後立即關閉,這樣會導致頻繁的建立關閉資料庫連接,效率也會比較低。
更好的方式使用資料庫連接池來管理資料庫串連,資料庫連接池可以有效地利用閑置的資料庫連接。想要串連資料庫時,通過資料庫連接池請求一個Connection對象,這個Connection可能是新建立的,也可能是之前建立尚未關閉的。關閉Connection對象時並不是真的與資料庫中斷連線,而是歸入到資料庫連接池的閑置串連中。
實現一個資料庫連接池最重要的問題就是解決調用Connection的close方法時,如何不真正關閉資料庫連接。我們可以使用Java動態代理來解決這個問題。
由於Connection是一個介面,而DriverManager.getConnection方法返回的Connection的具體類型我們又不知道,我們就無法直接通過覆蓋來實現新的close方法。我們當然也可以自己實現Conneciton介面,但這樣我們就需要實現許多介面方法,而實際上我們只需要改變close方法的實現。
Java反射庫中的Proxy類為我們提供了這樣的能力:可以構造一個實現一些介面的類,並指定一個調用處理器(實現InvocationHandler介面)來攔截對所有介面方法的調用。
下面給出一個簡易實現的資料庫連接池:
public class DBConnectionPool{ private String jdbcUrl; private String username; private String password; private LinkedList<Connection> pool; //儲存閑置的Connection對象 public DBConnectionPool(String driver,String jdbcUrl) throws ClassNotFoundException { this.jdbcUrl = jdbcUrl; Class.forName(driver); pool = new LinkedList<Connection>(); } public DBConnectionPool(String driver,String jdbcUrl,String username,String password) throws ClassNotFoundException { this.jdbcUrl = jdbcUrl; this.username = username; this.password = password; Class.forName(driver); pool = new LinkedList<Connection>(); }
//為了可以在多線程環境下使用,對一些方法使用synchronized關鍵字同步 public synchronized Connection getConnection() throws SQLException { Connection conn = null; if(pool.size() > 0) conn = pool.poll(); else { final Connection connection = DriverManager.getConnection(jdbcUrl, username, password);
//建立代理對象,並強制轉換為Connection介面類型,InvocationHandler介面表示調用處理器 conn = (Connection)Proxy.newProxyInstance(null,new Class[]{Connection.class},new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName() == "close" && method.getParameterTypes().length == 0) { pool.add((Connection)proxy); //執行close方法時,替換原方法的邏輯 return null; } else //執行其他方法時,保持原方法的邏輯 return method.invoke(connection,args); } }); } return conn; } public synchronized Connection getConnection(String username, String password) throws SQLException { Connection conn = null; if(pool.size() > 0) conn = pool.poll(); else { final Connection connection = DriverManager.getConnection(jdbcUrl, username, password); conn = (Connection)Proxy.newProxyInstance(null,new Class[]{Connection.class},new InvocationHandler(){ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName() == "close" && method.getParameterTypes().length == 0) { pool.add((Connection)proxy); return null; } else return method.invoke(connection,args); } }); } return conn; } public synchronized void reduce() throws SQLException { int size = pool.size(); while(pool.size() > (size / 2) ) pool.poll().close(); } public synchronized void close() throws SQLException { while(pool.size() > 0) pool.poll().close(); }}
Java資料庫編程