aaahd 发表于 2017-1-10 11:37:52

用apache.commons.pool 实现Access数据库连接池

  博客开张,把自己的一些经验做点记录.
  朋友的在维护一个老的内部网站系统,数据库使用access,该系统访问量一多经常会报sql错误,提示客户端过多问题.
  查看了系统代码,发现这个系统的数据库连接代码每次都是重新创建的.

    public static Connection getConn() throws ClassNotFoundException,
SQLException {
Connection conn = null;
String driver = "jdbc:odbc:driver={microsoft access driver (*.mdb)};dbq=d:\\data.mdb";
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");   
conn = DriverManager.getConnection(driver);
return conn;
}
  这样的问题就很严重,首先就是无法控制并发的数据库操作,导致并发高于一定量后access数据库直接报错提示客户端过多。
  其次就是数据库操作效率低下,每次建立连接和释放连接的效率很低。
  解决这个问题需要对数据库连接上加入连接池,保证数据库连接安全可控,高效。
  由于是老系统要求改动的代码尽量少,google了一把也没找到可以直接使用access文件作为数据库连接池的代码,就自己动手写了一个。
  该连接池只要依赖apache.commons.pool 开源组件。
  首先实现一个数据库连接创建,销毁的工厂类DBConnectFactory该类实现了接口org.apache.commons.pool.PoolableObjectFactory

package base;
import java.sql.Connection;
import java.sql.DriverManager;
import org.apache.commons.pool.PoolableObjectFactory;
/**
* 数据库连接工厂类
*
* @author binda.mabd
* @version 1.0
* @date 2009-4-3
*/
public class DBConnectFactory implements PoolableObjectFactory {
private int count =0 ;
private String driver ;
private String className;
public DBConnectFactory(String className,String driver){
this.className = className;
this.driver = driver;
}
/**
* 对象初始化后加入池的时候使用,设置对象为可用状态。
*/
public void activateObject(Object arg0) throws Exception {
//直接加入
}
/**
* 对象销毁方法
*/
public void destroyObject(Object arg0) throws Exception {
System.err.println("destroyObject Object " + arg0.getClass().getName());
if(arg0!=null){
Connection con = (Connection)arg0;
try{
con.close();
count --;
}catch(Exception e){
e.printStackTrace();
}
}
}
/**
* 对象创建方法
*/
public Object makeObject() throws Exception {
try {
Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
count ++;
returnDriverManager.getConnection(driver);
}
/**
* 非创建对象回到池的时使用,设置对象为可用.
*/
public void passivateObject(Object arg0) throws Exception {
Connection con = (Connection)arg0;
//如果非自动commit类型连接,强制comit后返回连接池
if(!con.getAutoCommit()){
con.commit();
}
}
/**
* 状态检查对象是否可用
*/
public boolean validateObject(Object arg0) {
try{
Connection con = (Connection)arg0;
return con!=null&&!con.isClosed();
}catch(Exception e){
return false ;
}
}
}

  有了DBConnectFactory工厂类,就可以通过org.apache.commons.pool.impl.GenericObijectPool 来创建一个自定义的连接池.

package base;
import java.sql.Connection;
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
public class SimpleAccessPool {
GenericObjectPool pool= null;
PoolableObjectFactory factory = null;
public SimpleAccessPool (String accessFilePath){
String className = "sun.jdbc.odbc.JdbcOdbcDriver";
String driver = "jdbc:odbc:driver={microsoft access driver (*.mdb)};dbq=";
driver = driver + accessFilePath;
factory = new DBConnectFactory(className,driver);
}
public void init(){
pool = new GenericObjectPool(factory);
//最大连接数
pool.setMaxActive(30);
//最大空闲连接数
pool.setMaxIdle(20);
//连接等待1分钟
pool.setMaxWait(60000);
}
    /** 获得连接*/
public Connection getConnection() {
try {
return(Connection) pool.borrowObject();
}catch (Exception e){
e.printStackTrace();
throw new RuntimeException("get connect error",e);
}
}
/**释放连接*/
public void relaseConnection(Connection con){
try {
pool.returnObject(con);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("relase connect error",e);
}
}
}

  写了一个测试类Main,并发1000个数据库查询请求

package base;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


public class DBTest {
static SimpleAccessPool connPoll = null ;
public static void main(String[] args) {
Stringcurrent_mdb_path = "D:\\data.mdb";
connPoll = new SimpleAccessPool(current_mdb_path);
connPoll.init();
for(int i=0;i<1000;i++){
Thread run = new Thread(new TestRun(i+""));
run.start();
}
}
}
/**
*并发测试考虑实现runable
* */
class TestRun implements Runnable{
private String id ;
public TestRun(String id){
this.id = id;
}
public void run() {
Connection conn =DBTest.connPoll.getConnection();
long t = System.currentTimeMillis();
PreparedStatement pstmt = null;
ResultSet rs= null ;
String sql_delele = "select * from news where news_id <?";
try {
pstmt = conn.prepareStatement(sql_delele);
pstmt.setInt(1,100);
rs = pstmt.executeQuery();
} catch (SQLException ex) {
ex.printStackTrace();
}
try{
rs.close();
pstmt.close();
}catch (SQLException ex) {
ex.printStackTrace();
}
DBTest.connPoll.relaseConnection(conn);
System.out.println("ok!cost time ms:"+(System.currentTimeMillis()-t));
}
}
  好了,一个简单的数据库连接池ok了.
  遇到的疑问:
  数据库连接Connection重复使用是否会有什么状态上的问题, 在回收的池的时候有什么方法把连接的状态初始化.我代码中做了一个检查,回收Connection时如果非自动commit连接则强制commit 避免重复使用connection事务回滚错误.
  开源的连接池框架没有研究过,是否能支持直接读取Access数据库文件方式获取数据源呢.
页: [1]
查看完整版本: 用apache.commons.pool 实现Access数据库连接池