1. 程式人生 > >使用Apache Commons Pool實現資料庫連線池

使用Apache Commons Pool實現資料庫連線池

通過組合Apache Commons Pool提供的GenericObjectPool實現資料庫連線池

import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import java.io.Closeable;
import java.sql.Connection;

public class DbPool implements Closeable {
    private GenericObjectPool<Connection> internalPool;

    public DbPool(GenericObjectPoolConfig poolConfig, PooledObjectFactory factory){
        if (this.internalPool != null) {
            try {
                closeInternalPool();
            } catch (Exception e) {
            }
        }
        this.internalPool = new GenericObjectPool(factory, poolConfig);
    }

    // 獲取連線
    public Connection getConnection(){
        Connection connection = null;
        try {
            connection = internalPool.borrowObject();
        } catch (Exception e) {
            throw new RuntimeException("Could not get connection from the pool", e);
        }
        return connection;
    }

    // 返還連線
    public void returnConnection(Connection connection){
        internalPool.returnObject(connection);
    }

    @Override
    public void close(){
        this.closeInternalPool();
    }

    private void closeInternalPool() {
        try {
            internalPool.close();
        } catch (Exception e) {
            throw new RuntimeException("Could not destroy the pool", e);
        }
    }
}

borrowObject的實現方式如下

public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
        implements ObjectPool<T>, GenericObjectPoolMXBean, UsageTracking<T> {

    private final PooledObjectFactory<T> factory;
    private final LinkedBlockingDeque<PooledObject<T>> idleObjects;

    public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
        ......
        p = idleObjects.pollFirst();
        if (p == null) {
            p = create();
            if (p != null) {
                create = true;
            }
        }
        ......
        return p.getObject();
    }
	
    private PooledObject<T> create() throws Exception {
        ......
        p = factory.makeObject(); 
        ......
        return p;
    }
}

物件池中並不直接儲存最終使用的物件例項,而是使用PooledObject對最終使用的物件例項進行了一層封裝

通過封裝物件給物件例項增加了諸如狀態、時間等一系列屬性

DbConnectionFactory用來定義物件池中的物件例項的生命週期方法(建立、銷燬等),通過繼承Apache Commons Pool提供的BasePooledObjectFactory實現

import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;

import java.sql.Connection;
import java.sql.DriverManager;

public class DbConnectionFactory extends BasePooledObjectFactory<Connection> {

    private DbConfig dbConfig;

    public DbConnectionFactory(DbConfig dbConfig){
        super();
        this.dbConfig = dbConfig;
    }

    // 建立新的資料庫連線
    public Connection create() throws Exception {
        Connection connection = DriverManager.getConnection(dbConfig.getUrl(),
                dbConfig.getUsername(), dbConfig.getPassword());
        return connection;
    }

    // 使用PooledObject對資料庫連線進行包裝
    public PooledObject<Connection> wrap(Connection connection) {
        return new DefaultPooledObject(connection);
    }

    // 由於validateObject失敗或其它什麼原因,物件例項從物件池中移除時呼叫
    // 不能保證物件例項被移除時所處的狀態
    public void destroyObject(PooledObject<Connection> pooledConnection) throws Exception {
        Connection connection = pooledConnection.getObject();
        connection.close();
    }

    // 僅能被active狀態的物件例項呼叫
    // 從物件池獲取物件例項,在物件池返回該物件例項前,呼叫該方法校驗其狀態
    // 物件例項歸還給物件池時,在呼叫passivateObject方法前,使用該方法校驗其狀態
    public boolean validateObject(PooledObject<Connection> pooledConnection) {
        Connection connection = pooledConnection.getObject();
        try {
            if (connection.isValid(1))
                return true;
            else
                return false;
        }catch(Exception e){
            return false;
        }
    }

    // 物件例項在歸還給物件池時呼叫了passivateObject方法,通過物件池再次取到該物件例項
    // 在物件池返回該物件例項前,需要呼叫該方法
    public void activateObject(PooledObject<Connection> pooledConnection) throws Exception {

    }

    // 物件例項歸還給物件池時呼叫
    public void passivateObject(PooledObject<Connection> pooledConnection) throws Exception {

    }
}

DbConfig用來儲存資料庫配置

public class DbConfig {

    private String url;
    private String username;
    private String password;

    public DbConfig(String driver, String url, String username,
                    String password){
        try {
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        this.url = url;
        this.username = username;
        this.password = password;
    }

    public String getUrl() {
        return url;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}

DbPoolConfig用來儲存物件池配置,通過繼承Apache Commons Pool提供的GenericObjectPoolConfig實現

提供了很多物件池相關的配置屬性以供使用

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

public class DbPoolConfig extends GenericObjectPoolConfig {

    public DbPoolConfig(){
        setMaxTotal(10);
        setMaxIdle(5);
        setMinIdle(2);
    }
}

測試程式碼如下

import java.sql.Connection;
import java.sql.Statement;

public class Test {
    public static void main(String[] args) throws Exception {
        String driver = "com.mysql.jdbc.Driver";
        String url = "jdbc:mysql://192.168.137.128:3306/mydb";
        String username = "root";
        String password = "";

        DbConfig dbConnectionConfig = new DbConfig(
                driver, url, username, password);
        DbConnectionFactory dbConnectionFactory = new DbConnectionFactory(dbConnectionConfig);

        DbPoolConfig dbPoolConfig = new DbPoolConfig();
        dbPoolConfig.setMaxTotal(10);
        dbPoolConfig.setMaxIdle(5);
        dbPoolConfig.setMinIdle(2);
        dbPoolConfig.setMaxWaitMillis(200);
        dbPoolConfig.setTestOnBorrow(false);
        dbPoolConfig.setTestOnReturn(false);

        DbPool dbPool = new DbPool(dbPoolConfig, dbConnectionFactory);

        for(int i = 0 ; i < 20 ; i++){
            Connection connection = dbPool.getConnection();
            Statement statement = connection.createStatement();
            statement.execute("insert into t_person(name, age) values ('a', 20)");
            dbPool.returnConnection(connection);
        }
    }
}