- 浏览: 53249 次
- 性别:
- 来自: 上海
最新评论
-
damoo:
谢谢你,我也遇到同样的问题,看了你的解决了。
mappedBy的问题 -
windFeng:
非常的感谢lz,文档图文并茂,写的很详细。。。
分享下我的代码生成工具(上传了文档) -
nishijia:
zhaoyta 写道能做企业级开发吗,几百个war的
几百个 ...
分享下我的代码生成工具(上传了文档) -
nishijia:
<div class="quote_title ...
分享下我的代码生成工具(上传了文档) -
zhaoyta:
能做企业级开发吗,几百个war的
分享下我的代码生成工具(上传了文档)
自己写了个连接池,不过性能不理想,当模拟1000个线程去连接数据库时,会在getConnection这个方法里爆出空指针。
oracle的配置文件:
driver = oracle.jdbc.driver.OracleDriver url = jdbc:oracle:thin:@localhost:1521:DB user = username password = pwd defaultRowPrefetch=50 defaultExecuteBatch=25 poolsize=20 islation=2
连接池:
package com.connection; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.util.HashMap; import java.util.Map; import java.util.Properties; import com.util.PropertiesHelper; import oracle.jdbc.driver.OracleConnection; public class ConnectionPool { private static Map<Connection, Boolean> pool = new HashMap<Connection, Boolean>(); public static DataBase db=null; private static Properties prop; private static int POOLSIZE = 5; public static synchronized void init(String file){ if (pool.size() == 0) { try { prop = readProperties(file); } catch (IOException e) { e.printStackTrace(); } try { Class.forName(prop.getProperty("driver")); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } if (prop.getProperty("poolsize").length() > 0) POOLSIZE = Integer.parseInt(prop.getProperty("poolsize")); for (int i = 0; i < POOLSIZE; i++) { pool.put(newConnection(), true); } } } private static synchronized Connection newConnection() { Connection conn=null; try { String url=prop.getProperty("url"); conn = DriverManager.getConnection(url, prop); conn.setTransactionIsolation(Integer.parseInt(prop.getProperty("islation"))); conn.setAutoCommit(false); } catch (SQLException e) { e.printStackTrace(); } return conn; } public static void closeAll() { for (Connection c : pool.keySet()) { try { c.close(); } catch (SQLException e) { e.printStackTrace(); } } } public static Connection getConnection() { synchronized (pool) { for (Connection conn : pool.keySet()) { if (pool.get(conn) == false) { pool.put(conn, true); return conn; } } } Connection c=newConnection(); return c; } public static void release(Connection conn) { if (conn != null) { synchronized (pool) { if (!pool.keySet().contains(conn)) { try { conn.close(); } catch (SQLException e) { throw new RuntimeException(e); } } else pool.put(conn, false); } } } public static void closePool() { for (Connection conn : pool.keySet()) { try { conn.close(); pool.clear(); } catch (SQLException e) { e.printStackTrace(); } } } private static Properties readProperties(String file) throws IOException { Properties result = new Properties(); InputStream is = ConnectionPool.class.getClassLoader() .getResourceAsStream(file); result.load(is); return result; } }
测试代码:
package com.test; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import com.connection.ConnectionPool; import com.dao.BaseDAO; public class Test extends Thread{ private String sql; private Connection c; public Test(Connection c,String sql){ this.c=c; this.sql=sql; } public static void main(String[] args) { ConnectionPool.init("oracle.properties"); for(int i=0;i<100;i++){ String sql="select sysdate from dual"; new Test(ConnectionPool.getConnection(),sql).start(); } } public void run(){ try { System.out.println(c); ResultSet rs=c.createStatement().executeQuery(sql); while(rs.next()){ System.out.println(rs.getString(1)); } } catch (SQLException e) { e.printStackTrace(); }finally{ ConnectionPool.release(c); } } }
模拟100个线程完全没问题,但是数量一旦上去了就不行了 连接池得不到连接了。
我的思路是当你请求一个资源,池会看看池中有没有资源,有的话把这个资源分发给请求,没有的话,生成新的资源同时把该资源放到池中。当请求归还的时候,池会判断池中是否满了,如果满了,那么释放该资源。其中关键的,就是生成新资源,判断是否释放资源。
我自己写是为了多学点知识 一直用别人封装好的连接池 没什么意思,个人写的很烂,欢迎有技术性的建议。
评论
10 楼
nishijia
2009-11-23
maomiandyou 写道
lz
你的代码
我测试3000都没问题
??
你改过了?
你的代码
我测试3000都没问题
??
你改过了?
我的代码在oracle 用户多了就有问题 MYSQL 没试过
9 楼
maomiandyou
2009-11-16
lz
你的代码
我测试3000都没问题
??
你改过了?
你的代码
我测试3000都没问题
??
你改过了?
8 楼
mathgl
2009-08-21
用 blockingqueue 放conn不错。。
7 楼
猪也有理想
2009-08-20
<div class="quote_title">zxmsdyz 写道</div>
<div class="quote_div">
<div class="quote_title">unsid 写道</div>
<div class="quote_div">两个建议:<br>1、getConnection中,如果池内没有连接则新建一个,新建的又不归池管,那连接池还有什么意义?应该判断如果池内连接全部占用则判断池连接数是否达到最大,如果最大了则阻塞wait,直到有人释放了连接才notify;如果没到,则新建一个连接放在池内并返回<br><br>2、release用来释放连接非常不好,因为很多习惯conn.close()写,导致了连接池中连接收不回来,应该创建连接的时候用Proxy重写Connection的close方法,在其中将true改成false</div>
<br><br>第二个明白了,第一条怎么做的呢,能否详细讲讲</div>
<p> </p>
<p> </p>
<p>unsid的意思是你做连接池的时候建立一个新的连接。然后把该连接存放起来,下次在连接的时候先判断是否有空闲的连接。如果有直接拿来用</p>
<p>如果没有。在判断是否超过了最大连接数,如果没超过。则重新创建一个新的连接,如超过。则等待</p>
<p> </p>
<div class="quote_div">
<div class="quote_title">unsid 写道</div>
<div class="quote_div">两个建议:<br>1、getConnection中,如果池内没有连接则新建一个,新建的又不归池管,那连接池还有什么意义?应该判断如果池内连接全部占用则判断池连接数是否达到最大,如果最大了则阻塞wait,直到有人释放了连接才notify;如果没到,则新建一个连接放在池内并返回<br><br>2、release用来释放连接非常不好,因为很多习惯conn.close()写,导致了连接池中连接收不回来,应该创建连接的时候用Proxy重写Connection的close方法,在其中将true改成false</div>
<br><br>第二个明白了,第一条怎么做的呢,能否详细讲讲</div>
<p> </p>
<p> </p>
<p>unsid的意思是你做连接池的时候建立一个新的连接。然后把该连接存放起来,下次在连接的时候先判断是否有空闲的连接。如果有直接拿来用</p>
<p>如果没有。在判断是否超过了最大连接数,如果没超过。则重新创建一个新的连接,如超过。则等待</p>
<p> </p>
6 楼
猪也有理想
2009-08-20
<p>MYsql做DEMO演示下给你</p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>首先在SRC下面建立一个db.properties文件.,内容如下:</p>
<p> </p>
<pre name="code" class="java">drivers=com.mysql.jdbc.Driver
logfile=D:\\log.txt
mysql.url=jdbc:mysql://127.0.0.1:3306/blog?characterEncoding=gb2312
mysql.maxconn=1
mysql.user=root
mysql.password=root</pre>
<p><span style="color: #0000ff;">drivers 以空格分隔的JDBC驱动程序类列表<br>logfile 日志文件的绝对路径<br><br>其它的属性和特定连接池相关,其属性名字前应加上连接池名字:<br><br><poolname>.url 数据库的 JDBC URL <br><poolname>.maxconn 允许建立的最大连接数,0表示没有限制 <br><poolname>.user 用于该连接池的数据库帐号<br><poolname>.password 相应的密码</span></p>
<p> </p>
<p> </p>
<p>其中url属性是必需的,而其它属性则是可选的。数据库帐号和密码必须合法。</p>
<p> </p>
<p> </p>
<p>获得连接处理代码:</p>
<p> </p>
<pre name="code" class="java">public class DBManager {
private Connection conn ;
private String poolURL = "";
private String driverName ="";
private String user="" ;
private String pass="";
static private DBManager instance; // 唯一实例
static private int clients;
private Vector drivers = new Vector();
private PrintWriter log;
private Hashtable pools = new Hashtable();
/**
* 返回唯一实例.如果是第一次调用此方法,则创建实例
*
* @return DBConnectionManager 唯一实例
*/
static synchronized public DBManager getInstance() {
if (instance == null) {
instance = new DBManager();
}
clients++;
return instance;
}
/**
* 建构函数私有以防止其它对象创建本类实例
*/
private DBManager() {
init();
}
/**
* 将连接对象返回给由名字指定的连接池
*
* @param name 在属性文件中定义的连接池名字
* @param con 连接对象
*/
public void freeConnection(String name, Connection con) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
pool.freeConnection(con);
}
}
/**
* 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数
* 限制,则创建并返回新连接
*
* @param name 在属性文件中定义的连接池名字
* @return Connection 可用连接或null
*/
public Connection getConnection(String name) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
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;
}
public int getClient(){
return clients;
}
/**
* 关闭所有连接,撤销驱动程序的注册
*/
public synchronized void release() {
// 等待直到最后一个客户程序调用
if (--clients != 0) {
return;
}
Enumeration allPools = pools.elements();
while (allPools.hasMoreElements()) {
DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();
pool.release();
}
Enumeration allDrivers = drivers.elements();
while (allDrivers.hasMoreElements()) {
Driver driver = (Driver) allDrivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
log("撤销JDBC驱动程序 " + driver.getClass().getName()+"的注册");
}
catch (SQLException e) {
log(e, "无法撤销下列JDBC驱动程序的注册: " + driver.getClass().getName());
}
}
}
/**
* 根据指定属性创建连接池实例.
*
* @param props 连接池属性
*/
private void createPools(Properties props) {
Enumeration propNames = props.propertyNames();
while (propNames.hasMoreElements()) {
String name = (String) propNames.nextElement();
if (name.endsWith(".url")) {
String poolName = name.substring(0, name.lastIndexOf("."));
String url = props.getProperty(poolName + ".url");
if (url == null) {
log("没有为连接池" + poolName + "指定URL");
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) {
log("错误的最大连接数限制: " + maxconn + " .连接池: " + poolName);
max = 0;
}
DBConnectionPool pool = new DBConnectionPool(poolName, url, user, password, max);
pools.put(poolName, pool);
log("成功创建连接池" + poolName);
}
}
}
/**
* 读取属性完成初始化
*/
private void init() {
InputStream is = getClass().getResourceAsStream("/db.properties");
Properties dbProps = new Properties();
try {
dbProps.load(is);
System.out.println("读取数据成功!");
}
catch (Exception e) {
System.err.println("不能读取属性文件. " +"请确保db.properties在CLASSPATH指定的路径中");
return;
}
String logFile = dbProps.getProperty("logfile", "DBManager.log");
System.out.println("日志文件存放位置:"+logFile);
try {
log = new PrintWriter(new FileWriter(logFile, true), true);
}catch (IOException e) {
System.err.println("无法打开日志文件: " + logFile);
log = new PrintWriter(System.err);
}
loadDrivers(dbProps);
createPools(dbProps);
}
/**
* 装载和注册所有JDBC驱动程序
*
* @param props 属性
*/
private void loadDrivers(Properties props) {
String driverClasses = props.getProperty("drivers");
StringTokenizer st = new StringTokenizer(driverClasses);
while (st.hasMoreElements()) {
String driverClassName = st.nextToken().trim();
try {
Driver driver = (Driver)Class.forName(driverClassName).newInstance();
DriverManager.registerDriver(driver);
drivers.addElement(driver);
log("成功注册JDBC驱动程序" + driverClassName);
}
catch (Exception e) {
log("无法注册JDBC驱动程序: " + driverClassName + ", 错误: " + e);
}
}
}
/**
* 将文本信息写入日志文件
*/
private void log(String msg) {
log.println(new Date().toLocaleString() + ": " + msg);
}
/**
* 将文本信息与异常写入日志文件
*/
private void log(Throwable e, String msg) {
log.println(new Date() + ": " + msg);
e.printStackTrace(log);
}
/**
* 此内部类定义了一个连接池.它能够根据要求创建新连接,直到预定的最
* 大连接数为止.在返回连接给客户程序之前,它能够验证连接的有效性.
*/
class DBConnectionPool {
private int checkedOut;
private Vector freeConnections = new Vector();
private int maxConn;
private String name;
private String password;
private String URL;
private String user;
/**
* 创建新的连接池
*
* @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;
}
/**
* 将不再使用的连接返回给连接池
*
* @param con 客户程序释放的连接
*/
public synchronized void freeConnection(Connection con) {
// 将指定连接加入到向量末尾
freeConnections.addElement(con);
checkedOut--;
notifyAll();
}
/**
* 从连接池获得一个可用连接.如没有空闲的连接且当前连接数小于最大连接
* 数限制,则创建新连接.如原来登记为可用的连接不再有效,则从向量删除之,
* 然后递归调用自己以尝试新的可用连接.
*/
public synchronized Connection getConnection() {
Connection con = null;
if (freeConnections.size() > 0) {
// 获取向量中第一个可用连接
con = (Connection) freeConnections.firstElement();
freeConnections.removeElementAt(0);
if (con.isClosed()) {
log("从连接池" + name+"删除一个无效连接");
// 递归调用自己,尝试再次获取可用连接
con = getConnection();
}
}
else if (maxConn == 0 || checkedOut < maxConn) {
con = newConnection();
}
if (con != null) {
checkedOut++;
}
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()返回的原因是超时
return null;
}
}
return con;
}
/**
* 关闭所有连接
*/
public synchronized void release() {
Enumeration allConnections = freeConnections.elements();
while (allConnections.hasMoreElements()) {
Connection con = (Connection) allConnections.nextElement();
try {
con.close();
log("关闭连接池" + name+"中的一个连接");
}
catch (SQLException e) {
log(e, "无法关闭连接池" + name+"中的连接");
}
}
freeConnections.removeAllElements();
}
/**
* 创建新的连接
*/
private Connection newConnection() {
Connection con = null;
try {
if (user == null) {
con = (Connection) DriverManager.getConnection(URL);
}
else {
con = (Connection) DriverManager.getConnection(URL, user, password);
}
log("连接池" + name+"创建一个新的连接");
}
catch (SQLException e) {
log(e, "无法创建下列URL的连接: " + URL);
return null;
}
return con;
}
}
}
</pre>
<p><span style="color: #ff0000;">init()方法在创建属性对象并读取db.properties文件之后,就开始检查logfile属性。如果属性文件中没有指定日志文件,则默认为当前目录下的DBConnectionManager.log文件。如日志文件无法使用,则向System.err输出日志记录。</span></p>
<p><span style="color: #ff0000;"><br>装载和注册所有在drivers属性中指定的JDBC驱动程序由loadDrivers()方法实现。该方法先用StringTokenizer将drivers属性值分割为对应于驱动程序名称的字符串,然后依次装载这些类并创建其实例,最后在 DriverManager中注册</span></p>
<p><span style="color: #ff0000;"><br>该实例并把它加入到一个私有的向量drivers。向量drivers将用于关闭服务时从DriverManager取消所有JDBC 驱动程序的注册。</span></p>
<p><span style="color: #ff0000;"><br>init()方法的最后一个任务是调用私有方法createPools()创建连接池对象。createPools()方法先创建所有属性名字的枚举对象(即Enumeration对象,该对象可以想象为一个元素系列,逐次调用其nextElement()方法将顺序返回各元素),然后在其中搜索名字以“.url”结尾的属性。对于每一个符合条件的属性,先提取其连接池名字部分,进而读取所有属于该连接池的属性,最后创建连接池对象并把它保存在实例变量pools中。散列表(Hashtable类 )pools实现连接池名字到连接池对象之间的映射,此处以连接池名字为键,连接池对象为值。</span></p>
<p><span style="color: #ff0000;"><br>为便于客户程序从指定连接池获得可用连接或将连接返回给连接池,类DBConnectionManager提供了方法getConnection()和freeConnection()。所有这些方法都要求在参数中指定连接池名字,具体的连接获取或返回操作则调用对应的连接池对象完成。</span></p>
<p><span style="color: #ff0000;"><br>为实现连接池的安全关闭,DBConnectionManager提供了方法release()。在上面我们已经提到,所有DBConnectionManager的客户程序都应该调用静态方法getInstance()以获得该管理器的引用,此调用将增加客户程序计数。</span></p>
<p><span style="color: #ff0000;"><br>客户程序在关闭时调用release()可以递减该计数。当最后一个客户程序调用release(),递减后的引用计数为0,就可以调用各个连接池的release()方法关闭所有连接了。管理类release()方法最后的任务是撤销所有JDBC驱动程序的注册。</span></p>
<p> </p>
<p> </p>
<p> </p>
<p>Servlet使用连接池示例</p>
<p> </p>
<p> </p>
<p> </p>
<p>Servlet API所定义的Servlet生命周期类如:<br><br>1) 创建并初始化Servlet(init()方法)。<br>2) 响应客户程序的服务请求(service()方法)。<br>3) Servlet终止运行,释放所有资源(destroy()方法)。<br><br>本例演示连接池应用,上述关键步骤中的相关操作为:<br><br>1) 在init(),用实例变量connMgr 保存调用DBManager.getInstance()所返回的引用。<br>2) 在service(),调用getConnection(),执行数据库操作,用freeConnection()将连接返回给连接池。<br>3) 在destroy(),调用release()关闭所有连接,释放所有资源。</p>
<p> </p>
<p> </p>
<p> </p>
<p>示例程序清单如下:</p>
<p> </p>
<p> </p>
<pre name="code" class="java">import java.io.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class TestServlet extends HttpServlet {
private DBManager connMgr;
public void init(ServletConfig conf) throws ServletException {
super.init(conf);
connMgr = DBManager.getInstance();
}
public void service(HttpServletRequest req, HttpServletResponse res)
throws IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
Connection con = connMgr.getConnection("idb");
if (con == null) {
out.println("不能获取数据库连接.");
return;
}
ResultSet rs = null;
ResultSetMetaData md = null;
Statement stmt = null;
try {
stmt = con.createStatement();
rs = stmt.executeQuery("SELECT * FROM EMPLOYEE");
md = rs.getMetaData();
out.println("<H1>职工数据</H1>");
while (rs.next()) {
out.println("<BR>");
for (int i = 1; i < md.getColumnCount(); i++) {
out.print(rs.getString(i) + ", ");
}
}
stmt.close();
rs.close();
}
catch (SQLException e) {
e.printStackTrace(out);
}
connMgr.freeConnection("idb", con);
}
public void destroy() {
connMgr.release();
super.destroy();
}
}</pre>
<p> </p>
<p> </p>
<p>测试下吧O(∩_∩)O</p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p> </p>
<p>首先在SRC下面建立一个db.properties文件.,内容如下:</p>
<p> </p>
<pre name="code" class="java">drivers=com.mysql.jdbc.Driver
logfile=D:\\log.txt
mysql.url=jdbc:mysql://127.0.0.1:3306/blog?characterEncoding=gb2312
mysql.maxconn=1
mysql.user=root
mysql.password=root</pre>
<p><span style="color: #0000ff;">drivers 以空格分隔的JDBC驱动程序类列表<br>logfile 日志文件的绝对路径<br><br>其它的属性和特定连接池相关,其属性名字前应加上连接池名字:<br><br><poolname>.url 数据库的 JDBC URL <br><poolname>.maxconn 允许建立的最大连接数,0表示没有限制 <br><poolname>.user 用于该连接池的数据库帐号<br><poolname>.password 相应的密码</span></p>
<p> </p>
<p> </p>
<p>其中url属性是必需的,而其它属性则是可选的。数据库帐号和密码必须合法。</p>
<p> </p>
<p> </p>
<p>获得连接处理代码:</p>
<p> </p>
<pre name="code" class="java">public class DBManager {
private Connection conn ;
private String poolURL = "";
private String driverName ="";
private String user="" ;
private String pass="";
static private DBManager instance; // 唯一实例
static private int clients;
private Vector drivers = new Vector();
private PrintWriter log;
private Hashtable pools = new Hashtable();
/**
* 返回唯一实例.如果是第一次调用此方法,则创建实例
*
* @return DBConnectionManager 唯一实例
*/
static synchronized public DBManager getInstance() {
if (instance == null) {
instance = new DBManager();
}
clients++;
return instance;
}
/**
* 建构函数私有以防止其它对象创建本类实例
*/
private DBManager() {
init();
}
/**
* 将连接对象返回给由名字指定的连接池
*
* @param name 在属性文件中定义的连接池名字
* @param con 连接对象
*/
public void freeConnection(String name, Connection con) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
pool.freeConnection(con);
}
}
/**
* 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数
* 限制,则创建并返回新连接
*
* @param name 在属性文件中定义的连接池名字
* @return Connection 可用连接或null
*/
public Connection getConnection(String name) {
DBConnectionPool pool = (DBConnectionPool) pools.get(name);
if (pool != null) {
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;
}
public int getClient(){
return clients;
}
/**
* 关闭所有连接,撤销驱动程序的注册
*/
public synchronized void release() {
// 等待直到最后一个客户程序调用
if (--clients != 0) {
return;
}
Enumeration allPools = pools.elements();
while (allPools.hasMoreElements()) {
DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();
pool.release();
}
Enumeration allDrivers = drivers.elements();
while (allDrivers.hasMoreElements()) {
Driver driver = (Driver) allDrivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
log("撤销JDBC驱动程序 " + driver.getClass().getName()+"的注册");
}
catch (SQLException e) {
log(e, "无法撤销下列JDBC驱动程序的注册: " + driver.getClass().getName());
}
}
}
/**
* 根据指定属性创建连接池实例.
*
* @param props 连接池属性
*/
private void createPools(Properties props) {
Enumeration propNames = props.propertyNames();
while (propNames.hasMoreElements()) {
String name = (String) propNames.nextElement();
if (name.endsWith(".url")) {
String poolName = name.substring(0, name.lastIndexOf("."));
String url = props.getProperty(poolName + ".url");
if (url == null) {
log("没有为连接池" + poolName + "指定URL");
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) {
log("错误的最大连接数限制: " + maxconn + " .连接池: " + poolName);
max = 0;
}
DBConnectionPool pool = new DBConnectionPool(poolName, url, user, password, max);
pools.put(poolName, pool);
log("成功创建连接池" + poolName);
}
}
}
/**
* 读取属性完成初始化
*/
private void init() {
InputStream is = getClass().getResourceAsStream("/db.properties");
Properties dbProps = new Properties();
try {
dbProps.load(is);
System.out.println("读取数据成功!");
}
catch (Exception e) {
System.err.println("不能读取属性文件. " +"请确保db.properties在CLASSPATH指定的路径中");
return;
}
String logFile = dbProps.getProperty("logfile", "DBManager.log");
System.out.println("日志文件存放位置:"+logFile);
try {
log = new PrintWriter(new FileWriter(logFile, true), true);
}catch (IOException e) {
System.err.println("无法打开日志文件: " + logFile);
log = new PrintWriter(System.err);
}
loadDrivers(dbProps);
createPools(dbProps);
}
/**
* 装载和注册所有JDBC驱动程序
*
* @param props 属性
*/
private void loadDrivers(Properties props) {
String driverClasses = props.getProperty("drivers");
StringTokenizer st = new StringTokenizer(driverClasses);
while (st.hasMoreElements()) {
String driverClassName = st.nextToken().trim();
try {
Driver driver = (Driver)Class.forName(driverClassName).newInstance();
DriverManager.registerDriver(driver);
drivers.addElement(driver);
log("成功注册JDBC驱动程序" + driverClassName);
}
catch (Exception e) {
log("无法注册JDBC驱动程序: " + driverClassName + ", 错误: " + e);
}
}
}
/**
* 将文本信息写入日志文件
*/
private void log(String msg) {
log.println(new Date().toLocaleString() + ": " + msg);
}
/**
* 将文本信息与异常写入日志文件
*/
private void log(Throwable e, String msg) {
log.println(new Date() + ": " + msg);
e.printStackTrace(log);
}
/**
* 此内部类定义了一个连接池.它能够根据要求创建新连接,直到预定的最
* 大连接数为止.在返回连接给客户程序之前,它能够验证连接的有效性.
*/
class DBConnectionPool {
private int checkedOut;
private Vector freeConnections = new Vector();
private int maxConn;
private String name;
private String password;
private String URL;
private String user;
/**
* 创建新的连接池
*
* @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;
}
/**
* 将不再使用的连接返回给连接池
*
* @param con 客户程序释放的连接
*/
public synchronized void freeConnection(Connection con) {
// 将指定连接加入到向量末尾
freeConnections.addElement(con);
checkedOut--;
notifyAll();
}
/**
* 从连接池获得一个可用连接.如没有空闲的连接且当前连接数小于最大连接
* 数限制,则创建新连接.如原来登记为可用的连接不再有效,则从向量删除之,
* 然后递归调用自己以尝试新的可用连接.
*/
public synchronized Connection getConnection() {
Connection con = null;
if (freeConnections.size() > 0) {
// 获取向量中第一个可用连接
con = (Connection) freeConnections.firstElement();
freeConnections.removeElementAt(0);
if (con.isClosed()) {
log("从连接池" + name+"删除一个无效连接");
// 递归调用自己,尝试再次获取可用连接
con = getConnection();
}
}
else if (maxConn == 0 || checkedOut < maxConn) {
con = newConnection();
}
if (con != null) {
checkedOut++;
}
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()返回的原因是超时
return null;
}
}
return con;
}
/**
* 关闭所有连接
*/
public synchronized void release() {
Enumeration allConnections = freeConnections.elements();
while (allConnections.hasMoreElements()) {
Connection con = (Connection) allConnections.nextElement();
try {
con.close();
log("关闭连接池" + name+"中的一个连接");
}
catch (SQLException e) {
log(e, "无法关闭连接池" + name+"中的连接");
}
}
freeConnections.removeAllElements();
}
/**
* 创建新的连接
*/
private Connection newConnection() {
Connection con = null;
try {
if (user == null) {
con = (Connection) DriverManager.getConnection(URL);
}
else {
con = (Connection) DriverManager.getConnection(URL, user, password);
}
log("连接池" + name+"创建一个新的连接");
}
catch (SQLException e) {
log(e, "无法创建下列URL的连接: " + URL);
return null;
}
return con;
}
}
}
</pre>
<p><span style="color: #ff0000;">init()方法在创建属性对象并读取db.properties文件之后,就开始检查logfile属性。如果属性文件中没有指定日志文件,则默认为当前目录下的DBConnectionManager.log文件。如日志文件无法使用,则向System.err输出日志记录。</span></p>
<p><span style="color: #ff0000;"><br>装载和注册所有在drivers属性中指定的JDBC驱动程序由loadDrivers()方法实现。该方法先用StringTokenizer将drivers属性值分割为对应于驱动程序名称的字符串,然后依次装载这些类并创建其实例,最后在 DriverManager中注册</span></p>
<p><span style="color: #ff0000;"><br>该实例并把它加入到一个私有的向量drivers。向量drivers将用于关闭服务时从DriverManager取消所有JDBC 驱动程序的注册。</span></p>
<p><span style="color: #ff0000;"><br>init()方法的最后一个任务是调用私有方法createPools()创建连接池对象。createPools()方法先创建所有属性名字的枚举对象(即Enumeration对象,该对象可以想象为一个元素系列,逐次调用其nextElement()方法将顺序返回各元素),然后在其中搜索名字以“.url”结尾的属性。对于每一个符合条件的属性,先提取其连接池名字部分,进而读取所有属于该连接池的属性,最后创建连接池对象并把它保存在实例变量pools中。散列表(Hashtable类 )pools实现连接池名字到连接池对象之间的映射,此处以连接池名字为键,连接池对象为值。</span></p>
<p><span style="color: #ff0000;"><br>为便于客户程序从指定连接池获得可用连接或将连接返回给连接池,类DBConnectionManager提供了方法getConnection()和freeConnection()。所有这些方法都要求在参数中指定连接池名字,具体的连接获取或返回操作则调用对应的连接池对象完成。</span></p>
<p><span style="color: #ff0000;"><br>为实现连接池的安全关闭,DBConnectionManager提供了方法release()。在上面我们已经提到,所有DBConnectionManager的客户程序都应该调用静态方法getInstance()以获得该管理器的引用,此调用将增加客户程序计数。</span></p>
<p><span style="color: #ff0000;"><br>客户程序在关闭时调用release()可以递减该计数。当最后一个客户程序调用release(),递减后的引用计数为0,就可以调用各个连接池的release()方法关闭所有连接了。管理类release()方法最后的任务是撤销所有JDBC驱动程序的注册。</span></p>
<p> </p>
<p> </p>
<p> </p>
<p>Servlet使用连接池示例</p>
<p> </p>
<p> </p>
<p> </p>
<p>Servlet API所定义的Servlet生命周期类如:<br><br>1) 创建并初始化Servlet(init()方法)。<br>2) 响应客户程序的服务请求(service()方法)。<br>3) Servlet终止运行,释放所有资源(destroy()方法)。<br><br>本例演示连接池应用,上述关键步骤中的相关操作为:<br><br>1) 在init(),用实例变量connMgr 保存调用DBManager.getInstance()所返回的引用。<br>2) 在service(),调用getConnection(),执行数据库操作,用freeConnection()将连接返回给连接池。<br>3) 在destroy(),调用release()关闭所有连接,释放所有资源。</p>
<p> </p>
<p> </p>
<p> </p>
<p>示例程序清单如下:</p>
<p> </p>
<p> </p>
<pre name="code" class="java">import java.io.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class TestServlet extends HttpServlet {
private DBManager connMgr;
public void init(ServletConfig conf) throws ServletException {
super.init(conf);
connMgr = DBManager.getInstance();
}
public void service(HttpServletRequest req, HttpServletResponse res)
throws IOException {
res.setContentType("text/html");
PrintWriter out = res.getWriter();
Connection con = connMgr.getConnection("idb");
if (con == null) {
out.println("不能获取数据库连接.");
return;
}
ResultSet rs = null;
ResultSetMetaData md = null;
Statement stmt = null;
try {
stmt = con.createStatement();
rs = stmt.executeQuery("SELECT * FROM EMPLOYEE");
md = rs.getMetaData();
out.println("<H1>职工数据</H1>");
while (rs.next()) {
out.println("<BR>");
for (int i = 1; i < md.getColumnCount(); i++) {
out.print(rs.getString(i) + ", ");
}
}
stmt.close();
rs.close();
}
catch (SQLException e) {
e.printStackTrace(out);
}
connMgr.freeConnection("idb", con);
}
public void destroy() {
connMgr.release();
super.destroy();
}
}</pre>
<p> </p>
<p> </p>
<p>测试下吧O(∩_∩)O</p>
5 楼
nishijia
2009-07-30
设计的不太好 考虑的太少
觉得用 LinkedList来代替Map 比较好 还是需要用线程
觉得用 LinkedList来代替Map 比较好 还是需要用线程
4 楼
srdrm
2009-07-29
自己实现连接池一般应该实现 DataSource 接口,以及用一个实现Connection接口的类, 来包装原始 Connection 实例.
针对你这个类, 说一点, 好像 Connection 的Map 处理有点问题, key是connection , value是Boolean, value 是表示连接是否正被使用吧, 好像处理上有点问题.
然后还有个, 如果 DriverManager.getConnection 产生了异常, 你这里隐藏了这个异常, 当然就返回null了, 这个异常应该抛出来, 方法声明为throws sqlexception. 你现在返回null实际上表示底层的驱动已无法支持这么多的Connection了, 除非你让申请线程等待有连接返回到池中,这样设计就又复杂了. 但不能返回一个null的Connection
针对你这个类, 说一点, 好像 Connection 的Map 处理有点问题, key是connection , value是Boolean, value 是表示连接是否正被使用吧, 好像处理上有点问题.
然后还有个, 如果 DriverManager.getConnection 产生了异常, 你这里隐藏了这个异常, 当然就返回null了, 这个异常应该抛出来, 方法声明为throws sqlexception. 你现在返回null实际上表示底层的驱动已无法支持这么多的Connection了, 除非你让申请线程等待有连接返回到池中,这样设计就又复杂了. 但不能返回一个null的Connection
3 楼
nishijia
2009-07-29
zxmsdyz 写道
unsid 写道
两个建议:
1、getConnection中,如果池内没有连接则新建一个,新建的又不归池管,那连接池还有什么意义?应该判断如果池内连接全部占用则判断池连接数是否达到最大,如果最大了则阻塞wait,直到有人释放了连接才notify;如果没到,则新建一个连接放在池内并返回
2、release用来释放连接非常不好,因为很多习惯conn.close()写,导致了连接池中连接收不回来,应该创建连接的时候用Proxy重写Connection的close方法,在其中将true改成false
1、getConnection中,如果池内没有连接则新建一个,新建的又不归池管,那连接池还有什么意义?应该判断如果池内连接全部占用则判断池连接数是否达到最大,如果最大了则阻塞wait,直到有人释放了连接才notify;如果没到,则新建一个连接放在池内并返回
2、release用来释放连接非常不好,因为很多习惯conn.close()写,导致了连接池中连接收不回来,应该创建连接的时候用Proxy重写Connection的close方法,在其中将true改成false
第二个明白了,第一条怎么做的呢,能否详细讲讲
private static class ConnectionHandler implements InvocationHandler { /** * 连接是否可用。 */ private boolean enabled; /** * 连接(Connection)对象。 */ private Connection conn; /** * 构造器。 * * @param conn * 连接(Connection)对象。 * */ public ConnectionHandler(Connection conn) { this.conn = conn; enabled = true; } /** * 代理方法,特别对于close方法进行了处理。 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 该连接管理者是否不可用? if (!enabled) // 抛出空指针异常 throw new NullPointerException(); // 是否调用了close方法? if (method.getName() == "close") { if (xx条件) { // 关闭连接。 conn.close(); } } // 正常调用连接的各种方法。 return method.invoke(conn, args); } }
是这样吗? conn的isClose属性是protected 没法直接调
2 楼
zxmsdyz
2009-07-29
unsid 写道
两个建议:
1、getConnection中,如果池内没有连接则新建一个,新建的又不归池管,那连接池还有什么意义?应该判断如果池内连接全部占用则判断池连接数是否达到最大,如果最大了则阻塞wait,直到有人释放了连接才notify;如果没到,则新建一个连接放在池内并返回
2、release用来释放连接非常不好,因为很多习惯conn.close()写,导致了连接池中连接收不回来,应该创建连接的时候用Proxy重写Connection的close方法,在其中将true改成false
1、getConnection中,如果池内没有连接则新建一个,新建的又不归池管,那连接池还有什么意义?应该判断如果池内连接全部占用则判断池连接数是否达到最大,如果最大了则阻塞wait,直到有人释放了连接才notify;如果没到,则新建一个连接放在池内并返回
2、release用来释放连接非常不好,因为很多习惯conn.close()写,导致了连接池中连接收不回来,应该创建连接的时候用Proxy重写Connection的close方法,在其中将true改成false
第二个明白了,第一条怎么做的呢,能否详细讲讲
1 楼
unsid
2009-07-29
两个建议:
1、getConnection中,如果池内没有连接则新建一个,新建的又不归池管,那连接池还有什么意义?应该判断如果池内连接全部占用则判断池连接数是否达到最大,如果最大了则阻塞wait,直到有人释放了连接才notify;如果没到,则新建一个连接放在池内并返回
2、release用来释放连接非常不好,因为很多习惯conn.close()写,导致了连接池中连接收不回来,应该创建连接的时候用Proxy重写Connection的close方法,在其中将true改成false
1、getConnection中,如果池内没有连接则新建一个,新建的又不归池管,那连接池还有什么意义?应该判断如果池内连接全部占用则判断池连接数是否达到最大,如果最大了则阻塞wait,直到有人释放了连接才notify;如果没到,则新建一个连接放在池内并返回
2、release用来释放连接非常不好,因为很多习惯conn.close()写,导致了连接池中连接收不回来,应该创建连接的时候用Proxy重写Connection的close方法,在其中将true改成false
相关推荐
自己写的java 连接池
在给定的标题“自己写的数据库连接池(java)”中,我们可以推测这是一个用户自定义的数据库连接池实现,可能是为了学习或实践目的。描述提到的“XML版读取属性文件”和“普通的”,暗示了两种不同的配置方式,一种是...
在IT行业中,数据库连接池是优化数据库访问性能和资源管理的重要技术。标题提到的“一个比较好手写连接池”指的是一个由个人或团队独立...开发者可以根据自己的需求定制连接池的特性和行为,从而更好地适应项目的需求。
这里,我们讨论的是一个自定义实现的简单连接池。 连接池的核心功能通常包括以下几点: 1. **初始化与配置**:连接池在启动时需要根据预先设定的参数(如最大连接数、最小连接数、超时时间等)进行初始化。这些...
数据库连接池是Java应用程序中非常重要的一个组件,它在提高应用性能、节省系统资源方面起着关键作用。本文将深入探讨如何使用Java手写数据库连接池,并基于maven进行项目构建。我们将分析四个核心类的功能,以及...
自定义连接池的基本思想是维护一个连接池,当应用需要连接时从池中获取,用完后归还。核心部分包括连接的创建、管理和回收,以及连接池的大小控制策略。实现自定义连接池需要考虑线程安全、连接有效性检测、异常处理...
本资料将详细介绍如何手写一个简单的连接池实现,以帮助理解连接池的工作原理。 在Java中,我们通常使用`java.sql.Connection`接口来代表数据库连接。一个连接池的实现主要包括以下几个核心功能: 1. **初始化配置...
JAVA学习中,自己学着写的一个连接池.
Java手写连接池是一个编程实践,它涉及到数据库管理和优化,主要目标是提高数据库访问的效率和资源利用率。在Java中,连接池是一个管理数据库连接的系统,它预先创建一定数量的数据库连接,当应用程序需要时可以立即...
数据库连接池是应用程序管理数据库连接的一种机制,它在多个并发请求之间共享数据库连接,从而减少了创建和销毁数据库连接的开销。在这个项目中,你使用Java实现了一个自定义的数据库连接池,应用了代理模式来优化...
数据库连接池是Java开发中非常重要的一个概念,它在处理大量并发访问数据库的应用场景中扮演着关键角色。本文将深入探讨数据库连接池的工作原理、重要性以及如何在Java中实现一个简单的连接池。 首先,数据库连接池...
DELPHI7编写的ADO连接池是数据库应用开发中的一个重要技术,它主要用于管理和优化数据库连接,提高系统性能并防止可能出现的死锁问题。在多用户同时访问SQL数据库时,如果没有有效的连接管理,可能会出现资源竞争,...
然而,如果你想自定义更复杂的连接池逻辑,例如根据需求动态调整连接池大小,那么就需要自己编写代码来实现。 描述中提到的“DBHelper2.0”可能是一个自定义的数据库操作类库,它封装了连接池的创建、管理和数据库...
Poolman是一个自定义实现的数据库连接池,通过管理和复用数据库连接,避免了频繁创建和销毁连接带来的开销。在这个项目中,你将找到Poolman 2.0.4版本的源码以及相关的示例代码,这将帮助你深入理解数据库连接池的...
* 数据库连接池特点: * 获取连接时不需要了解连接的名字,连接池内部维护连接的名字 * 支持多线程,保证获取到的连接一定是没有被其他线程正在使用 * 按需创建连接,可以创建多个连接,可以控制连接的数量 * 连接...
写ftp连接池的目的就是合理的利用资源,本文的目的是在初始的时候,创建10个Ftp连接,放到一个队列中去,当多个用户同时去下载ftp上的文件的时候,就会从队列中取,若当前的队列中存在着空闲的连接,就获取该ftp的...
这里提到的"自己写的数据库连接池"是一个自定义实现,提供了全面的功能和详细的方法,旨在帮助开发者理解和实践数据库连接池的工作原理。 数据库连接池的基本原理是预先创建并维护一定数量的数据库连接,当应用程序...
发现csdn上的连接池都是配设xml的,就手写了一份数据库连接池(java),连接sqlserver,里面一共两个java代码,Conn类包含了Connection和标志位,myconnection包含了数据库连接池的使用:获取连接,增加连接,释放...
GlassFish 是一个功能强大且流行的Java EE 应用服务器,它提供了强大的连接池机制,使得应用程序可以高效地访问数据库。下面,我们将介绍如何在 GlassFish 管理员界面中配置连接池,特别是使用 Oracle 数据库为例。 ...
本资源包含了一个名为`jdbcPool`的手写数据库连接池源码以及一个`jdbcPoolTest`的调用示例,支持Oracle和MySQL数据库。 首先,我们来看`jdbcPool`连接池的实现。在Java中,连接池通常由以下几个核心组件组成: 1. ...