使用jdbc连接数据库,获取数据库连接的过程在网络上其他文章中多有介绍,在此不多介绍,本文主要分析获取数据库连接并且完成处理过程后如何处理这个连接。
先看一个简单的模拟数据库连接池获取连接的实例:,
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.logging.Logger;
public class DataSourceUtils {
// 私有化构造函数,作为构建单例模式的第一步
private DataSourceUtils() {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//连接池的容器初始化
pool = new LinkedList<Connection>();
//填充容器
for (int i = 0; i < poolSize; i++) {
Connection con = null;
try {
con = DriverManager.getConnection(sqlurl,root,password);
} catch (SQLException e) {
e.printStackTrace();
}
pool.add(con);
}
log.info(" " + pool.size());
System.out.println(" ");
}
private static String root = "root";
private static String password = "1995";
private static Logger log = Logger.getLogger("com.utils.DataSourceUtils");
private static final String sqlurl = "jdbc:mysql://localhost:3306/hotelManager";
private static final int poolSize = 10;
//连接池的容器
private static LinkedList<Connection> pool;
//静态内部类实现单例模式
private static class InnerClass {
static DataSourceUtils instance = new DataSourceUtils();
}
// 获取单例
public Connection getConneciton() {
return getConnection(0);
}
// 获取数据库连接
public Connection getConnection(long time) {
//同步过程
synchronized (pool) {
log.info("number"+pool.size());
long sumTime = 0;
if (time > 0) {
//超时计算处理
while (time >= sumTime) {
if (pool.size() == 0) {
try {
pool.wait(50);
sumTime+=50;
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//如果连接池中有连接,则取出并返回
Connection con = pool.pollFirst();
log.info("get"+pool.size());
return con;
}
}
return null;
} else {
//无限等待模式:
while (true) {
if (pool.size() == 0) {
try {
pool.wait(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
Connection con = pool.pollFirst();
log.info("get"+pool.size());
return con;
}
}
}
}
}
public static DataSourceUtils getInstance() {
// TODO Auto-generated method stub
return InnerClass.instance;
}
//将数据库连接放回连接池中
public void realiseConnection(Connection con) {
synchronized (pool) {
pool.addLast(con);
pool.notifyAll();
}
}
}
在以上提供的连接池中可以获取新的连接,在用完之后在放回连接池中。这里没有提供连接池扩容的方法,后边可以加上。 到这里没有太大问题,下边看处理连接的过程:
import java.io.ObjectInputStream.GetField;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
public class RoomDao {
//获取遇到数据库工具类:
private static DataSourceUtils data = DataSourceUtils.getInstance();
public static final String addSql = "select * from adminUser";
//测试方法
public static String test() {
//获取数据库连接
Connection con = data.getConneciton();
PreparedStatement ps = null;
ResultSet rs = null;
try {
//执行sql语句之前的准备工作
ps = con.prepareStatement(addSql);
rs = ps.executeQuery();
//返回值无所谓
return null;
} catch (SQLException e) {
e.printStackTrace();
} finally {
//这里执行的PreparedStatement和ResultSet的释放,先不打开注释
/*
if(rs!=null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
*/
data.realiseConnection(con);
}
return null;
}
//运行10个线程,同时执行test()方法
public static void main(String args[]){
for(int i=0;i<10;i++){
new Thread(){
public void run(){
while(true){
test();
}
}
}.start();
}
}
}
在不打开注释的情况下分析,即在不主动释放PreparedStatement和ResultSet的情况下分析:
在dos下执行第二个类,使用jvm内存分析工具jconsole.exe检测内存使用情况,结果如图:
图中可以看到内存的使用量总的趋势是一路攀升,其中折线是由于jvn自动GC的过程,会自动清理不需要的对象,因此内存使用降低。
再看下边的例子,将注释部分打开,即主动将PreparedStatement和ResultSet关闭得到的结果:
可以程序使用的内存空间总的趋势是非常稳定的,也就意味着只有主动关闭了PreparedStatement和ResultSet,JVM才会主动释放该对象。
走到了这里,解决方案已经出现,问题得到解决。但是还存在一个问题:如果关闭了Connection,那么PreparedStatement和ResultSet是否会被关闭?这个问题无法通过上边的方案记性测试,因为大量短时间创建Connection会导致一个问题:
The driver was unable to create a connection due to an inability to establish the client portion of a socket. ----数据库连接错误。
原因请参考这篇博客。