- 引言
近年来,随着Internet/Intranet建网技术的飞速发展和在世界范围内的迅速普及,计算机
应用程序已从传统的桌面应用转到Web应用。基于B/S(Browser/Server)架构的3层开发模式逐渐取代C/S(Client/Server)架构的开发模式,成为开发企业级应用和电子商务普遍采用的技术。在Web应用开发的早期,主要使用的技术是CGI﹑ASP﹑PHP等。之后,Sun公司推出了基于Java语言的Servlet+Jsp+JavaBean技术。相比传统的开发技术,它具有跨平台﹑安全﹑有效﹑可移植等特性,这使其更便于使用和开发。
Java应用程序访问数据库的基本原理
在Java语言中,JDBC(Java DataBase Connection)是应用程序与数据库沟通的桥梁,
即Java语言通过JDBC技术访问数据库。JDBC是一种“开放”的方案,它为数据库应用开发人员﹑数据库前台工具开发人员提供了一种标准的应用程序设计接口,使开发人员可以用纯Java语言编写完整的数据库应用程序。JDBC提供两种API,分别是面向开发人员的API和面向底层的JDBC驱动程序API,底层主要通过直接的JDBC驱动和JDBC-ODBC桥驱动实现与数据库的连接。
一般来说,Java应用程序访问数据库的过程(如图1所示)是:
①装载数据库驱动程序;
②通过JDBC建立数据库连接;
③访问数据库,执行SQL语句;
④断开数据库连接。
JDBC作为一种数据库访问技术,具有简单易用的优点。但使用这种模式进行Web应用
程序开发,存在很多问题:首先,每一次Web请求都要建立一次数据库连接。建立连接是一个费时的活动,每次都得花费0.05s~1s的时间,而且系统还要分配内存资源。这个时间对于一次或几次数据库操作,或许感觉不出系统有多大的开销。可是对于现在的Web应用,尤其是大型电子商务网站,同时有几百人甚至几千人在线是很正常的事。在这种情况下,频繁的进行数据库连接操作势必占用很多的系统资源,网站的响应速度必定下降,严重的甚至会造成服务器的崩溃。不是危言耸听,这就是制约某些电子商务网站发展的技术瓶颈问题。其次,对于每一次数据库连接,使用完后都得断开。否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将不得不重启数据库。还有,这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。
数据库连接池(connection pool)的工作原理
1、基本概念及原理
由上面的分析可以看出,问题的根源就在于对数据库连接资源的低效管理。我们知道,
对于共享资源,有一个很著名的设计模式:资源池(Resource Pool)。该模式正是为了解决资源的频繁分配﹑释放所造成的问题。为解决上述问题,可以采用数据库连接池技术。数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。我们可以通过设定连接池最大连接数来防止系统无尽的与数据库连接。更为重要的是我们可以通过连接池的管理机制监视数据库的连接的数量﹑使用情况,为系统开发﹑测试及性能调整提供依据。连接池的基本工作原理见下图2。
图2 连接池的基本工作原理
2、服务器自带的连接池
JDBC的API中没有提供连接池的方法。一些大型的WEB应用服务器如BEA的WebLogic和IBM的WebSphere等提供了连接池的机制,但是必须有其第三方的专用类方法支持连接池的用法。
连接池关键问题分析
1、并发问题
为了使连接管理服务具有最大的通用性,必须考虑多线程环境,即并发问题。这个问题相对比较好解决,因为Java语言自身提供了对并发管理的支持,使用synchronized关键字即可确保线程是同步的。使用方法为直接在类方法前面加上synchronized关键字,如:
public synchronized Connection getConnection()
2、多数据库服务器和多用户
对于大型的企业级应用,常常需要同时连接不同的数据库(如连接Oracle和Sybase)。如何连接不同的数据库呢?我们采用的策略是:设计一个符合单例模式的连接池管理类,在连接池管理类的唯一实例被创建时读取一个资源文件,其中资源文件中存放着多个数据库的url地址(
1),DBConnectionPool.java 数据库连接池类
2),DBConnectionManager.java 数据库管理类
3),DSConfigBean .java 单个数据库连接信息Bean
4),ParseDSConfig.java 操作多(这个'多'包括不同的数据库和同一种数据库有多个数据库)数据 配置文件xml
5),ds.config.xml 数据库配置文件xml
DBConnectionPool.java
package com.tony.db;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Timer;
/**
* Created with IntelliJ IDEA.数据库连接池类
* User: Tony.Wang
* Date: 12-9-24
* Time: 上午8:59
* To change this template use File | Settings | File Templates.
*/
public class DBConnectionPool {
private Connection con = null;
private int inUsed = 0; //使用的连接数
private ArrayList freeConnections = new ArrayList();//容器,空闲连接
private int minConn; //最小连接数
private int maxConn; //最大连接
private String name; //连接池名字
private String password; //密码
private String url; //数据库连接地址
private String driver; //驱动
private String user; //用户名
public Timer timer; //定时
/**
*
*/
public DBConnectionPool() {
// TODO Auto-generated constructor stub
}
/**
* 创建连接池
*
* @param driver
* @param name
* @param URL
* @param user
* @param password
* @param maxConn
*/
public DBConnectionPool(String name, String driver, String URL, String user, String password, int maxConn) {
this.name = name;
this.driver = driver;
this.url = URL;
this.user = user;
this.password = password;
this.maxConn = maxConn;
}
/**
* 用完,释放连接
*
* @param con
*/
public synchronized void freeConnection(Connection con) {
this.freeConnections.add(con);//添加到空闲连接的末尾
this.inUsed--;
}
/**
* timeout 根据timeout得到连接
*
* @param timeout
* @return
*/
public synchronized Connection getConnection(long timeout) {
Connection con = null;
if (this.freeConnections.size() > 0) {
con = (Connection) this.freeConnections.get(0);
if (con == null) con = getConnection(timeout); //继续获得连接
} else {
con = newConnection(); //新建连接
}
if (this.maxConn == 0 || this.maxConn < this.inUsed) {
con = null;//达到最大连接数,暂时不能获得连接了。
}
if (con != null) {
this.inUsed++;
}
return con;
}
/**
* 从连接池里得到连接
*
* @return
*/
public synchronized Connection getConnection() {
Connection con = null;
if (this.freeConnections.size() > 0) {
con = (Connection) this.freeConnections.get(0);
this.freeConnections.remove(0);//如果连接分配出去了,就从空闲连接里删除
if (con == null) con = getConnection(); //继续获得连接
} else {
con = newConnection(); //新建连接
}
if (this.maxConn == 0 || this.maxConn < this.inUsed) {
con = null;//等待 超过最大连接时
}
if (con != null) {
this.inUsed++;
System.out.println("得到 " + this.name + " 的连接,现有" + inUsed + "个连接在使用!");
}
return con;
}
/**
* 释放全部连接
*/
public synchronized void release() {
Iterator allConns = this.freeConnections.iterator();
while (allConns.hasNext()) {
Connection con = (Connection) allConns.next();
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
this.freeConnections.clear();
}
/**
* 创建新连接
*
* @return
*/
private Connection newConnection() {
try {
Class.forName(driver);
con = DriverManager.getConnection(url, user, password);
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("sorry can't find db driver!");
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
System.out.println("sorry can't create Connection!");
}
return con;
}
/**
* 定时处理函数
*/
public synchronized void TimerEvent() {
//暂时还没有实现以后会加上的
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
/**
* @return the driver
*/
public String getDriver() {
return driver;
}
/**
* @param driver the driver to set
*/
public void setDriver(String driver) {
this.driver = driver;
}
/**
* @return the maxConn
*/
public int getMaxConn() {
return maxConn;
}
/**
* @param maxConn the maxConn to set
*/
public void setMaxConn(int maxConn) {
this.maxConn = maxConn;
}
/**
* @return the minConn
*/
public int getMinConn() {
return minConn;
}
/**
* @param minConn the minConn to set
*/
public void setMinConn(int minConn) {
this.minConn = minConn;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the password
*/
public String getPassword() {
return password;
}
/**
* @param password the password to set
*/
public void setPassword(String password) {
this.password = password;
}
/**
* @return the url
*/
public String getUrl() {
return url;
}
/**
* @param url the url to set
*/
public void setUrl(String url) {
this.url = url;
}
/**
* @return the user
*/
public String getUser() {
return user;
}
/**
* @param user the user to set
*/
public void setUser(String user) {
this.user = user;
}
}
DBConnectionManager.java
package com.tony.db;
import java.sql.Connection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
/**
* Created with IntelliJ IDEA.数据库连接池管理类
* User: Tony.Wang
* Date: 12-9-24
* Time: 上午8:59
* To change this template use File | Settings | File Templates.
*/
public class DBConnectionManager {
static private DBConnectionManager instance;//唯一数据库连接池管理实例类
static private int clients; //客户连接数
private Vector drivers = new Vector();//驱动信息
private Hashtable pools = new Hashtable();//连接池
/**
* 实例化管理类
*/
public DBConnectionManager() {
// TODO Auto-generated constructor stub
this.init();
}
/**
* 得到唯一实例管理类
*
* @return
*/
static synchronized public DBConnectionManager getInstance() {
if (instance == null) {
instance = new DBConnectionManager();
}
return instance;
}
/**
* 释放连接
*
* @param name
* @param con
*/
public void freeConnection(String name, Connection con) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);//根据关键名字得到连接池
if (pool != null)
pool.freeConnection(con);//释放连接
}
public void freeConnection() {
}
/**
* 得到一个连接根据连接池的名字name
*
* @param name
* @return
*/
public Connection getConnection(String name) {
DBConnectionPool pool = null;
Connection con = null;
pool = (DBConnectionPool) pools.get(name);//从名字中获取连接池
con = pool.getConnection();//从选定的连接池中获得连接
if (con != null)
System.out.println("得到连接。。。");
return con;
}
/**
* 得到一个连接,根据连接池的名字和等待时间
*
* @param name
* @param timeout
* @return
*/
public Connection getConnection(String name, long timeout) {
DBConnectionPool pool = null;
Connection con = null;
pool = (DBConnectionPool) pools.get(name);//从名字中获取连接池
con = pool.getConnection(timeout);//从选定的连接池中获得连接
System.out.println("得到连接。。。");
return con;
}
/**
* 释放所有连接
*/
public synchronized void release() {
Enumeration allpools = pools.elements();
while (allpools.hasMoreElements()) {
DBConnectionPool pool = (DBConnectionPool) allpools.nextElement();
if (pool != null) pool.release();
}
pools.clear();
}
/**
* 创建连接池
*
* @param dsb
*/
private void createPools(DSConfigBean dsb) {
DBConnectionPool dbpool = new DBConnectionPool();
dbpool.setName(dsb.getName());
dbpool.setDriver(dsb.getDriver());
dbpool.setUrl(dsb.getUrl());
dbpool.setUser(dsb.getUsername());
dbpool.setPassword(dsb.getPassword());
dbpool.setMaxConn(dsb.getMaxconn());
System.out.println("ioio:" + dsb.getMaxconn());
pools.put(dsb.getName(), dbpool);
}
/**
* 初始化连接池的参数
*/
private void init() {
//加载驱动程序
this.loadDrivers();
//创建连接池
Iterator alldriver = drivers.iterator();
while (alldriver.hasNext()) {
this.createPools((DSConfigBean) alldriver.next());
System.out.println("创建连接池。。。");
}
System.out.println("创建连接池完毕。。。");
}
/**
* 加载驱动程序
*
*/
private void loadDrivers() {
ParseDSConfig pd = new ParseDSConfig();
//读取数据库配置文件
drivers = pd.readConfigInfo("ds.config.xml");
System.out.println("加载驱动程序。。。");
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}
ParseDSConfig.java
package com.tony.db;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
/**
* Created with IntelliJ IDEA.操作配置文件类 读、写、修改、删除等操作
* User: Tony.Wang
* Date: 12-9-24
* Time: 上午9:01
* To change this template use File | Settings | File Templates.
*/
public class ParseDSConfig {
/**
* 构造函数
*/
public ParseDSConfig() {
// TODO Auto-generated constructor stub
}
/**
* 读取xml配置文件
*
* @param path
* @return
*/
public Vector readConfigInfo(String path) {
String rpath = this.getClass().getResource("").getPath().substring(1) + path;
Vector dsConfig = null;
FileInputStream fi = null;
try {
fi = new FileInputStream(rpath);//读取路径文件
dsConfig = new Vector();
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(fi);
Element root = doc.getRootElement();
List pools = root.getChildren();
Element pool = null;
Iterator allPool = pools.iterator();
while (allPool.hasNext()) {
pool = (Element) allPool.next();
DSConfigBean dscBean = new DSConfigBean();
dscBean.setType(pool.getChild("type").getText());
dscBean.setName(pool.getChild("name").getText());
System.out.println(dscBean.getName());
dscBean.setDriver(pool.getChild("driver").getText());
dscBean.setUrl(pool.getChild("url").getText());
dscBean.setUsername(pool.getChild("username").getText());
dscBean.setPassword(pool.getChild("password").getText());
dscBean.setMaxconn(Integer.parseInt(pool.getChild("maxconn").getText()));
dsConfig.add(dscBean);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
fi.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return dsConfig;
}
/**
* 修改配置文件 没时间写 过段时间再贴上去 其实一样的
*/
public void modifyConfigInfo(String path, DSConfigBean dsb) throws Exception {
String rpath = this.getClass().getResource("").getPath().substring(1) + path;
FileInputStream fi = null; //读出
FileOutputStream fo = null; //写入
}
/**
* 增加配置文件
*/
public void addConfigInfo(String path, DSConfigBean dsb) {
String rpath = this.getClass().getResource("").getPath().substring(1) + path;
FileInputStream fi = null;
FileOutputStream fo = null;
try {
fi = new FileInputStream(rpath);//读取xml流
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(fi); //得到xml
Element root = doc.getRootElement();
List pools = root.getChildren();//得到xml子树
Element newpool = new Element("pool"); //创建新连接池
Element pooltype = new Element("type"); //设置连接池类型
pooltype.setText(dsb.getType());
newpool.addContent(pooltype);
Element poolname = new Element("name");//设置连接池名字
poolname.setText(dsb.getName());
newpool.addContent(poolname);
Element pooldriver = new Element("driver"); //设置连接池驱动
pooldriver.addContent(dsb.getDriver());
newpool.addContent(pooldriver);
Element poolurl = new Element("url");//设置连接池url
poolurl.setText(dsb.getUrl());
newpool.addContent(poolurl);
Element poolusername = new Element("username");//设置连接池用户名
poolusername.setText(dsb.getUsername());
newpool.addContent(poolusername);
Element poolpassword = new Element("password");//设置连接池密码
poolpassword.setText(dsb.getPassword());
newpool.addContent(poolpassword);
Element poolmaxconn = new Element("maxconn");//设置连接池最大连接
poolmaxconn.setText(String.valueOf(dsb.getMaxconn()));
newpool.addContent(poolmaxconn);
pools.add(newpool);//将child添加到root
Format format = Format.getPrettyFormat();
format.setIndent("");
format.setEncoding("utf-8");
XMLOutputter outp = new XMLOutputter(format);
fo = new FileOutputStream(rpath);
outp.output(doc, fo);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
}
}
/**
* 删除配置文件
*/
public void delConfigInfo(String path, String name) {
String rpath = this.getClass().getResource("").getPath().substring(1) + path;
FileInputStream fi = null;
FileOutputStream fo = null;
try {
fi = new FileInputStream(rpath);//读取路径文件
SAXBuilder sb = new SAXBuilder();
Document doc = sb.build(fi);
Element root = doc.getRootElement();
List pools = root.getChildren();
Element pool = null;
Iterator allPool = pools.iterator();
while (allPool.hasNext()) {
pool = (Element) allPool.next();
if (pool.getChild("name").getText().equals(name)) {
pools.remove(pool);
break;
}
}
Format format = Format.getPrettyFormat();
format.setIndent("");
format.setEncoding("utf-8");
XMLOutputter outp = new XMLOutputter(format);
fo = new FileOutputStream(rpath);
outp.output(doc, fo);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (JDOMException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
fi.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
ParseDSConfig pd = new ParseDSConfig();
String path = "ds.config.xml";
pd.readConfigInfo(path);
//pd.delConfigInfo(path, "tj012006");
DSConfigBean dsb = new DSConfigBean();
dsb.setType("oracle");
dsb.setName("yyy004");
dsb.setDriver("org.oracle.jdbc");
dsb.setUrl("jdbc:oracle://localhost");
dsb.setUsername("sa");
dsb.setPassword("");
dsb.setMaxconn(1000);
pd.addConfigInfo(path, dsb);
pd.delConfigInfo(path, "yyy001");
}
}
ds.config.xml
<ds-config>
<pool>
<type>mysql</type>
<name>user</name>
<driver>com.mysql.jdbc.driver</driver>
<url>jdbc:mysql://localhost:3306/user</url>
<username>sa</username>
<password>123456</password>
<maxconn>100</maxconn>
</pool>
<pool>
<type>mysql</type>
<name>user2</name>
<driver>com.mysql.jdbc.driver</driver>
<url>jdbc:mysql://localhost:3306/user2</url>
<username>sa</username>
<password>1234</password>
<maxconn>10</maxconn>
</pool>
<pool>
<type>sql2000</type>
<name>books</name>
<driver>com.microsoft.sqlserver.driver</driver>
<url>jdbc:sqlserver://localhost:1433/books:databasename=books</url>
<username>sa</username>
<password></password>
<maxconn>100</maxconn>
</pool>
</ds-config>
连接池的使用
1. Connection 的获得与释放
Test.java
package com.tony.db;
import java.sql.Connection;
/**
* Created with IntelliJ IDEA.
* User: Tony.Wang
* Date: 12-9-24
* Time: 上午9:34
* To change this template use File | Settings | File Templates.
*/
public class Test {
public static void main(String[] args) {
DBConnectionManager connectionManager = DBConnectionManager.getInstance();
String name = "mysql";//从上下文得到你要访问的数据库的名字
Connection connection = connectionManager.getConnection(name);
//使用
//使用结束
connectionManager.freeConnection(name, connection);//释放,但并未断开连接
}
}
2。数据库连接的动态增加和连接池的动态增加
1。调用xml操作增加类
2。重新实例华连接池管理池类