数据库连接池的好处:
对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销。但是对于一个复杂的数据库应用,频繁的建立、关闭连接,会极大的降低系统的性能,因为数据库的连接是非常耗时的操作,所以可能对于数据库连接就成了系统性能的瓶颈。
此处,编写一个简单的数据库连接池,理解数据库连接池的实现思想。
数据库连接池的编写分析设计:
参考:https://www.jb51.net/article/78397.htm
数据库连接池的相关配置信息:
/*
* 数据库连接池的配置文件
*/
public class ParamConfig {
public static final int MIN_CONN=5; //一个连接池最小的连接数
public static final int MAX_CONN=50;//一个链接池最大的连接数
public static final int MIN_IDLE=5;//最小空闲连接数
public static final long MAX_Wait=1000; //等待获取连接的最长时间
}
连接数据库信息相关配置文件:DBConfig.properties
# .properties文件,#代表注释
#对于mysql的字段
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/test?useSSL=true&rewriteBatchedStatements=true
mysql.user_name=root
mysql.user_pwd=123456
#Oracle的字段
oracle.driver=
oracle.url=
oracle.user_name=
oracle.user_pwd=
DBHandler接口存储DBConfig.properties的key值:
public interface DBHandler {
public static String DATABASE="mysql.";
public static String DRIVER=DATABASE+"driver";
public static String USER_NAME=DATABASE+"user_name";
public static String USER_PWD=DATABASE+"user_pwd";
public static String URL=DATABASE+"url";
}
数据库连接池的封装:
/*
* 数据库连接池
*/
public class DBConnPool implements DataSource{
//数据库连接属性
private static String DRIVER="";
private static String USER_NAME="";
private static String USER_PWD="";
private static String URL="";
private static Properties p=null;
static{
//对连接属性进行赋值
p=new Properties();
try {
p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("DBConfig.properties"));
} catch (IOException e) {
e.printStackTrace();
}
DRIVER=getValue(DBHandler.DRIVER);
URL=getValue(DBHandler.URL);
USER_NAME=getValue(DBHandler.USER_NAME);
USER_PWD=getValue(DBHandler.USER_PWD);
}
private static final int DEFAULT_INIT_SIZE=5;//连接池默认连接数
private static ArrayBlockingQueue<Connection> idleConnQueue;//空闲连接队列
private static Vector<Connection> busyConnVector; //正在被使用的数据库连接集合
private static Logger logger=null;
private int size;//空闲连接数
private static DBConnPool dbConnPool=null;//使用单例模式
private DBConnPool() {
initConnPool();
}
/*
* 提供给外界获取此类对象的方法
*/
public static DBConnPool getInstance(){
if(dbConnPool==null){
dbConnPool=new DBConnPool();
}
return dbConnPool;
}
/*
* 初始化数据库连接池
*/
private void initConnPool() {
//取到最大连接数
int maxConn=ParamConfig.MAX_CONN;
//最小连接数
int minConn=ParamConfig.MIN_CONN;
idleConnQueue=new ArrayBlockingQueue<>(maxConn);//队列的大小为最大的连接数
busyConnVector=new Vector<>();
logger=Logger.getLogger(this.getClass().getName());
//在默认大小和配置的最小连接数之间选一个大的
int initSize=minConn<DEFAULT_INIT_SIZE?DEFAULT_INIT_SIZE:minConn;
//加载驱动
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//初始化连接池
for(int i=0;i<initSize;i++){
try {
idleConnQueue.put(newConn());
} catch (InterruptedException e) {
e.printStackTrace();
}
size++;
}
}
/**
* 创建一个连接
* @return
*/
private Connection newConn() {
Connection conn=null;
try {
conn = DriverManager.getConnection(URL, USER_NAME, USER_PWD);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 获取数据库配置文件对应的值
* @param key 键
* @return 值
*/
private static String getValue(String key) {
if(key==null||"".equals(key)){
throw new IllegalArgumentException("传入参数有误!");
}
return p.getProperty(key);
}
/**
* 获取连接
*/
@Override
public Connection getConnection() throws SQLException {
try {
//在指定的时间内获取指定的元素,返回队列的头部
final Connection conn=idleConnQueue.poll(ParamConfig.MAX_Wait,TimeUnit.MILLISECONDS);
if(conn==null){
//获取不到连接,给出相应的提示
logger.info(entryMsg());
//如果没有连接可以获取,那么需要进行扩容
ensureCapacity();
return null;
}
busyConnVector.add(conn);
return (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!method.getName().equals("close")) {
return method.invoke(conn, args);
} else {
idleConnQueue.offer(conn);
busyConnVector.remove(conn);
System.out.println(conn);
return null;
}
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
/**
* 对连接池进行扩容
*/
private void ensureCapacity() {
int minConn=ParamConfig.MIN_CONN; //最小连接数
int maxConn=ParamConfig.MAX_CONN; //最大连接数
int newConn=minConn+size; //新的连接数
newConn=newConn>maxConn?maxConn:newConn;
int modCount=0;
for(int i=size;i<newConn-size;i++){
try {
idleConnQueue.put(newConn());
modCount++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
size+=modCount;
}
/*
* 对数据库连接池进行清空
*/
public void clear(){
while(size-->0){
try {
Connection conn=idleConnQueue.take();
conn.close();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/*
* 获取空闲队列的大小
*/
protected int getIdleConnQueueSize(){
return idleConnQueue.size();
}
/**
* 获取正在使用的连接的数量
* @return
*/
protected int getBusyConnVectorSize(){
return busyConnVector.size();
}
private String entryMsg() {
return "数据库忙碌,请进行等待。。。";
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@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;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
}
数据库连接池的管理类:
public class DBConnPoolManage {
//提供给外界获取连接
public static Connection getConn() throws SQLException{
return DBConnPool.getInstance().getConnection();
}
}
测试类:
public class Test {
public static void main(String[] args) {
Connection conn=null;
Statement stmt=null;
ResultSet rs=null;
//获取连接
try {
conn=DBConnPoolManage.getConn();
stmt=conn.createStatement();
String sql="SELECT * FROM USER";
rs=stmt.executeQuery(sql);
while(rs.next()){
System.out.println(rs.getString(2));
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
try {
if(conn!=null){
conn.close();
}
if(stmt!=null){
stmt.close();
}
if(rs!=null){
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
分析总结:
核心代码:
public Connection getConnection() throws SQLException {
try {
//在指定的时间内获取指定的元素,返回队列的头部
final Connection conn=idleConnQueue.poll(ParamConfig.MAX_Wait,TimeUnit.MILLISECONDS);
if(conn==null){
//获取不到连接,给出相应的提示
logger.info(entryMsg());
//如果没有连接可以获取,那么需要进行扩容
ensureCapacity();
return null;
}
busyConnVector.add(conn);
return (Connection) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!method.getName().equals("close")) {
return method.invoke(conn, args);
} else {
idleConnQueue.offer(conn);
busyConnVector.remove(conn);
System.out.println(conn);
return null;
}
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
getConnection返回的并不是“真正”的Connection,而是代理类(此处使用匿名类),当用户调用close()方法时,不是对Connection进行close操作,而是将此Connection放回idleConnQueue队列中,从busyConnVector中移除。