数据库连接池
据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正式针对这个问题提出来的.数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个
数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中, 这些数据库连接的数量是由最小数据库连接数来设定的.无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量.连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中.
数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:
- 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
- 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
- 如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放.
编写数据库连接池
编写连接池需实现java.sql.DataSource接口。DataSource接口中定义了两个重载的getConnection方法:
Connection getConnection()
Connection getConnection(String username, String password)
实现DataSource接口,并实现连接池功能的步骤:
-
在DataSource构造函数中批量创建与数据库的连接,并把创建的连接加入LinkedList对象中。
-
实现getConnection方法,让getConnection方法每次调用时,从LinkedList中取一个Connection返回给用户。
-
当用户使用完Connection,调用Connection.close()方法时,Collection对象应保证将自己返回到LinkedList中,而不要把conn还给数据库。Collection保证将自己返回到LinkedList中是此处编程的难点。
使用数据库连接池核心就是使用代理技术构造connection,并且当使用完后还给连接池而不是断开
return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (!method.getName().equals("close")) {
return method.invoke(conn, args);
} else {
//如果调用的是Connection对象的close方法,就把conn还给数据库连接池
listConnections.add(conn);
return null;
}
}
});
数据库连接池代码:
- 构建数据库连接池,让其类实现DataSource接口
public class JdbcPool implements DataSource {
private static LinkedList<Connection> listConnections = new LinkedList<Connection>();
static {
//在静态代码块中加载db.properties数据库配置文件
InputStream in = JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
Properties prop = new Properties();
try {
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
//数据库连接池的初始化连接数大小
int jdbcPoolInitSize = Integer.parseInt(prop.getProperty("jdbcPoolInitSize"));
//加载数据库驱动
Class.forName(driver);
for (int i = 0; i < jdbcPoolInitSize; i++) {
Connection conn = DriverManager.getConnection(url, username, password);
//将获取到的数据库连接加入到listConnections集合中,listConnections集合此时就是一个存放了数据库连接的连接池
listConnections.add(conn);
}
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
/* 获取数据库连接
* @see javax.sql.DataSource#getConnection()
*/
public Connection getConnection() throws SQLException {
//如果数据库连接池中的连接对象的个数大于0
if (listConnections.size() > 0) {
//从listConnections集合中获取一个数据库连接
final Connection conn = listConnections.removeFirst();
System.out.println("listConnections数据库连接池大小是" + listConnections.size());
//返回Connection对象的代理对象
return (Connection) Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if (!method.getName().equals("close")) {
return method.invoke(conn, args);
} else {
//如果调用的是Connection对象的close方法,就把conn还给数据库连接池
listConnections.add(conn);
return null;
}
}
});
} else {
throw new RuntimeException("对不起,数据库忙");
}
}
- 构造获取连接并且还连接给连接池
public class JdbcConnectionUtil {
private static JdbcPool pool = new JdbcPool();
public static Connection getConnection() throws SQLException {
return pool.getConnection();
}
public static void release(Connection conn, Statement st) {
if (st != null) {
try {
//关闭负责执行SQL命令的Statement对象
st.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
//关闭Connection数据库连接对象
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
测试案列:
public class JdbcConnectionTest {
public static void main(String[] args) throws SQLException {
MarkDataBean markDataSt = new MarkDataBean();
markDataSt.setStockCode("asfv");
markDataSt.setStockName("asfv");
markDataSt.setTimestamp("asfv");
markDataSt.setMax("asfv");
markDataSt.setMin("asfv");
markDataSt.setOpen("asfv");
markDataSt.setClose("asfv");
markDataSt.setNow("asfv");
JdbcConnectionUtil ju = new JdbcConnectionUtil();
Connection conn=ju.getConnection();
String str ="insert into zhangzhe_mark_data(stockCode,stockName,timestamp,max,min,open,close,now) values(?,?,?,?,?,?,?,?)";
CallableStatement cst=conn.prepareCall(str);
cst.setString(1,markDataSt.getStockCode());
cst.setString(2,markDataSt.getStockName());
cst.setString(3,markDataSt.getTimestamp());
cst.setString(4,markDataSt.getMax());
cst.setString(5,markDataSt.getMin());
cst.setString(6,markDataSt.getOpen());
cst.setString(7,markDataSt.getClose());
cst.setString(8,markDataSt.getNow());
cst.executeUpdate();
ju.release(conn, cst);
System.out.println("已成功存入数据库!!!" + markDataSt.toString());
}
}