c3p0資料庫連線池如何正確的關閉資源(“too many connections”的解決辦法)
一.問題分析
關於c3p0資料庫連線池的資源的關閉是一個很重要的問題,但是資源的關閉不僅僅是隻呼叫close()方法,將連結放入池中那麼簡單,如果你不考慮資料來源DataSource的關閉,那麼你的Demo將在很少的資料庫互動之後報出“too many connections”。下面先看這樣一些程式碼(注:測試程式我也就不按照正確的開發模式寫程式碼了):
CreateConnection.java(本文討論的重點在這裡)
public class CreateConnection { private ComboPooledDataSource dataSource=new ComboPooledDataSource("mysql"); public Connection getConnection(){ Connection conn=null; try { conn=dataSource.getConnection(); } catch (SQLException e) { e.printStackTrace(); } return conn; } public DataSource getDataSource() { return dataSource; } public void closeConn(Connection conn){ try { if(conn!=null && conn.isClosed()){ conn.close(); } } catch (SQLException e) { e.printStackTrace(); } } }
testDao.java
public class testDao { public int reginster (String username,String pwd){ CreateConnection c=new CreateConnection(); Connection conn=c.getConnection(); int i=0; String sql="insert into user(username,pwd)values(?,?)"; try { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1,username); ps.setString(2,pwd); i=ps.executeUpdate(); }catch (Exception ex){ ex.printStackTrace(); }finally{ try { c.closeConn(conn); }catch (Exception ex){ ex.printStackTrace();} } return i; } }
testServlet.java
public class testServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String username=request.getParameter("username"); String pwd=request.getParameter("pwd"); testDao td=new testDao(); response.setCharacterEncoding("utf-8"); response.setContentType("text/html;charset=utf-8"); int i=td.reginster(username,pwd); if(i==1){ System.out.println("------------------------test"); response.getWriter().print("<script language='javascript'>alert('註冊成功!!!');history.back(-1)</script>"); }else{ response.getWriter().print("<script language='javascript'>alert('註冊失敗!!!');history.back(-1)</script>"); } } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request,response); } }
login.jsp
<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf-8"%>
<html>
<head>
<title>註冊</title>
</head>
<body>
<form action="bluemsun/testServlet" method="post">
使用者名稱: <input type="text" name="username"/><br>
密碼:<input type="password" name="pwd"/>
<input type="submit" value="註冊"/>
</form>
</body>
</html>
上邊程式在進行多次互動之後會報出“too many connections”的錯誤,原因分析如下:原因就在於程式只將連結放回了連結池使其處於空閒狀態,但是卻沒有關閉dataSource,這就導致目前dataSource中的初始化連結一直處於連結池中,當下一次互動開始後又重新建立了新的dataSource,而下一次的dataSource還是不能被關閉,就這樣每次都會累積不能被重用的連結,假如在配置時連線池的初始連結至配置數時100,而資料庫的最大連結數是1000,那麼在經過10次互動後會出現“too many connections”的錯誤。然而當把資源關閉的方法修改成如下的方法時就會發現永遠不會報出”too many connections”的錯誤:
public void closeConn(Connection conn){
try {
if(conn!=null && conn.isClosed()){
conn.close();
}
datasource.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
datasource.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
上邊貼出來的方法時將DataSource也進行了關閉。
二.方法改進
通過上邊的分析,我們基本知道了問題出現的原因了,接下來就是改進連線池的使用了,先貼一下改進的程式碼:
public class C3P0Util {
private static DataSource dataSource=null;
static{
dataSource=new ComboPooledDataSource("mysql1");
}
/**
* 獲取資料庫連線
* @return
*/
public static Connection getConnection(){
Connection conn=null;
try {
conn=dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
public static DataSource getDataSource() {
return dataSource;
}
/**
*
* @param conn
*/
public static void closeConn(Connection conn){
try {
if(conn!=null && conn.isClosed()){
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
從上邊程式中我們可以看出,是把所有的資源都用static描述,也就是把所有資源都交給了類,這樣做的好處是,每次互動都是共享C3P0Util類的datasource資源,不需要每次建立和關閉datasource資源,真正體現出了連結池的‘池’的特性。