对Java初学者来说,数据库连接池同其它的XX池一样,看起来是很神秘的一种技术,其实都是难者不会,会者不难的情况。当了解了数据库连接池技术之后,数据库连接池并不神秘而是一个非常简单的小技巧。
为什么会有数据库连接池呢?对于一个系统来说,如果没有数据库连接,当你每次需要进行数据库操作时,都要获取连接,完成操作之后将连接关闭。当系统访问量上来之后,你就会发现系统将会有大量的实践都花在获取数据库连接和关闭连接上。因此如果我们建立一个数据库连接池,在系统启动时,一次性的获取若干条数据库连接加入到连接池中,当我们需要进行数据库操作时,直接从数据库连接池中拿到连接,用完之后不再关闭,而是将其放入连接池中,这样系统运行时将再不会有频繁的获取连接和关闭连接了。下面,我们就自己写一个简单的数据库连接池,并加以测试。
1. ConnectionPool类:数据库连接池类. 运用单例模式,当要进行数据库操作时,先调用ConnectionPool.getInstance()方法,获取数据库连接池的实例,然后调用connectionPool.getConnection()方法获取数据库连接,使用完连接之后调用connectionPool.release(Connection conn)来代替connection.close()方法来将获得的连接释放到连接池中,以便重复利用。这样就解决了频繁打开和关闭数据库连接池的问题。
ConnectionPool.java
public class ConnectionPool {
private List<Connection> connections ;
private int poolSize = 1;//
private String url;
private String username;
private String password;
private String driverClassName;
private static ConnectionPool instance = null;
private ConnectionPool() {
initialize();
}
/**
* 初始化方法
*/
private void initialize() {
connections = new ArrayList<Connection>(poolSize);
readConfig();
addConnection();
}
/**
* 读取数据库连接池的配置文件
*/
private void readConfig() {
String path = System.getProperty("user.dir")+"/resources/config/jdbc.properties";
try {
FileInputStream is = new FileInputStream(path);
Properties prop = new Properties();
prop.load(is);
driverClassName = prop.getProperty("jdbc.driverClassName");
url = prop.getProperty("jdbc.url");
username = prop.getProperty("jdbc.username");
password = prop.getProperty("jdbc.password");
poolSize = Integer.parseInt(prop.getProperty("jdbc.poolSize"));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 往数据库连接池中添加连接
*/
private void addConnection() {
for (int i=0; i < poolSize; i++) {
try {
Class.forName(driverClassName);
Connection connection = java.sql.DriverManager.getConnection(url, username, password);
connections.add(connection);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
/**
* 获取数据库连接池
* @return
*/
public static ConnectionPool getConnectionPoolInstance() {
if (instance == null) instance = new ConnectionPool();
return instance;
}
/**
* 从数据库连接池中获取数据库连接
* @return
*/
public synchronized Connection getConnection() {
if (connections.size() <= 0) return null;
Connection connection = connections.get(0);
connections.remove(0);
return connection;
}
/**
* 释放数据库连接
* 使用完数据库连接池之后调用这个方法来代替close方法
* @param connection
*/
public synchronized void realase(Connection connection) {
connections.add(connection);
}
/**
* 关闭数据库连接池
*/
public synchronized void closePool() {
for (int i =0; i < connections.size(); i++) {
Connection connection = connections.get(i);
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
connections.remove(i);
}
}
}
2. jdbc.propeties 数据库连接池配置文件,初始化数据库连接池时使用到。
jdbc.driverClassName=oracle.jdbc.xa.client.OracleXADataSource
jdbc.url=jdbc:oracle:thin:@127.0.0.1:1521:mgodb
jdbc.username=CAR
jdbc.password=CAR
jdbc.poolSize=10
3. ConnectionPoolTest类: 测试类, 通过与不使用数据库连接池进行数据库访问所消耗的时间进行对比,为了使测试尽可能准确,通过100次循环调用,对比时间总和。
ConnectionPoolTest.java
public class ConnectionPoolTest extends TestCase {
public void testConnectionPoolTest() throws Exception {
String sql = "SELECT * FROM T_CR_CAR_TYPE_BRAND";
ConnectionPool connectionPool = null;
long starttime = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
connectionPool = ConnectionPool.getConnectionPoolInstance();
Connection conn = connectionPool.getConnection();
ResultSet rs;
Statement stmt = conn.createStatement();
rs = stmt.executeQuery(sql);
while (rs.next()) {
}
rs.close();
stmt.close();
rs = null;
stmt = null;
connectionPool.realase(conn);
}
connectionPool.closePool();
System.out.println("经过100次循环调用,使用连接池花费的时间:"
+ (System.currentTimeMillis() - starttime) + "ms\n");
String driverClassName = "oracle.jdbc.xa.client.OracleXADataSource";
String url = "jdbc:oracle:thin:@127.0.0.1:1521:mgodb";
String username = "CAR";
String password = "CAR";
starttime = System.currentTimeMillis();
for (int i = 0; i < 100; i++) {
Class.forName(driverClassName);
Connection conn = DriverManager.getConnection(url, username,
password);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
}
rs.close();
stmt.close();
conn.close();
rs = null;
stmt = null;
conn = null;
}
System.out.println("经过100次循环调用,不使用连接池花费的时间:" + (System.currentTimeMillis() - starttime) + "ms\n");
}
}
通过测试结果可以发现,使用数据库连接池所消耗的实践要小于不使用数据库连接池的,当调用的次数增加时,差异将会更加明显。
当然这个数据库连接池仅仅是用来了解数据库连接池的原理而已,对于超时的连接并没有强制收回的机制,也没有限制用户调用close方法来关闭连接。仅供学习。。。。