一.数据库链接池出现的原因
首先我们知道数据库操作大部分的耗时都来自于对数据库链接的获取与关闭。所以为了提高数据库操作的效率,我们需要避免频繁的对数据库链接的操作。为了解决这一问题,数据库链接池就出现了。
在讲数据库链接池之前,我们还需要了解一个重要的东西,DataSource
数据源:
作为 DriverManager 工具的替代项,DataSource 对象是获取连接的首选方法。实现 DataSource 接口的对象通常在基于 JavaTM Naming and Directory Interface (JNDI) API 的命名服务中注册。
DataSource 接口由驱动程序供应商实现。共有三种类型的实现:
基本实现 - 生成标准的 Connection 对象
连接池实现 - 生成自动参与连接池的 Connection 对象。此实现与中间层连接池管理器一起使用。
分布式事务实现 - 生成一个 Connection 对象,该对象可用于分布式事务,大多数情况下总是参与连接池。此实现与中间层事务管理器一起使用,大多数情况下总是与连接池管理器一起使用。
DataSource有个重要的方法getConnection()
尝试建立与此 DataSource 对象所表示的数据源的连接。
二.创建自己的数据库链接池
1.version1.0
package dataSource.versionOne;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;
import javax.sql.DataSource;
/**
* @ClassName: MyDataSource
* @Description: TODO(这里用一句话描述这个类的作用)
* @author wangcc
* @date 2016年11月28日 下午11:09:04
*
* 初始化数据源的时候,初始化一定量的连接放到池子中,当用户使用getConnection()方法取出连接的时候,
* 我们会判断这个连接池中还有没有连接了
* ,有就直接取出第一个连接返回,没有的话,我们在判断当前的连接数有没有超过最大连接数,超过的话,就抛出一个异常
* (其实这里还可以选择等待其他连接的释放,这个具体实现是很麻烦的),没有超过的话,就创建连接,并且将其放入池子中。
* 我们自定义的数据源是实现了JDBC中的DataSource接口的
* ,这个接口很重要的,后面我们会说到apache的数据源都是要实现这个接口的,这个接口统一了数据源的标准
* ,这个接口中有很多实现的,所以看到我们的数据源类中有很多没必要的方法
* ,但是那个方法都是要实现的,最重要的就是要实现getConnection方法,其他的实现都只需要调用super.XXX就可以了。
*/
public class MyDataSource implements DataSource {
private static final String url = "jdbc:oracle:thin:@127.0.0.1:1521:ORCL";
private static final String user = "SCOTT";
private static final String password = "cong960227";
private static int currentCount = 0;
private static int maxCount = 10;
private static int initCount = 5;
// 初始化连接数,最大连接数,当前的连接数,连接池(因为我们可能需要频繁的添加连接和删除连接所以使用LinkedList,因为这个list是链表结构的,增加和删除效率高)
private LinkedList<Connection> connectionPool = new LinkedList<Connection>();
public MyDataSource() {
try {
for (int i = 0; i < initCount; i++) {
this.connectionPool.addLast(createConnection());
currentCount++;
}
} catch (Exception e) {
// TODO: handle exception
throw new ExceptionInInitializerError(e);
}
}
public Connection createConnection() throws SQLException {
return DriverManager.getConnection(url, user, password);
}
@Override
public Connection getConnection() throws SQLException {
// TODO Auto-generated method stub
synchronized (connectionPool) {
if (connectionPool.size() > 0) {
return this.connectionPool.removeFirst();
}
if (currentCount < maxCount) {
currentCount++;
return createConnection();
}
throw new SQLException("没有可用链接了");
}
}
public void free(Connection conn) {
this.connectionPool.addLast(conn);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public Connection getConnection(String username, String password)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
在这里我们指定一个LinkedList的Connection对象集合类型:connectionPool。之所以将存储数据库链接的容器设置为LinkedList是因为该类型为链表实现,便于实现删除和添加,而数据库连接池中的数据库链接需要经常增删。
在这里,我们依旧使用DriverManager来获取初始化的五个数据库链接。
我们重写了getConnection方法,在该方法中,我们使用了关键字synchronized来进行同步,因为LinkedList并不是线程同步的。
package dataSource.versionOne;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import dataSource.versionOne.MyDataSource;
public class DbHelper {
private static final String driver = "oracle.jdbc.driver.OracleDriver";
private static MyDataSource dataSource = null;
static {
try {
Class.forName(driver);
dataSource = new MyDataSource();
} catch (Exception e) {
// TODO: handle exception
System.out.println("Exception:" + e.getMessage() + "");
throw new ExceptionInInitializerError(e);
}
}
public static DataSource getDataSource() {
return dataSource;
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
public static void free(ResultSet rs, Statement st, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (conn != null) {
dataSource.free(conn);
}
}
}
在DataHelper这个类中,我们在static代码块中直接new一个DataSource对象,使得五个数据库链接在一开始即被初始化
并使得Dbhelper中的声明的DataSource的引用dataSource持有这个对象。将原有的Dbhelper类中的getConnection方法具体实现改为MyDataSource中重写的方法。
package dataSource.versionOne;
import java.sql.Connection;
import java.sql.SQLException;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Connection conn = null;
for (int i = 0; i < 10; i++) {
try {
conn = DbHelper.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(conn);
DbHelper.free(null, null, conn);
}
}
}
测试一下,结果与我们设计吻合。
oracle.jdbc.driver.OracleConnection@105d6633
oracle.jdbc.driver.OracleConnection@63e0a6a9
oracle.jdbc.driver.OracleConnection@31ddd479
oracle.jdbc.driver.OracleConnection@5aa86d82
oracle.jdbc.driver.OracleConnection@67ada04d
oracle.jdbc.driver.OracleConnection@105d6633
oracle.jdbc.driver.OracleConnection@63e0a6a9
oracle.jdbc.driver.OracleConnection@31ddd479
oracle.jdbc.driver.OracleConnection@5aa86d82
oracle.jdbc.driver.OracleConnection@67ada04d
如果这里,我们想要我们关闭数据库链接的时候依旧是用close方法而不是dataSource的free方法我们该怎么做呢。