一.问题分析
关于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("");
}else{
response.getWriter().print("");
}
}
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"%>
注册
上边程序在进行多次交互之后会报出“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资源,真正体现出了链接池的‘池’的特性。