package com.summer.dsconnpool.config;
import lombok.Data;
/**
* 数据源基础配置
*
* @author summer
* @version : DataSourceConfig.java, v 0.1 2022年05月15日 10:19 PM summer Exp $
*/
@Data
public class DataSourceConfig {
/**
* 驱动类
*/
protected String driverClass;
/**
* jdbc url
*/
protected String jdbcUrl;
/**
* 用户名
*/
protected String user;
/**
* 密码
*/
protected String password;
}
package com.summer.dsconnpool.config;
import lombok.Data;
/**
* 池化相关配置
*
* @author summer
* @version : PooledDataSourceConfig.java, v 0.1 2022年05月15日 10:27 PM summer Exp $
*/
@Data
public class PooledDataSourceConfig extends DataSourceConfig{
/**
* 最小数量
*/
protected int minSize = 10;
/**
* 最大数量
*/
protected int maxSize = 200;
/**
* 最大的等待时间,5S
*/
protected long maxWaitMills = 10 * 1000;
/**
* 验证查询的语句
*
* 参考:https://docs.oracle.com/cd/B19306_01/server.102/b14200/queries009.htm
*/
protected String validQuery = "select 1 from dual";
/**
* 验证的超时时间,5S
*/
protected int validTimeOutSeconds = 5;
/**
* 获取时是否验证连接url可用性
*/
protected boolean testOnBorrow = false;
/**
* 归还连接时是否验证
*/
protected boolean testOnReturn = true;
/**
* 闲暇时是否验证
*/
protected boolean testOnIdle = true;
/**
* 闲暇时验证的时间间隔,60S
*/
protected long testOnIdleIntervalSeconds = 60;
}
package com.summer.dsconnpool.connection;
import com.summer.dsconnpool.IPooledDataSource;
import java.sql.Connection;
/**
* 池化连接接口定义
*
* @author summer
* @version : IPooledConnection.java, v 0.1 2022年05月15日 10:29 PM summer Exp $
*/
public interface IPooledConnection extends Connection {
/**
* 是否繁忙
*
* @return
*/
boolean isBusy();
/**
* 设置是否繁忙状态
* @param busy
*/
void setBusy(boolean busy);
/**
* 获取真正的连接
*/
Connection getConnection();
/**
* 设置连接信息
*/
void setConnection(Connection connection);
/**
* 设置数据源信息
* @param dataSource
*/
void setDataSource(final IPooledDataSource dataSource);
}
package com.summer.dsconnpool.connection;
import com.summer.dsconnpool.IPooledDataSource;
import com.summer.dsconnpool.exception.ConnPoolException;
import lombok.Data;
import lombok.extern.java.Log;
import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
/**
* 池化连接实现
*
* @author summer
* @version : PooledConnection.java, v 0.1 2022年05月15日 10:31 PM summer Exp $
*/
@Log
@Data
public class PooledConnection implements IPooledConnection {
/**
* 是否繁忙
*/
private volatile boolean isBusy;
/**
* 真正的数据库连接
*/
private Connection connection;
/**
* 对应的数据源信息
*/
private IPooledDataSource pooledDataSource;
@Override
public Statement createStatement() throws SQLException {
checkStatus();
return connection.createStatement();
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
checkStatus();
return connection.prepareStatement(sql);
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
checkStatus();
return connection.prepareCall(sql);
}
@Override
public String nativeSQL(String sql) throws SQLException {
checkStatus();
return connection.nativeSQL(sql);
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
checkStatus();
connection.setAutoCommit(autoCommit);
}
@Override
public boolean getAutoCommit() throws SQLException {
checkStatus();
return connection.getAutoCommit();
}
@Override
public void commit() throws SQLException {
checkStatus();
connection.commit();
}
@Override
public void rollback() throws SQLException {
checkStatus();
connection.rollback();
}
@Override
public void close() throws SQLException {
checkStatus();
this.pooledDataSource.returnConnection(this);
}
@Override
public boolean isClosed() throws SQLException {
checkStatus();
return connection.isClosed();
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
checkStatus();
return connection.getMetaData();
}
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
checkStatus();
connection.setReadOnly(readOnly);
}
@Override
public boolean isReadOnly() throws SQLException {
checkStatus();
return connection.isReadOnly();
}
@Override
public void setCatalog(String catalog) throws SQLException {
checkStatus();
connection.setCatalog(catalog);
}
@Override
public String getCatalog() throws SQLException {
checkStatus();
return connection.getCatalog();
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
checkStatus();
connection.setTransactionIsolation(level);
}
@Override
public int getTransactionIsolation() throws SQLException {
checkStatus();
return connection.getTransactionIsolation();
}
@Override
public SQLWarning getWarnings() throws SQLException {
checkStatus();
return connection.getWarnings();
}
@Override
public void clearWarnings() throws SQLException {
checkStatus();
connection.clearWarnings();
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
checkStatus();
return connection.createStatement(resultSetType, resultSetConcurrency);
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
checkStatus();
return connection.prepareStatement(sql, resultSetType, resultSetConcurrency);
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
checkStatus();
return connection.prepareCall(sql, resultSetType, resultSetConcurrency);
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
checkStatus();
return connection.getTypeMap();
}
@Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
checkStatus();
connection.setTypeMap(map);
}
@Override
public void setHoldability(int holdability) throws SQLException {
checkStatus();
connection.setHoldability(holdability);
}
@Override
public int getHoldability() throws SQLException {
checkStatus();
return connection.getHoldability();
}
@Override
public Savepoint setSavepoint() throws SQLException {
checkStatus();
return connection.setSavepoint();
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
checkStatus();
return connection.setSavepoint(name);
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
checkStatus();
connection.rollback(savepoint);
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
checkStatus();
connection.releaseSavepoint(savepoint);
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
checkStatus();
return connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability);
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
checkStatus();
return connection.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException {
checkStatus();
return connection.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability);
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
checkStatus();
return connection.prepareStatement(sql, autoGeneratedKeys);
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
checkStatus();
return connection.prepareStatement(sql, columnIndexes);
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
checkStatus();
return connection.prepareStatement(sql, columnNames);
}
@Override
public Clob createClob() throws SQLException {
checkStatus();
return connection.createClob();
}
@Override
public Blob createBlob() throws SQLException {
checkStatus();
return connection.createBlob();
}
@Override
public NClob createNClob() throws SQLException {
checkStatus();
return connection.createNClob();
}
@Override
public SQLXML createSQLXML() throws SQLException {
checkStatus();
return connection.createSQLXML();
}
@Override
public boolean isValid(int timeout) throws SQLException {
checkStatus();
return connection.isValid(timeout);
}
@Override
public void setClientInfo(String name, String value) throws SQLClientInfoException {
checkStatus();
connection.setClientInfo(name, value);
}
@Override
public void setClientInfo(Properties properties) throws SQLClientInfoException {
checkStatus();
connection.setClientInfo(properties);
}
@Override
public String getClientInfo(String name) throws SQLException {
checkStatus();
return connection.getClientInfo(name);
}
@Override
public Properties getClientInfo() throws SQLException {
checkStatus();
return connection.getClientInfo();
}
@Override
public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
checkStatus();
return connection.createArrayOf(typeName, elements);
}
@Override
public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
checkStatus();
return connection.createStruct(typeName, attributes);
}
@Override
public void setSchema(String schema) throws SQLException {
checkStatus();
connection.setSchema(schema);
}
@Override
public String getSchema() throws SQLException {
checkStatus();
return connection.getSchema();
}
@Override
public void abort(Executor executor) throws SQLException {
checkStatus();
connection.abort(executor);
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
checkStatus();
connection.setNetworkTimeout(executor, milliseconds);
}
@Override
public int getNetworkTimeout() throws SQLException {
checkStatus();
return connection.getNetworkTimeout();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
checkStatus();
return connection.unwrap(iface);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
checkStatus();
return connection.isWrapperFor(iface);
}
@Override
public boolean isBusy() {
return isBusy;
}
@Override
public void setBusy(boolean busy) {
isBusy = busy;
}
@Override
public Connection getConnection() {
return connection;
}
@Override
public void setConnection(Connection connection) {
this.connection = connection;
}
@Override
public void setDataSource(final IPooledDataSource dataSource) {
this.pooledDataSource = dataSource;
}
/**
* 检查状态,连接是否繁忙
*/
private void checkStatus() {
if(!isBusy) {
throw new ConnPoolException("连接已被关闭!");
}
}
}
package com.summer.dsconnpool.exception;
public class ConnPoolException extends RuntimeException {
public ConnPoolException() {
}
public ConnPoolException(String message) {
super(message);
}
public ConnPoolException(String message, Throwable cause) {
super(message, cause);
}
public ConnPoolException(Throwable cause) {
super(cause);
}
public ConnPoolException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package com.summer.dsconnpool.util;
import com.summer.dsconnpool.exception.ConnPoolException;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* 驱动工具类
*/
public final class DriverUtil {
/**
* 驱动类映射Map
*/
private static final Map<String, String> DRIVER_CLASS_MAP;
static {
DRIVER_CLASS_MAP = new HashMap<>(32);
DRIVER_CLASS_MAP.put("jdbc:db2", "COM.ibm.db2.jdbc.app.DB2Driver");
DRIVER_CLASS_MAP.put("jdbc:firebirdsql", "org.firebirdsql.jdbc.FBDriver");
DRIVER_CLASS_MAP.put("jdbc:edbc", "ca.edbc.jdbc.EdbcDriver");
DRIVER_CLASS_MAP.put("jdbc:pointbase", "com.pointbase.jdbc.jdbcUniversalDriver");
DRIVER_CLASS_MAP.put("jdbc:fake", "com.alibaba.druid.mock.MockDriver");
DRIVER_CLASS_MAP.put("jdbc:informix-sqli", "com.informix.jdbc.IfxDriver");
DRIVER_CLASS_MAP.put("jdbc:sqlite", "org.sqlite.JDBC");
DRIVER_CLASS_MAP.put("jdbc:microsoft", "com.microsoft.jdbc.sqlserver.SQLServerDriver");
DRIVER_CLASS_MAP.put("jdbc:hsqldb", "org.hsqldb.jdbcDriver");
DRIVER_CLASS_MAP.put("jdbc:postgresql", "org.postgresql.Driver");
DRIVER_CLASS_MAP.put("jdbc:ingres", "com.ingres.jdbc.IngresDriver");
DRIVER_CLASS_MAP.put("jdbc:cloudscape", "COM.cloudscape.core.JDBCDriver");
DRIVER_CLASS_MAP.put("jdbc:JSQLConnect", "com.jnetdirect.jsql.JSQLDriver");
DRIVER_CLASS_MAP.put("jdbc:derby", "org.apache.derby.jdbc.EmbeddedDriver");
DRIVER_CLASS_MAP.put("jdbc:timesten", "com.timesten.jdbc.TimesTenDriver");
DRIVER_CLASS_MAP.put("jdbc:interbase", "interbase.interclient.Driver");
DRIVER_CLASS_MAP.put("jdbc:h2", "org.h2.Driver");
DRIVER_CLASS_MAP.put("jdbc:as400", "com.ibm.as400.access.AS400JDBCDriver");
DRIVER_CLASS_MAP.put("jdbc:sybase:Tds", "com.sybase.jdbc2.jdbc.SybDriver");
DRIVER_CLASS_MAP.put("jdbc:mock", "com.alibaba.druid.mock.MockDriver");
DRIVER_CLASS_MAP.put("jdbc:oracle", "oracle.jdbc.driver.OracleDriver");
DRIVER_CLASS_MAP.put("jdbc:mysql", "com.mysql.jdbc.Driver");
DRIVER_CLASS_MAP.put("jdbc:odps", "com.aliyun.odps.jdbc.OdpsDriver");
DRIVER_CLASS_MAP.put("jdbc:mckoi", "com.mckoi.JDBCDriver");
DRIVER_CLASS_MAP.put("jdbc:jtds", "net.sourceforge.jtds.jdbc.Driver");
DRIVER_CLASS_MAP.put("jdbc:sapdb", "com.sap.dbtech.jdbc.DriverSapDB");
DRIVER_CLASS_MAP.put("jdbc:JTurbo", "com.newatlanta.jturbo.driver.Driver");
DRIVER_CLASS_MAP.put("jdbc:mimer:multi1", "com.mimer.jdbc.Driver");
}
/**
* 加载驱动类信息
*
* @param driverClass 驱动类
* @param url 连接url
*/
public static void loadDriverClass(String driverClass, final String url) {
if (StringUtils.isBlank(driverClass) && StringUtils.isBlank(url)) {
throw new ConnPoolException("loadDriverClass失败,配置缺失");
}
if(StringUtils.isBlank(driverClass.trim())) {
driverClass = getDriverClassByUrl(url);
}
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
throw new ConnPoolException(e);
}
}
/**
* 根据 URL 获取对应的驱动类
*
* @param url url,不可为空
* @return 驱动信息
*/
private static String getDriverClassByUrl(final String url) {
if (StringUtils.isBlank(url)) {
throw new ConnPoolException("getDriverClassByUrl失败,配置缺失");
}
for(Map.Entry<String, String> entry : DRIVER_CLASS_MAP.entrySet()) {
String urlPrefix = entry.getKey();
if(url.startsWith(urlPrefix)) {
return entry.getValue();
}
}
throw new ConnPoolException("Can't auto find match driver class for url: " + url);
}
}
package com.summer.dsconnpool;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
public class AbstractDataSource implements DataSource {
@Override
public Connection getConnection() throws SQLException {
return null;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
}
package com.summer.dsconnpool;
import com.summer.dsconnpool.config.PooledDataSourceConfig;
import com.summer.dsconnpool.connection.IPooledConnection;
import com.summer.dsconnpool.connection.PooledConnection;
import com.summer.dsconnpool.exception.ConnPoolException;
import com.summer.dsconnpool.util.DriverUtil;
import lombok.Data;
import lombok.extern.java.Log;
import org.apache.commons.lang3.StringUtils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 数据源
*
* @author summer
* @version : DefaultDataSource.java, v 0.1 2022年05月15日 10:19 PM summer Exp $
*/
@Log
@Data
public class DefaultDataSource extends AbstractDataSource implements IPooledDataSource, ILifeCycle {
/**
* 数据源相关配置
*/
private PooledDataSourceConfig dataSourceConfig;
/**
* 池化连接列表
*/
private List<IPooledConnection> pooledConnectionList = new ArrayList<>();
/**
* 初始化
*/
public synchronized void init() {
if (!checkConfigLegal()) {
return;
}
//加载驱动类
DriverUtil.loadDriverClass(dataSourceConfig.getDriverClass(), dataSourceConfig.getJdbcUrl());
//连接池初始化
this.initJdbcPool();
//空闲连接校验
initTestOnIdle();
}
@Override
public synchronized IPooledConnection getConnection() throws SQLException {
log.info("getConnection begin....");
logConnPoolDigest(pooledConnectionList);
//1. 获取第一个不是 busy 的连接
Optional<IPooledConnection> connectionOptional = getFreeConnectionFromPool();
if(connectionOptional.isPresent()) {
return connectionOptional.get();
}
//2. 考虑是否可以扩容
if(this.pooledConnectionList.size() >= this.dataSourceConfig.getMaxSize()) {
//2.1 立刻返回
if(this.dataSourceConfig.getMaxWaitMills() <= 0) {
throw new ConnPoolException("从连接池中获取失败");
}
log.info("开始等待空闲连接出现...");
try {
wait(this.dataSourceConfig.getMaxWaitMills());
log.info("等待结束,获取连接");
return getConnection();
} catch (InterruptedException exception) {
log.info("等待异常");
exception.printStackTrace();
throw new SQLException("等待空闲连接异常");
}
} else {
//3. 扩容(暂时只扩容一个)
log.info("开始扩容连接池大小...");
IPooledConnection pooledConnection = createPooledConnection();
pooledConnection.setBusy(true);
this.pooledConnectionList.add(pooledConnection);
log.info("扩容完成...");
logConnPoolDigest(pooledConnectionList);
return pooledConnection;
}
}
@Override
public synchronized void returnConnection(IPooledConnection pooledConnection) {
// 验证状态
if(this.dataSourceConfig.isTestOnReturn()) {
checkValid(pooledConnection);
}
// 设置为不繁忙
log.info("释放连接...");
pooledConnection.setBusy(false);
logConnPoolDigest(pooledConnectionList);
//通知其他线程
notifyAll();
}
/**
* 获取空闲的连接
* @return 连接
* @since 1.3.0
*/
private Optional<IPooledConnection> getFreeConnectionFromPool() {
for(IPooledConnection pc : this.pooledConnectionList) {
if(!pc.isBusy()) {
pc.setBusy(true);
log.info("从连接池中获取连接");
// 验证有效性
if(this.dataSourceConfig.isTestOnBorrow()) {
checkValid(pc);
}
return Optional.of(pc);
}
}
// 空
return Optional.empty();
}
/**
* 空闲时校验
*/
private void initTestOnIdle() {
if(StringUtils.isNotBlank(this.dataSourceConfig.getValidQuery())) {
ScheduledExecutorService idleExecutor = Executors.newSingleThreadScheduledExecutor();
idleExecutor.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
testOnIdleCheck();
}
}, this.dataSourceConfig.getTestOnIdleIntervalSeconds(),
this.dataSourceConfig.getTestOnIdleIntervalSeconds(), TimeUnit.SECONDS);
}
}
/**
* 验证空闲连接是否有效
*/
private void testOnIdleCheck() {
for(IPooledConnection pc : this.pooledConnectionList) {
if(!pc.isBusy()) {
checkValid(pc);
}
}
}
/**
* 校验连接是否成功
*
* @param pooledConnection 池化连接
*/
private void checkValid(final IPooledConnection pooledConnection) {
log.info("开始校验连接");
if(StringUtils.isNotBlank(this.dataSourceConfig.getValidQuery())) {
Connection connection = pooledConnection.getConnection();
try {
// 如果连接无效,重新申请一个新的替代
if(!connection.isValid(this.dataSourceConfig.getValidTimeOutSeconds())) {
log.info("连接无效,创建新连接");
Connection newConnection = createConnection();
pooledConnection.setConnection(newConnection);
log.info("连接无效,创建新连接成功");
}
} catch (SQLException throwables) {
throw new ConnPoolException(throwables);
}
} else {
log.info("校验SQL为空,跳过连接校验");
}
}
/**
* 初始化连接池
*/
private void initJdbcPool() {
final int minSize = this.dataSourceConfig.getMinSize();
pooledConnectionList = new ArrayList<>(minSize);
for(int i = 0; i < minSize; i++) {
pooledConnectionList.add(createPooledConnection());
}
}
/**
* 创建一个池化的连接
* @return
*/
private IPooledConnection createPooledConnection() {
Connection connection = createConnection();
IPooledConnection pooledConnection = new PooledConnection();
pooledConnection.setBusy(false);
pooledConnection.setConnection(connection);
pooledConnection.setDataSource(this);
return pooledConnection;
}
/**
* 创建一个新连接
* @return 连接
*/
private Connection createConnection() {
try {
log.info("创建新连接.....");
return DriverManager.getConnection(this.dataSourceConfig.getJdbcUrl(),
this.dataSourceConfig.getUser(), this.dataSourceConfig.getPassword());
} catch (SQLException e) {
log.warning("创建连接异常," + e.getMessage());
throw new ConnPoolException(e);
}
}
/**
* 检查数据源配置是否合法
* @return
*/
private boolean checkConfigLegal() {
if (this.dataSourceConfig == null) {
throw new ConnPoolException("数据源配置缺失");
}
if (StringUtils.isBlank(dataSourceConfig.getDriverClass())
&& StringUtils.isBlank(this.dataSourceConfig.getJdbcUrl())) {
throw new ConnPoolException("数据源配置缺失");
}
return true;
}
/**
* 日志打印
* @param pooledConnectionList
*/
private static void logConnPoolDigest(List<IPooledConnection> pooledConnectionList) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("pooledConnectionList status:[" + pooledConnectionList.size()).append(",busy status:[");
for (IPooledConnection pooledConnection : pooledConnectionList) {
stringBuilder.append(pooledConnection.isBusy()).append(",");
}
stringBuilder.append("]]");
log.info(stringBuilder.toString());
}
}
package com.summer.dsconnpool;
/**
* 生命周期管理接口
*/
public interface ILifeCycle {
/**
* 初始化
*/
void init();
}
package com.summer.dsconnpool;
import com.summer.dsconnpool.connection.IPooledConnection;
import javax.sql.DataSource;
import java.sql.SQLException;
/**
* 池化数据源接口
*
* @author summer
* @version : IPooledDataSourceConfig.java, v 0.1 2022年05月15日 10:36 PM summer Exp $
*/
public interface IPooledDataSource extends DataSource {
/**
* 获取连接
* @return
* @throws SQLException
*/
IPooledConnection getConnection() throws SQLException;
/**
* 归还连接
*
* @param pooledConnection 连接池
*/
void returnConnection(IPooledConnection pooledConnection);
}