一、建立数据库连接池(基本信息)
package book.database;
/**
* 创建一个数据库连接池需要的信息:用户名、密码、最大连接数等
* @author wuhailin
*
*/
public class JDBCInfo {
/**连接数据库的相关信息*/
private String driver;//连接数据库的驱动类
private String url;//数据库的路径
private String user;//用户名
private String password;//密码
private String name;//数据库连接池的名字
private int maxconn;//最大连接数
public JDBCInfo(){
this.driver="";
this.url="";
this.name="";
this.maxconn=0;
}
public JDBCInfo(String name,String driver,String url,String user,String password,int maxconn){
this.name=name;
this.driver=driver;
this.user=user;
this.password=password;
this.maxconn=maxconn;
this.url=url;
}
public String getDriver() {
return driver;
}
public void setDriver(String driver) {
this.driver = driver;
}
public int getMaxconn() {
return maxconn;
}
public void setMaxconn(int maxconn) {
this.maxconn = maxconn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
}
二、数据库连接管理器
package book.database;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
/**
* 数据库连接管理器,能够管理不同类的数据库的连接池
* @author Administrator
*
*/
public class DataConnectMgr {
//默认的数据库连接池配置文件
public static final String DEFAULT_DB_PROPERTLES="./db.properties";
//唯一实例
static private DataConnectMgr instance;
//当前连接到该数据连接管理器的客户端数目
static private int clients;
//存放驱动类,对于每个数据库,都有一个驱动类
private Vector drivers=new Vector();
//存放已经建立的连接池,每种数据库都有一个连接池
private Hashtable pools=new Hashtable();
/**
*构造函数私有,以防止其他对象创建本类实例
*/
private DataConnectMgr(){
init();
}
private DataConnectMgr(LinkedList drivers,Hashtable jdbcs){
init2(drivers,jdbcs);
}
/**
* 单例模式,返回唯一一个实例,如果是第一次调用此方法,则创建实例
* 根据默认的数据库连接池配置文件创建连接池
* @return 唯一实例
*/
public static synchronized DataConnectMgr getInstance() {
if(instance==null){
clients++;
return instance;
}else{
instance=new DataConnectMgr();
return instance;
}
}
/**
* 获取数据库连接管理器实例,如果不存在,便创建实例,并指定了创建连接池的参数
* @param jdbcInfo 连接池的参数
* @return
*/
public static synchronized DataConnectMgr getInstance(JDBCInfo jdbcInfo){
if(instance!=null){
clients++;
return instance;
}
LinkedList drivers=new LinkedList();
drivers.add(jdbcInfo.getDriver());
Hashtable jdbcs=new Hashtable();
jdbcs.put(jdbcInfo.getName(),jdbcInfo);
return getInstance(drivers,jdbcs);
}
/**
* 获取数据库连接管理器实例,如果不存在,便创建实例,可以一次创建多个连接
* @param drivers 每个连接池的数据库驱动类
* @param jdbcs 每个连接池的连接参数
* @return
*/
public static synchronized DataConnectMgr getInstance(LinkedList drivers,Hashtable jdbcs){
if(instance==null){
instance=new DataConnectMgr(drivers,jdbcs);
}
clients++;
return instance;
}
/**
* 读取默认的数据库连接池配置文件,根据默认值完成初始化,创建数据库连接池
*/
private void init() {
InputStream is=null;
Properties dbProps=new Properties();
try{
is=new FileInputStream(DEFAULT_DB_PROPERTLES);
dbProps.load(is);
}catch(Exception e){
System.err.println("不能读取默认的数据库连接池配置文件,请确认文件是否存在:"
+DEFAULT_DB_PROPERTLES);
return;
}
loadDrivers(dbProps);
createPools(dbProps);
}
/**
* 创建多个数据库连接池
* @param drivers 每个连接池的数据库驱动类
* @param jdbcInfo 每个连接池的连接参数
*/
private void init2(LinkedList drivers, Hashtable jdbcInfo) {
loadDrivers(drivers);
createPools(jdbcInfo);
}
/**
* 动态加载数据库连接驱动类
* @param mdrivers
*/
private void loadDrivers(LinkedList mdrivers){
for(int i=0;i<mdrivers.size();i++){
try{
//根据数据库连接驱动类,利用反射机制创建驱动类对象
Driver driver=(Driver)Class
.forName((String)mdrivers.get(i)).newInstance();
DriverManager.registerDriver(driver);
drivers.addElement(driver);
System.out.println("成功加载数据库连接驱动:"
+mdrivers.get(i));
}catch(Exception e){
System.err.println("加载数据库连接驱动信息失败:"
+mdrivers.get(i)+".错误信息:"+e);
}
}
}
/**
* 装载和注册数据库连接配置文件中所有的JDBC驱动程序
* @param props 属性
*/
private void loadDrivers(Properties props){
String driverClass=null;
driverClass=props.getProperty("drivers");
StringTokenizer st=new StringTokenizer(driverClass);
while(st.hasMoreElements()){
String driverClassName=st.nextToken().trim();
try{
//新建一个驱动类
Driver driver=(Driver)Class.forName(driverClassName)
.newInstance();
//注册驱动
DriverManager.registerDriver(driver);
drivers.addElement(driver);
System.out.println("成功加载数据库连接驱动:"+
driverClassName);
}catch(Exception e){
System.err.println("加载数据库驱动连接失败:"+
driverClassName+".错误信息:"+e);
}
}
}
/**
* 根据指定的数据库连接池配置文件创建连接池实例
* @param 连接池属性
*/
private void createPools(Properties props){
Enumeration propNames=props.propertyNames();
while(propNames.hasMoreElements()){
String > //获得连接数据库的各种属性
if(name.endsWith(".url")){
String poolName=name.substring(0,name.lastIndexOf("."));
String url=props.getProperty(poolName+".url");
if(url==null){
continue;
}
String user=props.getProperty(poolName+".user");
String password=props.getProperty(poolName+".password");
String maxconn=props.getProperty(poolName+".maxconn","0");
int max;
try{
max=Integer.valueOf(maxconn).intValue();
}catch(NumberFormatException e){
System.err.println("最大连接数限制数错误:"+
maxconn+".连接池名:"+poolName);
max=0;
}
System.out.println("准备创建数据库池:"+poolName);
DBConnectionPool pool=new DBConnectionPool(poolName,url,user,password,max);
pools.put(poolName,pool);
System.out.println("创建数据库连接池:"+poolName+"成功");
}
}
}
/**
* 根据数据库连接池配置信息创建连接池
* @param jdbcInfos
*/
private void createPools(Hashtable jdbcInfos){
Iterator it=jdbcInfos.entrySet().iterator();
while(it.hasNext()){
Map.Entry en=(Map.Entry)it.next();
JDBCInfo info=(JDBCInfo)en.getValue();
if(info.getUrl()==null){
continue;
}
System.out.println("准备创建数据库连接池:"+(String)en.getKey());
DBConnectionPool pool=new DBConnectionPool((String)en.getKey(),info.getUrl(),info.getUser(),
info.getPassword(),info.getMaxconn());
pools.put(en.getKey(),pool);
}
}
/**
* 获得一个可用的(空闲的)连接,如果没有可用连接,且已有连接数小于最大连接数
* 限制,则创建并返回新的连接
* @param name 在属性文件中定义的连接池的名字
* @return Connection 可用连接或者null
*/
public Connection getConnection(String name){
DBConnectionPool pool=(DBConnectionPool)pools.get(name);
if(pool!=null){
System.out.println("从数据库连接池:"+pool.getName()+"获取一个连接!");
return pool.getConnection();
}
return null;
}
/**
* 获得一个可用的连接,若没有可用的连接,且已有连接数小于最大连接限制
* 则创建并返回新连接;否则,在指定的时间内等待其他线程释放连接
* @param name 连接池名字
* @param time 以毫秒记得等待时间
* @return Connection 可用连接或null
*/
public Connection getConnection(String name,long time){
DBConnectionPool pool=(DBConnectionPool)pools.get(name);
if(pool!=null){
return pool.getConnection(time);
}
return null;
}
/**
* 将连接对象返回给由名字指定的连接池
* @param name 在属性文件中定义的连接池名字,即数据源信息的name字段
* @param con 连接对象
*/
public void freeConnection(String name,Connection con){
DBConnectionPool pool=(DBConnectionPool)pools.get(name);
if(pool!=null){
System.out.println("释放了一个数据库到连接池"+pool.getName());
pool.freeConnection(con);
}
}
/**
* 关闭所有连接,撤销驱动程序的注册
*只有当连接到该数据库连接管理器的客户端数目为0时,才能够完成撤销
*/
public synchronized void release(){
if(--clients!=0){
return;
}
//释放连接池
Enumeration allPools=pools.elements();
while(allPools.hasMoreElements()){
DBConnectionPool pool=(DBConnectionPool)allPools.nextElement();
System.out.println("准备关闭数据库连接池:"+pool.getName());
pool.release();
System.out.println("数据库连接池:"+pool.getName()+"已经被关闭!");
}
//反注册已经注册的数据库连接驱动类
Enumeration allDrivers=drivers.elements();
while(allDrivers.hasMoreElements()){
Driver driver=(Driver)allDrivers.nextElement();
try{
DriverManager.deregisterDriver(driver);
System.out.println("数据库连接驱动:"+driver.getClass().getName()+"已经被注销了!");
}catch(SQLException e){
}
}
}
}
/**
* 此内部类定义了一个连接池,它能够根据要求创建心连接,直到预定的最大
* 连接为止,在返回连接给客户程序之前,它能够验证连接的有效性
* @author wuhailin
*
*/
class DBConnectionPool{
private int checkOut=0;//当前已经被取走的数据库连接数,也就是正在被使用的连接数
private Vector freeConnections=new Vector();//该连接池中可用的数据库连接
private int maxConn;//该连接池容许的最大数据库连接数
private String name;//数据库连接池名字
private String user;//连接数据库的用户名
private String password;//连接数据库密码
private String URL;//数据库的URL
//默认构造函数
public DBConnectionPool(){
this.maxConn=0;
this.password="";
this.URL="";
}
/**
* 创建新的连接池
* @param name 连接池的名字
* @param URL 数据库的JDBC URL
* @param user 数据库帐号或null
* @param password 密码或者null
* @param maxConn 此连接池容许建立的最大连接池
*/
public DBConnectionPool(String name,String URL,String user,String password,int maxConn){
this.name=name;
this.URL=URL;
this.user=user;
this.password=password;
this.maxConn=maxConn;
this.initConnection();
}
/**创建新的连接*/
private Connection newConnection(){
Connection con=null;
try{
if(user==null){
con=DriverManager.getConnection(URL.trim());
}else{
con=DriverManager.getConnection(URL,user,password);
}
System.out.println("连接池"+this.name+"创建一个新的数据库连接,目前有" +this.checkOut+"个连接在使用!");
}catch(SQLException e){
System.err.println(e.getMessage());
return null;
}
return con;
}
/**预先打开一个连接*/
private void initConnection(){
Connection con=getConnection();
freeConnections.addElement(con);
}
/**
* 从连接池获得一个可用连接。如没有空闲的连接且当前使用的连接小于最大连接
* 数限制,则创建新的连接。如原来登记为可用的连接不再有效,则从向量删除
* 然后递归调用自己以尝试新的可用连接
*/
public synchronized Connection getConnection(){
Connection con=null;
if(freeConnections.size()>0){
//获取向量中第一个可用连接
con=(Connection)freeConnections.firstElement();
freeConnections.removeElementAt(0);
try{
//如果存放的这个连接已经过期或者不可用,则继续获取
if((con==null)||(con.isClosed())){
con=getConnection();
}
}catch(SQLException e){
con=getConnection();
}
}else if(maxConn==0||checkOut<maxConn){
//创建新的连接
System.out.println("数据库连接池:"+this.name+"准备创建一个新的连接");
con=newConnection();
}else{
System.out.println("数据库连接池"+this.name+"没有可用的连接!");
}
if(con!=null){
this.checkOut++;
}
return con;
}
/**
* 从连接池获取可用连接,可以指定客户程序能等待的最长时间
* 参见前一个getConnection()方法
* @param timeout 以毫秒的等待时间限制
*/
public synchronized Connection getConnection(long timeout){
long startTime=new Date().getTime();
Connection con;
while((con=getConnection())==null){
try{
//等待一段时间,期待其他客户端释放连接
wait(timeout);
}catch(InterruptedException e){
}
if((new Date().getTime()-startTime)>=timeout){
//wait()返回的原因是超时,表示没有得到可用的连接,返回null
return null;
}
}
return con;
}
/**
* 将不再使用的连接返回给连接池
* @param con 客户程序释放的连接
*/
public synchronized void freeConnection(Connection con){
//将指定连接加入到向量末尾
freeConnections.addElement(con);
checkOut--;
notifyAll();
}
public synchronized void release(){
Enumeration allConnections=freeConnections.elements();
while(allConnections.hasMoreElements()){
Connection con=(Connection)allConnections.nextElement();
try{
con.close();
System.out.println("关闭了数据库连接池:"+this.name+"中的数据库连接!");
}catch(SQLException e){
System.err.println("无法关闭连接池"+this.name+"中的连接"+e.getMessage());
}
}
freeConnections.removeAllElements();
}
/**返回数据库连接池的名字*/
public String getName(){
return this.name;
}
}
三、测试
package book.database;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DatabasePoolTest {
public static JDBCInfo getJDBCInfo(){
JDBCInfo jdbc=new JDBCInfo();
jdbc.setDriver("oracle.jdbc.driver.OracleDriver");
jdbc.setName("Oracle");
jdbc.setUrl("jdbc:oracle:thin:@59.69.65.86:1521:orcl");
jdbc.setUser("wuhailin");
jdbc.setPassword("wuhailin");
jdbc.setMaxconn(10);
return jdbc;
}
public static void main(String[] args) {
JDBCInfo jdbc=getJDBCInfo();
String sql="select * from student_basic";
DataConnectMgr mgr=DataConnectMgr.getInstance(jdbc);
Connection con=mgr.getConnection(jdbc.getName());
try{
System.out.println("正在使用刚刚获得的数据库连接");
Statement sm=con.createStatement();
ResultSet rs=sm.executeQuery(sql);
OperateDB.showResultSet(rs);
sm.close();
}catch(SQLException e){
System.err.println("连接数据库出错!");
}finally{
mgr.freeConnection(jdbc.getName(), con);
}
mgr.release();
}
}