一、數據庫連接池:
在一般用JDBC 進行連接數據庫進行CRUD操作時,每一次都會:
通過:java.sql.Connection conn = DriverManager.getConnection(url,user,password); 重新獲取一個數據庫的鏈接再進行操作,這樣用戶每次請求都需要向數據庫獲得鏈接,而數據庫創建連接通常需要消耗相對較大的資源,創建時間也較長。
所以為了減少服務器的壓力,便可用連接池的方法:在啟動Web應用時,數據就創建好一定數量的Connection鏈接
存放到一個容器中,然后當用戶請求時,服務器則向容器中獲取Connection鏈接來處理用戶的請求,當用戶的請求完成后,
又將該Connection 鏈接放回到該容器中。這樣的一個容器稱為連接池。
編寫一個基本的連接池實現連接復用
步驟:
1、建立一個數據庫連接池容器。(因為方便存取,則使用LinkedList集合)
2、初始化一定數量的連接,放入到容器中。
3、等待用戶獲取連接對象。(該部分要加鎖)
|---記得刪除容器中對應的對象,放置別人同時獲取到同一個對象。
4、提供一個方法,回收用戶用完的連接對象。
5、要遵循先入先出的原則。
1 import java.io.InputStream; 2 import java.sql.Connection; 3 import java.sql.DriverManager; 4 import java.sql.SQLException; 5 import java.util.LinkedList; 6 import java.util.Properties; 7 8 9 /** 10 * 一個基本的數據連接池: 11 * 1、初始化時就建立一個容器,來存儲一定數量的Connection 對象 12 * 2、用戶通過調用MyDataSource 的getConnection 來獲取Connection 對象。 13 * 3、再通過release 方法來回收Connection 對象,而不是直接關閉連接。 14 * 4、遵守先進先出的原則。 15 * 16 * 17 * @author 賀佐安 18 * 19 */ 20 public class MyDataSource { 21 private static String url = null; 22 private static String password = null; 23 private static String user = null ; 24 private static String DriverClass = null; 25 private static LinkedList<Connection> pool = new LinkedList<Connection>() ; 26 // 注冊數據庫驅動 27 static { 28 try { 29 InputStream in = MyDataSource.class.getClassLoader() 30 .getResourceAsStream("db.properties"); 31 Properties prop = new Properties(); 32 prop.load(in); 33 user = prop.getProperty("user"); 34 url = prop.getProperty("url") ; 35 password = prop.getProperty("password") ; 36 DriverClass = prop.getProperty("DriverClass") ; 37 Class.forName(DriverClass) ; 38 39 } catch (Exception e) { 40 throw new RuntimeException(e) ; 41 } 42 } 43 //初始化建立數據連接池 44 public MyDataSource () { 45 for(int i = 0 ; i < 10 ; i ++) { 46 try { 47 Connection conn = DriverManager.getConnection(url, user, password) ; 48 pool.add(conn) ; 49 } catch (SQLException e) { 50 e.printStackTrace(); 51 } 52 } 53 } 54 //、從連接池獲取連接 55 public Connection getConnection() throws SQLException { 56 return pool.remove() ; 57 } 58 // 回收連接對象。 59 public void release(Connection conn) { 60 System.out.println(conn+"被回收"); 61 pool.addLast(conn) ; 62 } 63 public int getLength() { 64 return pool.size() ; 65 } 66 }
這樣當我們要使用Connection 連接數據庫時,則可以直接使用連接池中Connection 的對象。測試如下:
1 import java.sql.Connection; 2 import java.sql.SQLException; 3 4 import org.junit.Test; 5 6 7 public class MyDataSourceTest { 8 9 10 /** 11 * 獲取數據庫連接池中的所有連接。 12 */ 13 @Test 14 public void Test() { 15 MyDataSource mds = new MyDataSource() ; 16 Connection conn = null ; 17 try { 18 19 for (int i = 0 ; i < 20 ; i ++) { 20 conn = mds.getConnection() ; 21 System.out.println(conn+"被獲取;連接池還有:"+mds.getLength()); 22 mds.release(conn) ; 23 } 24 } catch (SQLException e) { 25 e.printStackTrace(); 26 } 27 } 28 }
再運行的時候,可以發現,循環10次后,又再一次獲取到了第一次循環的得到的Connection對象。所以,這樣可以大大的減輕數據庫的壓力。上面只是一個簡單的數據庫連接池,不完美的便是,回收需要調用數據池的release() 方法來進行回收,那么可以不可以直接調用Connection 實例的close 便完成Connection 對象的回收呢?
二、數據源:
> 編寫連接池需實現javax.sql.DataSource接口。
> 實現DataSource接口,並實現連接池功能的步驟:
1、在DataSource構造函數中批量創建與數據庫的連接,並把創建的連接加入LinkedList對象中。
2、實現getConnection方法,讓getConnection方法每次調用時,從LinkedList中取一個Connection返回給用戶。當用戶使用完Connection,調用Connection.close()方法時,Collection對象應保證將自己返回到LinkedList中,而不要把conn還給數據庫。
利用動態代理和包裝設計模式來標准的數據源。
1、包裝設計模式實現標准數據源:
這里的用包裝設計模式,便是將Connection 接口進行包裝。簡單總結一下包裝設計模式的步驟:
a)定義一個類,實現與被包裝類()相同的接口。
|----可以先自己寫一個適配器,然后后面繼承這個適配器,改寫需要改寫的方法,提高編程效率。
b)定義一個實例變量,記住被包裝類的對象的引用。
c)定義構造方法,轉入被包裝類的對象。
e)對需要改寫的方法,改寫。
f)對不需要改寫的方法,調用原來被包裝類的對應方法。
所以先編寫一個類似適配器的類,將Connection 接口的方法都進行實現:

import java.sql.Array; import java.sql.Blob; import java.sql.CallableStatement; import java.sql.Clob; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.NClob; import java.sql.PreparedStatement; import java.sql.SQLClientInfoException; import java.sql.SQLException; import java.sql.SQLWarning; import java.sql.SQLXML; import java.sql.Savepoint; import java.sql.Statement; import java.sql.Struct; import java.util.Map; import java.util.Properties; /** * 實現Connection 的適配器: * 目的:在使用包裝設計模式時方便使用 * @author 賀佐安 * */ public class MyConnectionAdapter implements Connection { //用一個實例變量,記住被包裝類的實例引用 protected Connection conn ; //構造函數,轉入被包裝類的對象 public MyConnectionAdapter(Connection conn) { this.conn = conn ; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { return conn.unwrap(iface); } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return conn.isWrapperFor(iface); } @Override public Statement createStatement() throws SQLException { return conn.createStatement(); } @Override public PreparedStatement prepareStatement(String sql) throws SQLException { return conn.prepareStatement(sql); } @Override public CallableStatement prepareCall(String sql) throws SQLException { return conn.prepareCall(sql); } @Override public String nativeSQL(String sql) throws SQLException { return conn.nativeSQL(sql); } @Override public void setAutoCommit(boolean autoCommit) throws SQLException { conn.setAutoCommit(autoCommit); } @Override public boolean getAutoCommit() throws SQLException { return conn.getAutoCommit(); } @Override public void commit() throws SQLException { conn.commit() ; } @Override public void rollback() throws SQLException { conn.rollback() ; } @Override public void close() throws SQLException { conn.close() ; } @Override public boolean isClosed() throws SQLException { return conn.isClosed(); } @Override public DatabaseMetaData getMetaData() throws SQLException { return conn.getMetaData(); } @Override public void setReadOnly(boolean readOnly) throws SQLException { conn.setReadOnly(readOnly); } @Override public boolean isReadOnly() throws SQLException { return conn.isReadOnly(); } @Override public void setCatalog(String catalog) throws SQLException { conn.setCatalog(catalog) ; } @Override public String getCatalog() throws SQLException { return conn.getCatalog(); } @Override public void setTransactionIsolation(int level) throws SQLException { conn.setTransactionIsolation(level) ; } @Override public int getTransactionIsolation() throws SQLException { return conn.getTransactionIsolation(); } @Override public SQLWarning getWarnings() throws SQLException { return conn.getWarnings(); } @Override public void clearWarnings() throws SQLException { conn.clearWarnings() ; } @Override public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { return conn.createStatement(resultSetType, resultSetConcurrency); } @Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return conn.prepareStatement(sql, resultSetType, resultSetConcurrency); } @Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { return conn.prepareCall(sql, resultSetType, resultSetConcurrency); } @Override public Map<String, Class<?>> getTypeMap() throws SQLException { return conn.getTypeMap(); } @Override public void setTypeMap(Map<String, Class<?>> map) throws SQLException { conn.setTypeMap(map) ; } @Override public void setHoldability(int holdability) throws SQLException { conn.setHoldability(holdability) ; } @Override public int getHoldability() throws SQLException { return conn.getHoldability(); } @Override public Savepoint setSavepoint() throws SQLException { return conn.setSavepoint(); } @Override public Savepoint setSavepoint(String name) throws SQLException { return conn.setSavepoint(name); } @Override public void rollback(Savepoint savepoint) throws SQLException { conn.rollback(savepoint); } @Override public void releaseSavepoint(Savepoint savepoint) throws SQLException { conn.releaseSavepoint(savepoint); } @Override public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return conn.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); } @Override public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return conn.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); } @Override public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { return conn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); } @Override public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { return conn.prepareStatement(sql, autoGeneratedKeys); } @Override public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { return conn.prepareStatement(sql, columnIndexes); } @Override public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { return conn.prepareStatement(sql, columnNames); } @Override public Clob createClob() throws SQLException { return conn.createClob(); } @Override public Blob createBlob() throws SQLException { return conn.createBlob(); } @Override public NClob createNClob() throws SQLException { return conn.createNClob(); } @Override public SQLXML createSQLXML() throws SQLException { return conn.createSQLXML(); } @Override public boolean isValid(int timeout) throws SQLException { return conn.isValid(timeout); } @Override public void setClientInfo(String name, String value) throws SQLClientInfoException { conn.setClientInfo(name, value) ; } @Override public void setClientInfo(Properties properties) throws SQLClientInfoException { conn.setClientInfo(properties) ; } @Override public String getClientInfo(String name) throws SQLException { return conn.getClientInfo(name); } @Override public Properties getClientInfo() throws SQLException { return conn.getClientInfo(); } @Override public Array createArrayOf(String typeName, Object[] elements) throws SQLException { return conn.createArrayOf(typeName, elements); } @Override public Struct createStruct(String typeName, Object[] attributes) throws SQLException { return conn.createStruct(typeName, attributes); } }
然后再對Connection 接口進行包裝,將close 方法修改掉:
1 import java.sql.Connection; 2 import java.sql.SQLException; 3 import java.util.LinkedList; 4 /** 5 * 對MyConnectionAdapter 進行包裝處理 6 * @author 賀佐安 7 * 8 */ 9 public class MyConnectionWrap extends MyConnectionAdapter { 10 11 private LinkedList<Connection> pool = new LinkedList<Connection>() ; 12 public MyConnectionWrap(Connection conn ,LinkedList<Connection> pool ) { 13 super(conn); 14 this.pool = pool ; 15 } 16 17 //改寫要實現的方法 18 public void close() throws SQLException { 19 pool.addLast(conn) ; 20 } 21 }
編寫標准數據源:
1 import java.io.PrintWriter; 2 import java.sql.Connection; 3 import java.sql.DriverManager; 4 import java.sql.SQLException; 5 import java.util.LinkedList; 6 import java.util.ResourceBundle; 7 8 import javax.sql.DataSource; 9 10 11 /** 12 * 編寫標准的數據源: 13 * 1、實現DataSource 接口 14 * 2、獲取在實現類的構造方法中批量獲取Connection 對象,並將這些Connection 存儲 15 * 在LinkedList 容器中。 16 * 3、實現getConnection() 方法,調用時返回LinkedList容器的Connection對象給用戶。 17 * @author 賀佐安 18 * 19 */ 20 public class MyDataSource implements DataSource{ 21 private static String url = null; 22 private static String password = null; 23 private static String user = null ; 24 private static String DriverClass = null; 25 private static LinkedList<Connection> pool = new LinkedList<Connection>() ; 26 27 // 注冊數據庫驅動 28 static { 29 try { 30 ResourceBundle rb = ResourceBundle.getBundle("db") ; 31 url = rb.getString("url") ; 32 password = rb.getString("password") ; 33 user = rb.getString("user") ; 34 DriverClass = rb.getString("DriverClass") ; 35 Class.forName(DriverClass) ; 36 37 //初始化建立數據連接池 38 for(int i = 0 ; i < 10 ; i ++) { 39 Connection conn = DriverManager.getConnection(url, user, password) ; 40 pool.add(conn) ; 41 } 42 } catch (Exception e) { 43 throw new RuntimeException(e) ; 44 } 45 46 } 47 public MyDataSource () { 48 } 49 50 //、從連接池獲取連接:通過包裝模式 51 public synchronized Connection getConnection() throws SQLException { 52 if (pool.size() > 0) { 53 MyConnectionWrap mcw = new MyConnectionWrap(pool.remove(), pool) ; 54 return mcw ; 55 }else { 56 throw new RuntimeException("服務器繁忙!"); 57 } 58 } 59 60 // 回收連接對象。 61 public void release(Connection conn) { 62 System.out.println(conn+"被回收"); 63 pool.addLast(conn) ; 64 } 65 66 public int getLength() { 67 return pool.size() ; 68 } 69 70 71 @Override 72 public PrintWriter getLogWriter() throws SQLException { 73 return null; 74 } 75 @Override 76 public void setLogWriter(PrintWriter out) throws SQLException { 77 78 } 79 @Override 80 public void setLoginTimeout(int seconds) throws SQLException { 81 82 } 83 @Override 84 public int getLoginTimeout() throws SQLException { 85 return 0; 86 } 87 @Override 88 public <T> T unwrap(Class<T> iface) throws SQLException { 89 return null; 90 } 91 @Override 92 public boolean isWrapperFor(Class<?> iface) throws SQLException { 93 return false; 94 } 95 @Override 96 public Connection getConnection(String username, String password) 97 throws SQLException { 98 return null; 99 } 100 101 }
2、動態代理實現標准數據源:
相對於用包裝設計來完成標准數據源,用動態代理則方便許多:
1 import java.io.PrintWriter; 2 import java.lang.reflect.InvocationHandler; 3 import java.lang.reflect.Method; 4 import java.lang.reflect.Proxy; 5 import java.sql.Connection; 6 import java.sql.DriverManager; 7 import java.sql.SQLException; 8 import java.util.LinkedList; 9 import java.util.ResourceBundle; 10 11 import javax.sql.DataSource; 12 13 14 /** 15 * 編寫標准的數據源: 16 * 1、實現DataSource 接口 17 * 2、獲取在實現類的構造方法中批量獲取Connection 對象,並將這些Connection 存儲 18 * 在LinkedList 容器中。 19 * 3、實現getConnection() 方法,調用時返回LinkedList容器的Connection對象給用戶。 20 * @author 賀佐安 21 * 22 */ 23 public class MyDataSource implements DataSource{ 24 private static String url = null; 25 private static String password = null; 26 private static String user = null ; 27 private static String DriverClass = null; 28 private static LinkedList<Connection> pool = new LinkedList<Connection>() ; 29 30 // 注冊數據庫驅動 31 static { 32 try { 33 ResourceBundle rb = ResourceBundle.getBundle("db") ; 34 url = rb.getString("url") ; 35 password = rb.getString("password") ; 36 user = rb.getString("user") ; 37 DriverClass = rb.getString("DriverClass") ; 38 Class.forName(DriverClass) ; 39 40 //初始化建立數據連接池 41 for(int i = 0 ; i < 10 ; i ++) { 42 Connection conn = DriverManager.getConnection(url, user, password) ; 43 pool.add(conn) ; 44 } 45 } catch (Exception e) { 46 throw new RuntimeException(e) ; 47 } 48 } 49 public MyDataSource () { 50 51 } 52 53 //、從連接池獲取連接:通過動態代理 54 public Connection getConnection() throws SQLException { 55 if (pool.size() > 0) { 56 final Connection conn = pool.remove() ; 57 Connection proxyCon = (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), 58 new InvocationHandler() { 59 //策略設計模式: 60 @Override 61 public Object invoke(Object proxy, Method method, Object[] args) 62 throws Throwable { 63 if("close".equals(method.getName())){ 64 //誰調用, 65 return pool.add(conn);//當調用close方法時,攔截了,把鏈接放回池中了 66 }else{ 67 return method.invoke(conn, args); 68 } 69 } 70 }); 71 return proxyCon ; 72 }else { 73 throw new RuntimeException("服務器繁忙!"); 74 } 75 } 76 77 public int getLength() { 78 return pool.size() ; 79 } 80 81 82 @Override 83 public PrintWriter getLogWriter() throws SQLException { 84 return null; 85 } 86 @Override 87 public void setLogWriter(PrintWriter out) throws SQLException { 88 89 } 90 @Override 91 public void setLoginTimeout(int seconds) throws SQLException { 92 93 } 94 @Override 95 public int getLoginTimeout() throws SQLException { 96 return 0; 97 } 98 @Override 99 public <T> T unwrap(Class<T> iface) throws SQLException { 100 return null; 101 } 102 @Override 103 public boolean isWrapperFor(Class<?> iface) throws SQLException { 104 return false; 105 } 106 @Override 107 public Connection getConnection(String username, String password) 108 throws SQLException { 109 return null; 110 } 111 }
當然覺得麻煩的則可以直接使用一些開源的數據源如:DBCP、C3P0等。DBCP的原理是用包裝設計模式開發的數據源,而C3P0則是動態代理的。
1、DBCP的使用:
1 import java.io.InputStream; 2 import java.sql.Connection; 3 import java.sql.SQLException; 4 import java.util.Properties; 5 6 import javax.sql.DataSource; 7 8 import org.apache.commons.dbcp.BasicDataSourceFactory; 9 10 /** 11 * 創建DBCP 工具類 12 * @author 賀佐安 13 * 14 */ 15 public class DbcpUtil { 16 private static DataSource ds = null ; 17 static { 18 try { 19 //讀取配置文件 20 InputStream in = DbcpUtil.class.getClassLoader().getResourceAsStream("dbcpconfig.properties") ; 21 Properties prop = new Properties() ; 22 prop.load(in) ; 23 24 //通過BasicDataSourceFactory 的creatDataSurce 方法創建 BasicDataSource 對象。 25 ds = BasicDataSourceFactory.createDataSource(prop) ; 26 27 } catch (Exception e) { 28 e.printStackTrace(); 29 } 30 } 31 public static DataSource getDs() { 32 return ds ; 33 } 34 public static Connection getConnection () { 35 try { 36 return ds.getConnection() ; 37 } catch (SQLException e) { 38 throw new RuntimeException() ; 39 } 40 } 41 }
2、C3P0 的使用:
1 import java.sql.Connection; 2 import java.sql.SQLException; 3 4 import com.mchange.v2.c3p0.ComboPooledDataSource; 5 /** 6 * C3P0 開源數據源的使用 7 * @author 賀佐安 8 * 9 */ 10 public class C3p0Util { 11 private static ComboPooledDataSource cpds = null ; 12 static { 13 14 cpds = new ComboPooledDataSource() ; 15 } 16 public static Connection getConnection() { 17 try { 18 return cpds.getConnection() ; 19 } catch (SQLException e) { 20 throw new RuntimeException() ; 21 } 22 } 23 }
使用這兩個數據源時,直接調用獲取到的Connection 連接的close 方法,也是將連接放到pool中去。
三、元數據(DatabaseMetaData)信息的獲取
> 元數據:數據庫、表、列的定義信息。
> 元數據信息的獲取:為了編寫JDBC框架使用。
1、數據庫本身信息的獲取:java.sql.DataBaseMateData java.sql.Connection.getMetaData() ;
DataBaseMateData 實現類的常用方法:
getURL():返回一個String類對象,代表數據庫的URL。
getUserName():返回連接當前數據庫管理系統的用戶名。
getDatabaseProductName():返回數據庫的產品名稱。
getDatabaseProductVersion():返回數據庫的版本號。
getDriverName():返回驅動驅動程序的名稱。
getDriverVersion():返回驅動程序的版本號。
isReadOnly():返回一個boolean值,指示數據庫是否只允許讀操作。
2、ParameterMetaData: 代表PerparedStatment 中的SQL 參數元數據信息: java.sql.ParameterMetaData java.sql.PerparedStatement.getParameterMetaData() ;
ParameterMetaData 實現類常用方法:
getParameterCount() :獲得指定參數的個數
getParameterType(int param) :獲得指定參數的sql類型(驅動可能不支持)
3、ResultSetMetaData : 代表結果集的源數據信息:相當於SQL 中的 :DESC java.sql.ResultSetMetaData java.sql.ResultSet.getMetaData() ;
java.sql.ResultSetMetaData 接口中常用的方法:
a) getColumnCount() : 獲取查詢方法有幾列。
b) getColumnName(int index) : 獲取列名:index從1開始。
c) getColumnType(int index) : 獲取列的數據類型。返回的是TYPES 中的常量值。
四、編寫自己的JDBC框架:
JDBC框架的基本組成:
1、核心類:
a、定義一個指定javax.sql.DataSource 實例的引用變量,通過構造函數獲取指定的實例並給定義的變量。
b、編寫SQL運行框架。
DML 語句的編寫:
1、通過獲取的javax.sql.DataSource 實例,獲取Connection 對象。
2、通過ParamenterMeteData 獲取數據庫元數據。
DQL 語句的編寫:
1、通過獲取的DataSource 實例,獲取Connection 對象。
2、通過ParamenterMeteData、ResultSetMetaData 等獲取數據庫元數據。
3、用抽象策略設計模式:設計一個ResultSetHandler 接口,作用:將查找出的數據封裝到指定的JavaBean中。
|————這里的JavaBean,由用戶來指定。
抽象策略模式,用戶可以更具具體的功能來擴展成具體策略設計模式。如:查找的一條信息、查找的所有信息。
1 import java.sql.Connection; 2 import java.sql.ParameterMetaData; 3 import java.sql.PreparedStatement; 4 import java.sql.ResultSet; 5 import java.sql.SQLException; 6 import java.sql.Statement; 7 8 import javax.sql.DataSource; 9 10 /** 11 * 實現JDBC 框架的核心類。 12 * 在該類中定義了SQL語句完成的方法; 13 * @author 賀佐安 14 * 15 */ 16 public class MyJdbcFrame { 17 /** 18 * javax.sql.DataSource 實例的引用變量 19 */ 20 private DataSource ds = null ; 21 /** 22 * 將用戶指定的DataSource 指定給系統定義的DataSource 實例的引用變量 23 * @param ds 24 */ 25 public MyJdbcFrame(DataSource ds ) { 26 this.ds = ds ; 27 } 28 /** 29 * 執行UPDATE、DELETE、INSERT 語句。 30 * @param sql 31 * @param obj 32 */ 33 public void update(String sql , Object[] obj) { 34 Connection conn = null ; 35 PreparedStatement stmt = null ; 36 try { 37 //獲取Connection 對象 38 conn = ds.getConnection() ; 39 stmt = conn.prepareStatement(sql) ; 40 41 // 獲取ParameterMetaData 元數據對象。 42 ParameterMetaData pmd = stmt.getParameterMetaData() ; 43 44 //獲取SQL語句中需要設置的參數的個數 45 int parameterCount = pmd.getParameterCount() ; 46 if (parameterCount > 0) { 47 if (obj == null || obj.length != parameterCount) { 48 throw new MyJdbcFrameException( "parameterCount is error!") ; 49 } 50 //設置參數: 51 for ( int i = 0 ; i < obj.length ; i++) { 52 stmt.setObject(i+1, obj[i]) ; 53 } 54 } 55 //執行語句: 56 stmt.executeUpdate() ; 57 58 } catch(Exception e ) { 59 throw new MyJdbcFrameException(e.getMessage()) ; 60 } finally { 61 release(stmt, null, conn) ; 62 } 63 } 64 65 public Object query(String sql , Object[] obj , ResultSetHandler rsh) { 66 Connection conn = null ; 67 PreparedStatement stmt = null ; 68 ResultSet rs = null ; 69 try { 70 //獲取Connection 對象 71 conn = ds.getConnection() ; 72 stmt = conn.prepareStatement(sql) ; 73 74 // 獲取ParameterMetaData 元數據對象。 75 ParameterMetaData pmd = stmt.getParameterMetaData() ; 76 77 //獲取SQL語句中需要設置的參數的個數 78 int parameterCount = pmd.getParameterCount() ; 79 80 if (obj.length != parameterCount) { 81 throw new MyJdbcFrameException( "'" +sql +"' : parameterCount is error!") ; 82 } 83 //設置參數: 84 for ( int i = 0 ; i < obj.length ; i++) { 85 stmt.setObject(i+1, obj[i]) ; 86 } 87 //執行語句: 88 rs = stmt.executeQuery(); 89 90 return rsh.handler(rs); 91 } catch(Exception e ) { 92 throw new MyJdbcFrameException(e.getMessage()) ; 93 } finally { 94 release(stmt, null, conn) ; 95 } 96 } 97 /** 98 * 釋放資源 99 * @param stmt 100 * @param rs 101 * @param conn 102 */ 103 public static void release(Statement stmt 104 , ResultSet rs 105 , Connection conn) { 106 if(rs != null) { 107 try { 108 rs.close() ; 109 } catch (SQLException e) { 110 e.printStackTrace(); 111 } 112 rs = null ; 113 } 114 if (stmt != null) { 115 try { 116 stmt.close(); 117 } catch (SQLException e) { 118 e.printStackTrace(); 119 } 120 stmt = null ; 121 } 122 if (conn != null) { 123 try { 124 conn.close(); 125 } catch (SQLException e) { 126 e.printStackTrace(); 127 } 128 conn = null ; 129 } 130 } 131 132 }
2、接口:策略模式的接口:ResultSetHandler 。
1 import java.sql.ResultSet; 2 3 //抽象策略模式 4 public interface ResultSetHandler { 5 public Object handler(ResultSet rs) ; 6 }
這里對ResultSetHandler 接口實現一個BeanHandler 實例 :
1 import java.lang.reflect.Field; 2 import java.sql.ResultSet; 3 import java.sql.ResultSetMetaData; 4 5 /** 6 * 該類獲取ResultSet 結果集中的第一個值,封裝到JavaBean中 7 * @author 賀佐安 8 * 9 */ 10 public class BeanHandler implements ResultSetHandler { 11 //獲取要封裝的JavaBean的字節碼 12 private Class clazz ; 13 public BeanHandler (Class clazz) { 14 this.clazz = clazz ; 15 } 16 17 public Object handler(ResultSet rs) { 18 try { 19 if (rs.next()) { 20 //1、獲取結果集的元數據。 21 ResultSetMetaData rsm = rs.getMetaData() ; 22 //2、創建JavaBean的實例: 23 Object obj = clazz.newInstance() ; 24 //3、將數據封裝到JavaBean中。 25 for (int i = 0 ; i < rsm.getColumnCount() ; i ++) { 26 //獲取屬性名 27 String columnName = rsm.getColumnName(i+1) ; 28 //獲取屬性值 29 Object value = rs.getObject(i+1) ; 30 31 Field objField = obj.getClass().getDeclaredField(columnName) ; 32 objField.setAccessible(true) ; 33 objField.set(obj, value) ; 34 } 35 return obj ; 36 } else { 37 return null ; 38 } 39 } catch (Exception e) { 40 throw new RuntimeException(e) ; 41 } 42 } 43 }
3、自定義異常類:繼承RuntimeException。如:
1 public class MyJdbcFrameException extends RuntimeException { 2 public MyJdbcFrameException() { 3 super() ; 4 } 5 public MyJdbcFrameException(String e) { 6 super(e) ; 7 } 8 }
然后就可以將其打包發布,在以后寫數據庫操作時就可以用自己的JDBC框架了,如果要完成查詢多條語句什么的,則要實現ResultSetHandler 接口。來完成更多的功能。
當然,使用DBUtils 則更簡單:Apache 組織提供的一個開源JDBC 工具類庫。
--------------------------------------------------------------------------------------學習時的總結。2013年7月18日 19:29:44