java程序與數據庫之間進行的是TCP長連接,如果程序每一次操作數據庫都要連接一次數據庫的話,會產生比較大的開銷,影響性能。這時候數據庫池的出現解決了這個問題,數據庫連接池在初始化的時候會生成一定數量的數據庫連接存放到連接池中,在外界獲取連接池時提供數據庫連接,再外界不用連接的時候負責回收釋放數據庫連接。
下面是我實現的簡單數據庫連接池demo
JdbcConnect.java
用於連接數據庫獲取數據庫連接Connection和釋放Connection資源的單例模式Jdbc連接類
package jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
public class JdbcConnect {
//使用靜態內部類實現單例模式
private JdbcConnect(){};
private static class SingleJdbcConnect{
public static final JdbcConnect jdbcConnect = new JdbcConnect();
}
public static JdbcConnect getJdbcConnectInstance(){
return SingleJdbcConnect.jdbcConnect;
}
private static final String URL = "jdbc:mysql://127.0.0.1:3306/test";
private static final String NAME = "root";
private static final String PASSWORD = "123456";
/**
* @Author cuizx
* @Description 使用DriverManager獲取數據庫連接Connection
* @Date 2019/8/25 14:41
* @Param []
* @return java.sql.Connection
**/
public synchronized Connection getConnection(){
Connection connection = null;
try {
connection = DriverManager.getConnection(URL,NAME,PASSWORD);
}catch (Exception e){
e.printStackTrace();
}
return connection;
}
/**
* @Author cuizx
* @Description 釋放數據庫連接Connection
* @Date 2019/8/25 14:42
* @Param [connection]
* @return void
**/
public synchronized void freeConnection(Connection connection){
try {
connection.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
JdbcPool.java
數據庫連接池的單例模式實現類,
package jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.concurrent.CopyOnWriteArrayList;
public class JdbcPool {
//使用靜態內部類實現單例模式
private JdbcPool(){};
private static class SingleJdbcPool{
public static final JdbcPool jdbcPool = new JdbcPool();
}
public static JdbcPool getJdbcPoolInstance(){
return SingleJdbcPool.jdbcPool;
}
/**
* mysql的jdbc驅動類
*/
private static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
/**
* 初始化連接數
*/
private static final int INIT_LINK_NUM = 30;
/**
* 最大連接數
*/
private static final int MAX_LINK_NUM = 100;
/**
* 最小連接數
*/
private static final int MIN_LINK_NUM = 10;
/**
* 正在被使用的數據庫連接數量
*/
private int CURR_USED_LINK_NUM = 0;
/**
* 併發數組集合類
*/
private CopyOnWriteArrayList<Connection> jdbcConnectPool;
{
try {
//加載mysql的jdbc驅動類
Class.forName(DRIVER_CLASS_NAME);
//初始化承載連接的容器
jdbcConnectPool = new CopyOnWriteArrayList();
//以初始化連接數量生成對應數量的jdbc連接且放入jdbcConnectPool中
JdbcConnect jdbcConnect = JdbcConnect.getJdbcConnectInstance();
for (int i = 0; i < INIT_LINK_NUM; i++) {
Connection tempConnection = jdbcConnect.getConnection();
if(tempConnection != null){
jdbcConnectPool.add(tempConnection);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* @Author cuizx
* @Description 同步方法,用於從連接池中獲取數據庫連接
* @Date 2019/8/25 14:41
* @Param []
* @return java.sql.Connection
**/
public synchronized Connection getJdbcConnection(){
//獲取數據庫連接,如果當前數據庫連接池中沒有
if(jdbcConnectPool.isEmpty() && getAllConnectionNum() < MAX_LINK_NUM){
//獲取JdbcConnect連接對象
JdbcConnect jdbcConnect = JdbcConnect.getJdbcConnectInstance();
//獲取Connection
Connection tempConnection = jdbcConnect.getConnection();
//將創建好的連接放入連接池
jdbcConnectPool.add(tempConnection);
}else if(jdbcConnectPool.isEmpty() && getAllConnectionNum() >= MAX_LINK_NUM){
//如果當前數據庫連接池爲空且連接總數已經超出最大值,則拋錯
throw new RuntimeException("當前數據庫連接池中沒有可用連接");
}
//獲取最後一個connection連接
Connection returnConnection = jdbcConnectPool.get(jdbcConnectPool.size()-1);
//然後移出剛纔得到的連接
jdbcConnectPool.remove(jdbcConnectPool.size()-1);
//當前正在被使用的連接數+1
CURR_USED_LINK_NUM++;
return returnConnection;
}
/**
* @Author cuizx
* @Description 釋放資源,按順序首先釋放ResultSet、Statement,然後再將使用結束的Conneection放回數據庫連接池
* @Date 2019/8/25 14:38
* @Param [resultSet, statement, connection]
* @return void
**/
public synchronized void releaseJdbcConnection(ResultSet resultSet, Statement statement,Connection connection){
releaseJdbcStatementAndResultSet(resultSet, statement);
//如果當前連接數小於最大連接數,則把數據庫連接重新放置到數據庫連接池中
if(CURR_USED_LINK_NUM >0 && getCurrConnectionNum() < MAX_LINK_NUM){
jdbcConnectPool.add(connection);
CURR_USED_LINK_NUM--;
}
}
/**
* @Author cuizx
* @Description 按順序釋放ResultSet、Statement資源
* @Date 2019/8/25 14:39
* @Param [resultSet, statement]
* @return void
**/
public void releaseJdbcStatementAndResultSet(ResultSet resultSet, Statement statement){
try {
if(resultSet!=null){
resultSet.close();
}
if(resultSet!=null){
statement.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* @Author cuizx
* @Description 獲取當前數據庫連接池中的連接數
* @Date 2019/8/25 14:40
* @Param []
* @return int
**/
public synchronized int getCurrConnectionNum(){
return jdbcConnectPool.size();
}
/**
* @Author cuizx
* @Description 獲取數據庫連接池和正在被使用的連接的總和
* @Date 2019/8/25 14:40
* @Param []
* @return int
**/
public synchronized int getAllConnectionNum(){
return jdbcConnectPool.size()+CURR_USED_LINK_NUM;
}
}
package jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
public class Test {
public static void main(String[] args) {
String sql = "select * from user";
try {
//獲取到數據庫連接池的單例對象
JdbcPool jdbcPool = JdbcPool.getJdbcPoolInstance();
System.out.println("當前數據庫連接池中的連接數:"+jdbcPool.getCurrConnectionNum());
//從連接池中獲取到一個數據庫連接
Connection connection = jdbcPool.getJdbcConnection();
System.out.println("獲取一個連接後的數據庫連接池中的連接數:"+jdbcPool.getCurrConnectionNum());
//創建PreparedStatement
PreparedStatement preparedStatement = connection.prepareStatement(sql);
//執行sql且獲取ResultSet
ResultSet resultSet = preparedStatement.executeQuery();
//循環ResultSet獲取其中數據
while (resultSet.next()){
String id = resultSet.getString("id");
String userName = resultSet.getString("userName");
System.out.println("id:"+id+",userName:"+userName);
}
//最後釋放連接,將資源交給連接池進行回收
jdbcPool.releaseJdbcConnection(resultSet,preparedStatement,connection);
System.out.println("將使用後的連接還給數據庫連接池後的連接數:"+jdbcPool.getCurrConnectionNum());
}catch (Exception e){
e.printStackTrace();
}
}
}
運行結果
作爲數據庫連接池,沒有存在多個實例的必要,所以這裏使用了單例模式來實現數據庫連接池,使用了單例模式那麼就會出現線程安全問題,我使用了synchronized對其中的方法進行加鎖,保證一次只有一個線程能進入該方法,且存儲數據庫連接對象的容器使用的是CopyOnWriteArrayList併發容器。