数据库 -- 由数据库连接池引出的三种设计模式
筆記摘要:
? ? ?這里首先對數據庫連接池的優化進行了說明,同時自己編寫了一個數據庫連接池,在實際開發中,為了獲取標準的數據源,我們需要去實現javax.sal.DataSource接口,
? ? ?在實現過程中對于鏈接對象的close方法進行了不同的實現,以便在關閉close的時候,將連接對象放回連接池,而不是關閉掉,針對這一問題,提供了3種不同的解決
? ? ?方案,涉及了3種設計模式:裝飾,適配器和代理。
?
一、直接獲取連接與使用連接池的比較
應用程序直接獲取連接示意圖
?
?
缺點:
? ? 用戶每次請求都需要向數據庫獲得鏈接,而數據庫創建連接通常需要消耗相對較大的資源,創建時間也較長。假設網站一天10萬訪問量,數據庫服務器就需要創建10萬次連接,極大的浪費數據庫的資源,并且極易造成數據庫服務器內存溢出、拓機。
?
?數據庫連接池示意圖
?
優勢:
連接池中會有指定個數的連接對象,每次連接的時候,只要將請求發給連接池,連接池就會提供連接對象,使用完之后,再將連接對象放回連接池,
這樣不用每次都去連接,大大提高了性能。
?
二、編寫一個基本的連接池實現連接復用
?
原理:
通過一個LinkedList來模擬連接池,每次取Connection的時候,就remove,當釋放的時候就再add進去,這里通過打印remove的前后,來說明每次使用之后會放回“連接池”
dbinfo.properties
className=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/mydb user=root password=root?
自定義連接池
import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.Collections; import java.util.LinkedList; import java.util.ResourceBundle;import javax.management.RuntimeErrorException;public class SimpleConnectionPool {private static String className;private static String url;private static String user;private static String password;//創建一個集合用于模擬連接池private static LinkedList<Connection> pool = new LinkedList<Connection>(); // private static LinkedList<Connection> pool = (LinkedList<Connection>) Collections.synchronizedCollection(new LinkedList<Connection>());//加載配置文件并注冊驅動static{try {ResourceBundle bundle = ResourceBundle.getBundle("cn.itmonkey.util.dbinfo");className = bundle.getString("className");url = bundle.getString("url");user = bundle.getString("user");password = bundle.getString("password");Class.forName(className);//創建10個連接對象for(int i=0;i<10;i++){Connection conn = DriverManager.getConnection(url,user,password);pool.add(conn);}System.out.println("初始化連接");//打印所創建的連接對象int i=0;for(Connection conn: pool){System.out.println(conn+"..."+i++);}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (SQLException e) {e.printStackTrace();}}//獲取連接對象,由于可能同時會有多個對象來取,所以使用同步public synchronized static Connection getConnection(){System.out.println("取之前的連接如下");int i=0;for(Connection conn: pool){System.out.println(conn+"..."+i++);}//從隊列中移出一個連接對象,返回給調用者if(pool.size()>0){ //防止為0的時候,導致異常,所以要進行判斷Connection conn = pool.remove();return conn;}else{throw new RuntimeException("對不起,服務器忙!");}}//將連接對象釋放到隊列中public static void release(Connection conn){pool.addLast(conn);} }二、編寫標準的數據庫連接池
標準的數據源:
需要實現javax.sal.DataSource接口的類,標準的數據源里默認了維護了一個連接池
?
問題:
在直接獲取標準數據源的Connection后,在調用它的close方法時,不是還回連接池中,而是直接關閉,我們希望在調用close方法的時候,是將Connection還回連接池中,而不是關閉
?
解決方案一:裝飾設計模式
通過裝飾,對需要的已有功能進行增強
?
裝飾設計模式編寫步驟
1、編寫一個類,實現與被增強對象相同的接口
2、定義一個引用變量,記住被增強對象
3、定義構造方法,參數為接口后者父類,比便實現多態,傳入被增強對象, 并給第2部的變量賦值
4、對于要增強的方法,自己改寫
5、對于不需要增強的方法,調用原有對象的對應方法。
public class MyConnection implements Connection{private Connection conn;private LinkedList<Connection> pool;//模擬連接池public MyConnection(Connection conn,LinkedList<Connection> pool){this.conn = conn;this.pool = pool;}//釋放連接對象到連接池 @Overridepublic void close() throws SQLException {pool.add(conn);}@Overridepublic void clearWarnings() throws SQLException {conn.clearWarnings();}@Overridepublic void commit() throws SQLException {conn.commit();} //不需要增強的方法,調用原有對象的對應方法即可, @Overridepublic Array createArrayOf(String typeName, Object[] elements)throws SQLException {return conn.createArrayOf(typeName, elements);}//后面有很多方法,因為不需要增強的方法,調用原有對象的對應方法即可,這里略去 ………… }解決方案二:適配器模式
?通過一個類去繼承一個接口或父類,對需要增強的方法進行改寫即可,適配器模式在監聽機制中出現比較多,由于父類中有很多的抽象方法,如果一一實現,比較麻煩,所以通常在API中會提供一個已經默認實現的類,我們只需去繼承這個類,然后對希望增強的方法進行復寫即可
?
適配器模式編寫步驟
1.編寫一個類,繼承默認適配器
2.定義一個引用變量,記住被增強對象
3.定義構造方法,傳入被增強的對象,并給第2部的變量賦值
4.對于要增強的方法,自己改寫
?
為Connection準備的適配器
?適配器本身也是一個包裝類,實現或者繼承一個類,但是什么都不做,所有的方法都調用原有對象的對應方法
?
public class ConnectionWrapper implements Connection{protected Connection conn;public ConnectionWrapper(Connection conn){this.conn = conn;}@Overridepublic void clearWarnings() throws SQLException {conn.clearWarnings();}@Overridepublic void close() throws SQLException {conn.close();}@Overridepublic void commit() throws SQLException {conn.commit();}@Overridepublic Array createArrayOf(String typeName, Object[] elements)throws SQLException {return null;}………… }?
繼承適配器,對需要增強的方法復寫即可
public class MyConnection2 extends ConnectionWrapper {private LinkedList<Connection> pool;public MyConnection2(Connection conn, LinkedList<Connection> pool) {super(conn);this.pool = pool;} //將連接對象還回池中public void close() throws SQLException {pool.add(conn);} }解決方案三:動態代理
使用動態代理,在獲取Connection的方法中使用代理,所以在獲取Connection的時候,就是一個代理的Connection,該代理的Connection對象,會對調用方法進行判斷,如果是close方法,就對返回值進行改寫(這里是將Connection對象放回連接池中),否則就按照原來方法去執行
關于更多動態代理的知識,筆者在Java基礎里面有詳細地提到,其機制,實現等,想深刻了解的親們可以查看:http://www.cnblogs.com/xushuai123/archive/2012/12/02/2978070.html
import java.io.PrintWriter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.LinkedList; import java.util.ResourceBundle;import javax.sql.DataSource;//標準的數據源 public class ProxyDataSource implements DataSource{private static String className;// 驅動類名private static String url;// 連接串private static String user;private static String password;private static LinkedList<Connection> pool = new LinkedList<Connection>();static {try {ResourceBundle rb = ResourceBundle.getBundle("cn.itxushuai.util.dbinfo");className = rb.getString("className");url = rb.getString("url");user = rb.getString("user");password = rb.getString("password");Class.forName(className);// 初始化10個連接for (int i = 0; i < 10; i++) {Connection conn = DriverManager.getConnection(url, user,password);pool.add(conn);}} catch (Exception e) {throw new ExceptionInInitializerError("驅動加載失敗");}}@Overridepublic synchronized Connection getConnection() throws SQLException {if(pool.size()>0){System.out.println("池中的連接如下");int i=1;for(Connection conn : pool){System.out.println(conn+"----"+i++);}final Connection conn = pool.remove();Connection proxyConn = (Connection)Proxy.newProxyInstance(conn.getClass().getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler(){@Overridepublic Object invoke(Object proxy, Method method,Object[] args) throws Throwable {if("close".equals(method.getName())){ return pool.add(conn);}else{return method.invoke(conn, args);}}}); return proxyConn;}else{throw new RuntimeException("服務器忙");}}@Overridepublic Connection getConnection(String username, String password)throws SQLException {// TODO Auto-generated method stubreturn null;}@Overridepublic PrintWriter getLogWriter() throws SQLException {// TODO Auto-generated method stubreturn null;}@Overridepublic int getLoginTimeout() throws SQLException {// TODO Auto-generated method stubreturn 0;}@Overridepublic void setLogWriter(PrintWriter out) throws SQLException {// TODO Auto-generated method stub }@Overridepublic void setLoginTimeout(int seconds) throws SQLException {// TODO Auto-generated method stub }@Overridepublic boolean isWrapperFor(Class<?> iface) throws SQLException {// TODO Auto-generated method stubreturn false;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {// TODO Auto-generated method stubreturn null;}}?
?
轉載于:https://www.cnblogs.com/xushuai123/archive/2013/03/31/2992385.html
總結
以上是生活随笔為你收集整理的数据库 -- 由数据库连接池引出的三种设计模式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: O(n*m)复杂度的多重背包coinsP
- 下一篇: linux cmake编译源码,linu