編寫一個簡單的數據庫連接池

爲什麼要使用數據庫連接池

使用jdbc最大的開銷之一就是創建數據庫,當我們頻繁的創建數據庫時,勢必影響應用的效率,或者在數據庫關閉出現問題時,我們不能馬上釋放,時間長一些,整個數據庫的 資源將會被我們的應用耗盡,這是一個很危險的訊息,怎樣將數據庫連接創建帶來的開銷降到最低呢,就是我們實現將一系列創建完畢的連接存放起來,等到使用的時候再來使用,這就是數據庫連接池解決的最大問題和存在的最大必要。


數據庫連接池雛形

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

public class SimpleDataSource {

    private static LinkedList<Connection> connPool = null;

    static {
        try{
            Class.forName("com.mysql.jdbc.Driver");
            connPool = new LinkedList<Connection>();
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
    }

    public SimpleDataSource(){
        init();
    }

    private void init(){
        for(int i = 0;i<10;i++){
            try {
                Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8", "root", "root");
                // 實現了靜態代理的Connection類
                Connection proxyConnection = new SimpleConnection(this,conn);
                connPool.addFirst(proxyConnection);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public Connection getConnection() throws Exception {
        synchronized (connPool){
            if (connPool.isEmpty()){
                throw new Exception("have no connection now!");
            }
            return connPool.removeLast();
        }
    }

    public void closeConn(Connection conn){
        synchronized (connPool) {
            connPool.addFirst(conn);
        }
    }

    public int getCurrentCount(){
        synchronized (connPool) {
            return connPool.size();
        }
    }

}

數據庫連接池優化

  • 對連接池加鎖,保證多線程併發時獲取的連接各不相干,修改其中的代碼片段如下
public Connection getConnection(){
        synchronized (connPool) {
            return connPool.removeLast();
        }
}
  • 連接不夠用時拋出異常
public Connection getConnection() throws Exception{
        synchronized (connPool) {
            if(connPool.isEmpty())
                throw new Exception("have no connection now!");
            return connPool.removeLast();
        }
}

數據庫連接池之代理模式

在使用JDBC連接數據庫的時候,最後需要用戶釋放資源,如果使用者按照傳統的方式關閉連接,那麼我們的連接池就沒有存在的意義了,因爲每一次使用者都會給關閉掉,導致連接池的連接會是無效的或者越來越少,爲了防止這樣的事情發生,我們需要保留使用者的使用習慣,也就是說允許使用者通過close方法釋放連接,這個時候我們應該如何做既能起到用戶的使用習慣,又能在進行關閉的時候不是真的關掉數據庫連接,而是直接存放至數據庫連接池中

  • 靜態代理

實現Connection接口,只需要關注將close方法實現爲自己的方法就可以


package util;

import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

public class SimpleConnection implements Connection {

    private SimpleDataSource dataSource = null;

    private Connection connection = null;

    public SimpleConnection(SimpleDataSource dataSource,Connection connection) {
        this.dataSource = dataSource;
        this.connection = connection;
    }


    @Override
    public Statement createStatement() throws SQLException {
       return connection.createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return connection.prepareStatement(sql);
    }

    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        return connection.prepareCall(sql);
    }

    @Override
    public String nativeSQL(String sql) throws SQLException {
        return connection.nativeSQL(sql);
    }

    @Override
    public void setAutoCommit(boolean autoCommit) throws SQLException {
        connection.setAutoCommit(autoCommit);
    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        return false;
    }

    @Override
    public void commit() throws SQLException {
        connection.commit();
    }

    @Override
    public void rollback() throws SQLException {
        connection.rollback();
    }

    @Override
    public void close() throws SQLException {
        dataSource.closeConn(this);
    }

    @Override
    public boolean isClosed() throws SQLException {
        return false;
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        return connection.getMetaData();
    }

    @Override
    public void setReadOnly(boolean readOnly) throws SQLException {
        connection.setReadOnly(readOnly);
    }

    @Override
    public boolean isReadOnly() throws SQLException {
        return false;
    }

    @Override
    public void setCatalog(String catalog) throws SQLException {
        connection.setCatalog(catalog);
    }

    @Override
    public String getCatalog() throws SQLException {
        return connection.getCatalog();
    }

    @Override
    public void setTransactionIsolation(int level) throws SQLException {
        connection.setTransactionIsolation(level);
    }

    @Override
    public int getTransactionIsolation() throws SQLException {
        return connection.getTransactionIsolation();
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        return connection.getWarnings();
    }

    @Override
    public void clearWarnings() throws SQLException {
        connection.clearWarnings();
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
        return connection.createStatement(resultSetType,resultSetConcurrency);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return connection.prepareCall(sql,resultSetType,resultSetConcurrency);
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
        return connection.prepareCall(sql,resultSetType,resultSetConcurrency);
    }

    @Override
    public Map<String, Class<?>> getTypeMap() throws SQLException {
        return connection.getTypeMap();
    }

    @Override
    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
        connection.setTypeMap(map);
    }

    @Override
    public void setHoldability(int holdability) throws SQLException {
        connection.setHoldability(holdability);
    }

    @Override
    public int getHoldability() throws SQLException {
        return connection.getHoldability();
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
        return connection.setSavepoint();
    }

    @Override
    public Savepoint setSavepoint(String name) throws SQLException {
        return connection.setSavepoint(name);
    }

    @Override
    public void rollback(Savepoint savepoint) throws SQLException {
        connection.rollback(savepoint);
    }

    @Override
    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
        connection.releaseSavepoint(savepoint);
    }

    @Override
    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return connection.createStatement(resultSetType,resultSetConcurrency,resultSetHoldability);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return connection.prepareCall(sql,resultSetType,resultSetConcurrency);
    }

    @Override
    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
        return connection.prepareCall(sql,resultSetType,resultSetConcurrency,resultSetHoldability);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
        return connection.prepareStatement(sql,autoGeneratedKeys);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
        return connection.prepareStatement(sql,columnIndexes);
    }

    @Override
    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
        return connection.prepareStatement(sql,columnNames);
    }

    @Override
    public Clob createClob() throws SQLException {
        return connection.createClob();
    }

    @Override
    public Blob createBlob() throws SQLException {
        return connection.createBlob();
    }

    @Override
    public NClob createNClob() throws SQLException {
        return connection.createNClob();
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
        return connection.createSQLXML();
    }

    @Override
    public boolean isValid(int timeout) throws SQLException {
        return false;
    }

    @Override
    public void setClientInfo(String name, String value) throws SQLClientInfoException {
        connection.setClientInfo(name, value);
    }

    @Override
    public void setClientInfo(Properties properties) throws SQLClientInfoException {
        connection.setClientInfo(properties);
    }

    @Override
    public String getClientInfo(String name) throws SQLException {
        return connection.getClientInfo(name);
    }

    @Override
    public Properties getClientInfo() throws SQLException {
        return connection.getClientInfo();
    }

    @Override
    public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
        return connection.createArrayOf(typeName, elements);
    }

    @Override
    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
        return connection.createStruct(typeName, attributes);
    }

    @Override
    public void setSchema(String schema) throws SQLException {
        connection.setSchema(schema);
    }

    @Override
    public String getSchema() throws SQLException {
        return connection.getSchema();
    }

    @Override
    public void abort(Executor executor) throws SQLException {
        connection.abort(executor);
    }

    @Override
    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
        connection.setNetworkTimeout(executor, milliseconds);
    }

    @Override
    public int getNetworkTimeout() throws SQLException {
        return connection.getNetworkTimeout();
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return connection.unwrap(iface);
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }
}
  • 動態代理
package util;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;

public class DynSimpleConnection implements InvocationHandler {

    private Connection realConnection = null;// Connection real

    private Connection warpConnection = null;

    private DynSimpleDataSource dataSource = null;

    public DynSimpleConnection(DynSimpleDataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Connection bind(Connection source){
        this.realConnection = source;
        this.warpConnection = (Connection) Proxy.newProxyInstance(source.getClass().getClassLoader(),source.getClass().getInterfaces(),this);
        return warpConnection;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String methodName = method.getName();
        Object obj = null;
        if(methodName.equals("close")){
            System.out.println("{{close}}");
            // 實現自己的邏輯
            dataSource.closeConn(warpConnection);
        }else {
            obj = method.invoke(realConnection,args);
        }
        return obj;
    }
}

數據庫連接池


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

public class DynSimpleDataSource {
    private static LinkedList<Connection> connPool = null;

    private static int minCount = 5;

    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            connPool = new LinkedList<Connection>();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public DynSimpleDataSource(){
        for(int i = 0;i<minCount;i++){
            try {
                Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8", "root", "root");
                DynSimpleConnection dynSimpleConnection = new DynSimpleConnection(this);
                Connection connWarp=dynSimpleConnection.bind(conn);
                connPool.addFirst(connWarp);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    public Connection getConnection() throws Exception{
        synchronized (connPool) {
            if(connPool.isEmpty())
                throw new Exception("have no connection now!");
            return connPool.removeLast();
        }
    }

    void closeConn(Connection conn) throws SQLException{
        synchronized (connPool) {
            connPool.addFirst(conn);
        }
    }

    /**
     * 獲取當前數據庫連接數
     * @return
     */
    public int getCurrentCount(){
        synchronized (connPool) {
            return connPool.size();
        }
    }

}

創建Connection的類


public class ConnCreate {

    public static DynSimpleDataSource dataSource = new DynSimpleDataSource();

    public static Connection getConnection() {
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }

    public static void close(Connection conn, Statement stmt, ResultSet rs)
            throws SQLException {

        if (null != rs) {
            rs.close();
        }
        if (null != stmt) {
            stmt.close();
        }
        if (null != conn) {
            dataSource.closeConn(conn);
        }
    }

}

測試

    @Test
    public void createDataSource2() throws Exception{
        for(int i = 0;i<20;i++){
            Connection conn=ConnCreate.getConnection();
            conn.close();
            System.out.println(conn);
        }
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章