c3p0连接池用法
使用連接池的時候并不是在代碼中不用獲取/釋放數據庫連接,而是在代碼中向連接池申請/釋放連接,對于代碼而言,可以把連接池看成數據庫。
換句話說,連接池就是數據庫的代理,之所以要使用這個代理是因為直接向數據庫申請/釋放連接是要降低性能的:如果每一次數據訪問請求都必須經歷建立數據庫連接、打開數據庫、存取數據和關閉數據庫連接等步驟,而連接并打開數據庫是一件既消耗資源又費時的工作,那么頻繁發生這種數據庫操作時,系統的性能必然會急劇下降。
連接池的作用是自己維護數據庫連接,數據庫連接池的主要操作如下:
(1)建立數據庫連接池對象(服務器啟動)。
(2)按照事先指定的參數創建初始數量的數據庫連接(即:空閑連接數)。
(3)對于一個數據庫訪問請求,直接從連接池中得到一個連接。如果數據庫連接池對象中沒有空閑的連接,且連接數沒有達到最大(即:最大活躍連接數),創建一個新的數據庫連接。
(4)存取數據庫。
(5)關閉數據庫,釋放所有數據庫連接(此時的關閉數據庫連接,并非真正關閉,而是將其放入空閑隊列中。如實際空閑連接數大于初始空閑連接數則釋放連接)。
(6)釋放數據庫連接池對象(服務器停止、維護期間,釋放數據庫連接池對象,并釋放所有連接)。
從連接池獲取的連接connection跟JDK中的connection有點不同,前者的close方法并沒有關閉與數據庫的連接,而是將連接返回到池中,這樣就可以復用了。如果不調用close方法的話拿就失去了使用連接池的意義了。
開源連接池有很多:DBCP、C3P0、Proxool 、 BoneCP等
C3P0是一個開放源代碼的JDBC連接池,它在lib目錄中與Hibernate一起發布,包括了實現jdbc3和jdbc2擴展規范說明的Connection 和Statement 池的DataSources 對象。
- 下載c3p0的jar,并添加log4j.jar.
- 采用ThreadLocal線程局部變量保證線程安全.
使用連接池和不使用連接池時的性能差異簡單的C3P0使用測試示例
package com.lnbdqn;import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.SQLException; import com.mchange.v2.c3p0.ComboPooledDataSource;public final class ConnectionManager {private static ConnectionManager instance;private static ComboPooledDataSource dataSource;private ConnectionManager() throws SQLException, PropertyVetoException {dataSource = new ComboPooledDataSource();dataSource.setUser("loux");dataSource.setPassword("loux");dataSource.setJdbcUrl("jdbc:oracle:thin:@192.168.100.70:1521:orcl");dataSource.setDriverClass("oracle.jdbc.driver.OracleDriver");dataSource.setInitialPoolSize(5);dataSource.setMinPoolSize(1);dataSource.setMaxPoolSize(10);dataSource.setMaxStatements(50);dataSource.setMaxIdleTime(60);}public static final ConnectionManager getInstance() {if (instance == null) {try {instance = new ConnectionManager();} catch (Exception e) {e.printStackTrace();}}return instance;}public synchronized final Connection getConnection() {Connection conn = null;try {conn = dataSource.getConnection();} catch (SQLException e) {e.printStackTrace();}return conn;} }
package com.lnbdqn;import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException;import oracle.jdbc.pool.OracleDataSource;public class ConnectionDemo {public static void main(String[] args) throws SQLException {System.out.println("使用連接池................................");for (int i = 0; i < 20; i++) {long beginTime = System.currentTimeMillis();Connection conn = ConnectionManager.getInstance().getConnection();try {PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM t_fmscpy200");ResultSet rs = pstmt.executeQuery();while (rs.next()) {}} catch (SQLException e) {e.printStackTrace();} finally {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}long endTime = System.currentTimeMillis();System.out.println("第" + (i + 1) + "次執行花費時間為:" + (endTime - beginTime));}System.out.println("不使用連接池................................");for (int i = 0; i < 20; i++) {long beginTime = System.currentTimeMillis();OracleDataSource ods = new OracleDataSource();ods.setUser("loux");ods.setPassword("loux");ods.setURL("jdbc:oracle:thin:@192.168.100.70:1521:orcl");Connection conn = ods.getConnection();try {PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM table_name");ResultSet rs = pstmt.executeQuery();while (rs.next()) {// do nothing... }} catch (SQLException e) {e.printStackTrace();} finally {try {conn.close();} catch (SQLException e) {e.printStackTrace();}}long endTime = System.currentTimeMillis();System.out.println("第" + (i + 1) + "次執行花費時間為:"+ (endTime - beginTime));}} }
控制臺輸出的結果為:使用連接池................................ 第1次執行花費時間為:1469 第2次執行花費時間為:0 第3次執行花費時間為:16 第4次執行花費時間為:0 第5次執行花費時間為:0 第6次執行花費時間為:15 第7次執行花費時間為:0 第8次執行花費時間為:0 第9次執行花費時間為:0 第10次執行花費時間為:0 第11次執行花費時間為:16 第12次執行花費時間為:0 第13次執行花費時間為:0 第14次執行花費時間為:0 第15次執行花費時間為:0 第16次執行花費時間為:16 第17次執行花費時間為:0 第18次執行花費時間為:0 第19次執行花費時間為:15 第20次執行花費時間為:0 不使用連接池................................ 第1次執行花費時間為:47 第2次執行花費時間為:31 第3次執行花費時間為:32 第4次執行花費時間為:46 第5次執行花費時間為:32 第6次執行花費時間為:31 第7次執行花費時間為:47 第8次執行花費時間為:31 第9次執行花費時間為:47 第10次執行花費時間為:31 第11次執行花費時間為:47 第12次執行花費時間為:31 第13次執行花費時間為:32 第14次執行花費時間為:46 第15次執行花費時間為:47 第16次執行花費時間為:32 第17次執行花費時間為:46 第18次執行花費時間為:47 第19次執行花費時間為:32 第20次執行花費時間為:31
可以看出,在使用連接池時,第一次執行花費的時間稍長,因為第一次初始化操作需要創建多個連接并放入池中,以后使用時將會大大縮短執行時間。 在不使用連接池時,每次花費的時間都比較長。
下面是一個service層的銀行轉賬方法
public void transferMoneyNew(int from,int to,float money) throws Exception{AccountDAOImpl dao = null;try{/*完成的功能:1.從數據源中獲取Connection2.開啟事務3. 放到線程上*/JDBCUtils.startTransaction(); //創建DAOdao = new AccountDAOImpl(); //獲取賬戶信息Account fromAccount = dao.findAccountByID(from);Account toAccount = dao.findAccountByID(to);//扣錢和加錢fromAccount.setMoney(fromAccount.getMoney() - money);toAccount.setMoney(toAccount.getMoney() + money); //更新數據庫 dao.updateAccount(fromAccount); //產生錯誤int i=1/0; dao.updateAccount(toAccount); //提交 JDBCUtils.commit();}catch(Exception ex){JDBCUtils.rollback();throw new ServiceException(ex);}finally{JDBCUtils.release();}} JDBCUtils類 public class JDBCUtils {//連接的容器public static ThreadLocal<Connection> container = new ThreadLocal<Connection>();//定義c3p0 數據源private static DataSource ds = new ComboPooledDataSource();/*完成的功能:1.從數據源中獲取Connection2.開啟事務3. 放到線程上*/public static void startTransaction() throws SQLException{Connection conn = container.get();//當前線程上是否已經存在連接if(conn == null){conn = ds.getConnection();}//開啟事務conn.setAutoCommit(false);//放到當前線程上 container.set(conn);}//提交當前線程上的連接public static void commit() throws SQLException{Connection conn = container.get();if(conn != null){conn.commit();}}//回滾當前線程上的連接public static void rollback() throws SQLException{Connection conn = container.get();if(conn != null){conn.rollback();}}//釋放當前線程上的連接public static void release() throws SQLException{Connection conn = container.get();if(conn != null){//從當前線程上,拿掉連接 container.remove();conn.close();} }//返回數據源public static DataSource getDataSource(){return ds;}public static Connection getConnection() throws SQLException {return ds.getConnection();}//釋放資源public static void release(Connection conn,Statement st,ResultSet rs){if(rs != null){try {rs.close();} catch (SQLException e) {throw new RuntimeException(e);}finally{rs = null;}} if(st != null){try {st.close();} catch (SQLException e) {throw new RuntimeException(e);}finally{st = null;}}if(conn != null){try {conn.close();} catch (SQLException e) {throw new RuntimeException(e);}finally{conn = null;}} } }
OSChina 的 DBManager 類?管理數據庫連接
public class DBManager {private final static Log log = LogFactory.getLog(DBManager.class);private final static ThreadLocal<Connection> conns = new ThreadLocal<Connection>();private static DataSource dataSource;private static boolean show_sql = false;static {initDataSource(null);}/*** 初始化連接池* @param props* @param show_sql*/private final static void initDataSource(Properties dbProperties) {try {if(dbProperties == null){dbProperties = new Properties();dbProperties.load(DBManager.class.getResourceAsStream("db.properties"));}Properties cp_props = new Properties();for(Object key : dbProperties.keySet()) {String skey = (String)key;if(skey.startsWith("jdbc.")){String name = skey.substring(5);cp_props.put(name, dbProperties.getProperty(skey));if("show_sql".equalsIgnoreCase(name)){show_sql = "true".equalsIgnoreCase(dbProperties.getProperty(skey));}}}dataSource = (DataSource)Class.forName(cp_props.getProperty("datasource")).newInstance();if(dataSource.getClass().getName().indexOf("c3p0")>0){//Disable JMX in C3P0System.setProperty("com.mchange.v2.c3p0.management.ManagementCoordinator", "com.mchange.v2.c3p0.management.NullManagementCoordinator");}log.info("Using DataSource : " + dataSource.getClass().getName());BeanUtils.populate(dataSource, cp_props);Connection conn = getConnection();DatabaseMetaData mdm = conn.getMetaData();log.info("Connected to " + mdm.getDatabaseProductName() + " " + mdm.getDatabaseProductVersion());closeConnection();} catch (Exception e) {throw new DBException(e);}}/*** 斷開連接池*/public final static void closeDataSource(){try {dataSource.getClass().getMethod("close").invoke(dataSource);} catch (NoSuchMethodException e){ } catch (Exception e) {log.error("Unabled to destroy DataSource!!! ", e);}}public final static Connection getConnection() throws SQLException {Connection conn = conns.get();if(conn ==null || conn.isClosed()){conn = dataSource.getConnection();conns.set(conn);}return (show_sql && !Proxy.isProxyClass(conn.getClass()))?new _DebugConnection(conn).getConnection():conn;}/*** 關閉連接*/public final static void closeConnection() {Connection conn = conns.get();try {if(conn != null && !conn.isClosed()){conn.setAutoCommit(true);conn.close();}} catch (SQLException e) {log.error("Unabled to close connection!!! ", e);}conns.set(null);}/*** 用于跟蹤執行的SQL語句* @author Winter Lau*/static class _DebugConnection implements InvocationHandler {private final static Log log = LogFactory.getLog(_DebugConnection.class);private Connection conn = null;public _DebugConnection(Connection conn) {this.conn = conn;}/*** Returns the conn.* @return Connection*/public Connection getConnection() {return (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), this);}public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {try {String method = m.getName();if("prepareStatement".equals(method) || "createStatement".equals(method))log.info("[SQL] >>> " + args[0]); return m.invoke(conn, args);} catch (InvocationTargetException e) {throw e.getTargetException();}}}}
# DataSource jdbc.datasource=com.mchange.v2.c3p0.ComboPooledDataSource jdbc.show_sql=true# Database Configurations jdbc.driverClass=com.mysql.jdbc.Driver jdbc.jdbcUrl=jdbc:mysql://localhost:3306/oscdb jdbc.user=root jdbc.password=xxxx jdbc.maxPoolSize=100 jdbc.minPoolSize=2 jdbc.initialPoolSize=2 jdbc.acquireIncrement=2 jdbc.maxStatements=1000 jdbc.maxIdleTime=300 jdbc.checkoutTimeout=5000
參數配置例子
package com.wb.db; import java.beans.PropertyVetoException; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; /** * 采用ThreadLocal線程局部變量保證線程安全 * @author hemes1314 */ public class C3p0Pool {public static ThreadLocal connectionHolder = new ThreadLocal(); private static DataSource dataSource;public C3p0Pool(){ } public static Connection getConnection() {Connection conn = (Connection) connectionHolder.get();//如果在當前線程中沒有綁定相應的Connectionif(conn==null){if (dataSource == null) { initDataSource();} try { conn = dataSource.getConnection(); //將Connection設置到ThreadLocal線程變量中 connectionHolder.set(conn); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }return conn; }public static void closeConnection(){Connection conn = (Connection) connectionHolder.get();if(conn!=null){try {conn.close();//從ThreadLocal中清除Connection connectionHolder.remove();} catch (SQLException e) {// TODO Auto-generated catch block e.printStackTrace();}}}public static void initDataSource(){ String driverClassName=null; String url=null; String username=null; String password=null; int initialPoolSize=3; int maxPoolSize=15;int minPoolSize=5;int acquireRetryDelay=1000;int maxIdleTime=60;Configuration config=new Configuration("oraConn.properties");driverClassName = config.getValue("driver"); url = config.getValue("url");username = config.getValue("user");password = config.getValue("password"); initialPoolSize = Integer.parseInt(config.getValue("initialPoolSize").trim()); maxPoolSize = Integer.parseInt(config.getValue("maxPoolSize").trim());minPoolSize = Integer.parseInt(config.getValue("minPoolSize").trim());maxIdleTime = Integer.parseInt(config.getValue("maxIdleTime").trim()); ComboPooledDataSource cpds = new ComboPooledDataSource(); try {cpds.setDriverClass(driverClassName);} catch (PropertyVetoException e) {// TODO Auto-generated catch block e.printStackTrace();}cpds.setUser(username); cpds.setPassword(password); cpds.setJdbcUrl(url);//初始化時獲取三個連接,取值應在minPoolSize與maxPoolSize之間。Default: 3 initialPoolSize cpds.setInitialPoolSize(initialPoolSize); //連接池中保留的最大連接數。Default: 15 maxPoolSize cpds.setMaxPoolSize(maxPoolSize);//連接池中保留的最小連接數。 cpds.setMinPoolSize(minPoolSize);//獲得連接的最大等待毫秒數。Default: 1000 acquireRetryDelay cpds.setAcquireRetryDelay(acquireRetryDelay);//最大空閑時間,60秒內未使用則連接被丟棄。若為0則永不丟棄。Default: 0 maxIdleTime cpds.setMaxIdleTime(maxIdleTime);//當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 acquireIncrement //cpds.setAcquireIncrement(3); //每60秒檢查所有連接池中的空閑連接。Default: 0 idleConnectionTestPeriod //cpds.setIdleConnectionTestPeriod(60);//連接關閉時默認將所有未提交的操作回滾。Default: false autoCommitOnClose //cpds.setAutoCommitOnClose(true);//JDBC的標準參數,用以控制數據源內加載的PreparedStatements數量。但由于預緩存的statements屬于單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。如果maxStatements與maxStatementsPerConnection均為0,則緩存被關閉。Default: 0 //cpds.setMaxStatements(1);//maxStatementsPerConnection定義了連接池內單個連接所擁有的最大緩存statements數//cpds.setMaxStatementsPerConnection(100);//定義所有連接測試都執行的測試語句。在使用連接測試的情況下這個一顯著提高測試速度。注意:測試的表必須在初始數據源的時候就存在。Default: null preferredTestQuery //cpds.setPreferredTestQuery("select sysdate from dual"); // 因性能消耗大請只在需要的時候使用它。如果設為true那么在每個connection提交的 // 時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable // 等方法來提升連接測試的性能。Default: false testConnectionOnCheckout //cpds.setTestConnectionOnCheckout(true);//如果設為true那么在取得連接的同時將校驗連接的有效性。Default: false testConnectionOnCheckin //cpds.setTestConnectionOnCheckin(true); //定義在從數據庫獲取新連接失敗后重復嘗試的次數。Default: 30 acquireRetryAttempts //cpds.setAcquireRetryAttempts(30); //獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效 //保留,并在下次調用getConnection()的時候繼續嘗試獲取連接。如果設為true,那么在嘗試 //獲取連接失敗后該數據源將申明已斷開并永久關閉。Default: false breakAfterAcquireFailure //cpds.setBreakAfterAcquireFailure(false); dataSource = cpds; }/* 用于測試連接狀態的方法*/public static void main(String[] args) {ComboPooledDataSource ds=(ComboPooledDataSource)dataSource; try {System.out.println(ds.getConnection());} catch (SQLException e) {// TODO Auto-generated catch block e.printStackTrace();}//System.out.println(ds.getInitialSize()); //System.out.println(ds.getNumActive()); //System.out.println(ds.getNumIdle()); //System.out.println(ds.getDefaultAutoCommit()); }}
轉載于:https://www.cnblogs.com/janko208/archive/2012/08/27/2658628.html
總結
- 上一篇: 想问下是男的渣还是女的不自爱,到底谁的错
- 下一篇: ((ios开发学习笔记九)) Simpl