论坛首页 Java企业应用论坛

写了一个简单的数据库连接池,请帮忙看一下

浏览 16251 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2011-03-12  
我是一个新手,第一次发帖,以前都是看别人的帖子。
我写了一个简单的数据库连接池实现,感觉有很多欠缺的地方,希望各位指点一下。十分感谢。
有四个类:
(1)连接池配置对象 Config
(2)连接池主类 ConnectionPool
(3)Properties读取类  PropertyReader
(4)异常类 DBPoolException


ConnectionPool.java
  public class ConnectionPool {
	
	private static Logger logger = Logger.getLogger(ConnectionPool.class);
	//当前已用连接数
	private static volatile int curConnections = 0;
	
	private static Config config = null;
	
	//初始化成功标志
	private static boolean initFlag = false;
	
	private static volatile Stack<Connection> conns;
	static {
		PropertyReader pr = null;

		try {
			pr = new PropertyReader("pool-config.properties");
			config = new Config();
			//设置数据库驱动
			config.setDriver(pr.getValue("driver"));
			//url
			config.setUrl(pr.getValue("url"));
			//uername
			config.setUsername(pr.getValue("username"));
			//password
			config.setPassword(pr.getValue("password"));
			//最大连接数
			if(pr.getValue("maxConnections") != null){
				config.setMaxConnections(Integer.valueOf(pr.getValue("maxConnections")));
			}
			//初始化时最小连接数
			if(pr.getValue("minConnections") != null){
				config.setMinConnections(Integer.valueOf(pr.getValue("minConnections")));
			}
			//返还连接时是否提交
			if(pr.getValue("autoCommitOnClose") != null){
				config.setAutoCommitOnClose(Boolean.valueOf(pr.getValue("autoCommitOnClose")));
			}
			//当连接池用完时客户端调用getConn()后等待获取新连接的时间 
			//Default: (100毫秒)
			if(pr.getValue("checkoutTimeout") != null){
				config.setCheckoutTimeout(Integer.valueOf(pr.getValue("checkoutTimeout")));
			}
			//当没有可用链接时,尝试获取链接的次数
			if(pr.getValue("checkTimes") != null){
				config.setCheckTimes(Integer.valueOf(pr.getValue("checkTimes")));
			}
			initPool();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}
	/**
	 * 隐藏构造函数
	 */
	private ConnectionPool(){
		
	}
	
	/**
	 * 初始化连接池 保存minConnections个链接
	 * @throws SQLException
	 * @throws ClassNotFoundException 
	 */
	private static synchronized void initPool() throws SQLException, ClassNotFoundException{
		conns = new Stack<Connection>();
		Class.forName(config.getDriver());
		for(int i = 0 ; i < config.getMinConnections() ; i++){
			Connection conn = newConnection();
			conns.push(conn);
		}
		initFlag = true;
	}
	/**
	 * 获取一个可用链接
	 * @return
	 * @throws SQLException 
	 * @throws InterruptedException 
	 * @throws DBPoolException 
	 * @throws Exception
	 */
	public static Connection getConn() throws SQLException, InterruptedException, DBPoolException  {
		Connection conn = null;
		if (initFlag) {
			synchronized (conns) {
				// 循环次数
				int times = 0;
				while (null == conn && times < config.getCheckTimes() + 1) {
					times++;
					// 如果未达到最大链接
					if (curConnections < config.getMaxConnections()) {
						// 栈中未空
						if (!conns.isEmpty()) {
							conn = conns.pop();
							// 如果返回的链接不可用
							if (null == conn || conn.isClosed()) {
								conn = newConnection();
							}
						// 栈中空了
						} else {
							conn = newConnection();
						}
					} else {
						conns.wait(config.getCheckoutTimeout());
					}

				}
				if(null == conn){
					logger.warn("获取链接超时!!!!!");
					throw new DBPoolException("获取链接超时!!!!!");
				}else{
					curConnections++;
					conns.notifyAll();
				}

			}
		} else {
			logger.error("连接池初始化失败!!!!");
			throw new DBPoolException("连接池初始化失败!!!!");
		}

		return conn;
	}
	
	/**
	 * 归还一个链接
	 * @param conn
	 * @throws SQLException
	 * @throws InterruptedException
	 */
	public static void returnConn(Connection conn) throws SQLException, InterruptedException {
		synchronized (conns) {
			if (null != conn && !conn.isClosed()) {
				// 如果设置归还前自动提交为真
				if (config.isAutoCommitOnClose()) {
					conn.commit();
				} else {
					conn.rollback();
				}
			}
			int times = 0;
			//尝试归还5次 如果归还失败则关闭连接
			while (times < 6) {
				times++;
				if (curConnections > 0 && curConnections <= config.getMaxConnections()) {
					conns.push(conn);
					curConnections--;
					break;
				} else if (curConnections == 0) {
					conns.wait(1000);
				} else {
					if(conn!=null && !conn.isClosed())
						conn.close();
					curConnections--;
					break;
				}
			}
			if(times == 5 && conn != null && !conn.isClosed()){
				conn.close();
			}
			
			
			conns.notifyAll();
		}
	}
	
	/**
	 * 简单的创建一个链接
	 * @return
	 * @throws SQLException
	 */
	private static Connection newConnection() throws SQLException{
		return DriverManager.getConnection(config.getUrl(),config.getUsername(),config.getPassword());
	}
	/**
	 * 获取已使用的连接数
	 * @return
	 */
	public static int getCurConnections() {
		return curConnections;
	}
		
}



Config.java
public class Config {
	private String driver;
	private String url;
	private String username;
	private String password;
	private int minConnections = 10;
	private int maxConnections = 20;
	private boolean autoCommitOnClose = true;
	private int checkoutTimeout = 100;
	private int checkTimes = 10;

	public String getDriver() {
		return driver;
	}

	public void setDriver(String driver) {
		this.driver = driver;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public int getMinConnections() {
		return minConnections;
	}

	public void setMinConnections(int minConnections) {
		this.minConnections = minConnections;
	}

	public int getMaxConnections() {
		return maxConnections;
	}

	public void setMaxConnections(int maxConnections) {
		this.maxConnections = maxConnections;
	}

	public boolean isAutoCommitOnClose() {
		return autoCommitOnClose;
	}

	public void setAutoCommitOnClose(boolean autoCommitOnClose) {
		this.autoCommitOnClose = autoCommitOnClose;
	}

	public int getCheckoutTimeout() {
		return checkoutTimeout;
	}

	public void setCheckoutTimeout(int checkoutTimeout) {
		this.checkoutTimeout = checkoutTimeout;
	}

	public int getCheckTimes() {
		return checkTimes;
	}

	public void setCheckTimes(int checkTimes) {
		this.checkTimes = checkTimes;
	}
	
	
}



PropertyReader.java
public class PropertyReader {
	Logger log = Logger.getLogger(PropertyReader.class);
	Properties props = null;
	public PropertyReader(String path) throws IOException{
		props = new Properties();
		try {
			props.load(getClass().getClassLoader().getResourceAsStream(path));
		} catch (IOException e) {
			log.error("读取properties失败");
			throw e;
		}
	}
	public String getValue(String key){
		return props.getProperty(key);
	}
}




DBPoolException .java
public class DBPoolException extends Throwable {
	public DBPoolException(String str){
		super(str);
	}
}



properties配置文件
driver=oracle.jdbc.driver.OracleDriver
url=jdbc:oracle:thin:@localhost:1521:orcl
username=test
password=password
minConnections=10
maxConnections=30
#当链接关闭的时候自动将未提交的内容提交
autoCommitOnClose=false
#当连接池用完时客户端调用getConn()后等待获取新连接的时间,Default: (100毫秒)
checkoutTimeout=100
#当没有可用链接时,尝试获取链接的次数
checkTimes=30
   发表时间:2011-03-12  
个人觉得,一个数据库连接池,必须是非侵入性的,和jdbc本身兼容。

即我只需要把jdbc/datasource配置改成连接池的设置,
原来用jdbc写的代码什么都不用改。

比如只换掉一个jdbc driver类。
0 请登录后投票
   发表时间:2011-03-12  
kimmking 写道
个人觉得,一个数据库连接池,必须是非侵入性的,和jdbc本身兼容。

即我只需要把jdbc/datasource配置改成连接池的设置,
原来用jdbc写的代码什么都不用改。

比如只换掉一个jdbc driver类。


那必须的。。。。
0 请登录后投票
   发表时间:2011-03-12  
kimmking 写道
个人觉得,一个数据库连接池,必须是非侵入性的,和jdbc本身兼容。

即我只需要把jdbc/datasource配置改成连接池的设置,
原来用jdbc写的代码什么都不用改。

比如只换掉一个jdbc driver类。


首先 谢谢你的耐心观看 和 回复

我是通过properties来改变数据库的driver,就在最下面的properties配置文件中,如下:
driver=oracle.jdbc.driver.OracleDriver   
url=jdbc:oracle:thin:@localhost:1521:orcl   
username=test   
password=password   
minConnections=10  
maxConnections=30  
#当链接关闭的时候自动将未提交的内容提交   
autoCommitOnClose=false  
#当连接池用完时客户端调用getConn()后等待获取新连接的时间,Default: (100毫秒)   
checkoutTimeout=100  
#当没有可用链接时,尝试获取链接的次数   
checkTimes=30 


不知道我说的是不是你的意思。
0 请登录后投票
   发表时间:2011-03-12  
yy59750901 写道
kimmking 写道
个人觉得,一个数据库连接池,必须是非侵入性的,和jdbc本身兼容。

即我只需要把jdbc/datasource配置改成连接池的设置,
原来用jdbc写的代码什么都不用改。

比如只换掉一个jdbc driver类。


首先 谢谢你的耐心观看 和 回复

我是通过properties来改变数据库的driver,就在最下面的properties配置文件中,如下:
driver=oracle.jdbc.driver.OracleDriver   
url=jdbc:oracle:thin:@localhost:1521:orcl   
username=test   
password=password   
minConnections=10  
maxConnections=30  
#当链接关闭的时候自动将未提交的内容提交   
autoCommitOnClose=false  
#当连接池用完时客户端调用getConn()后等待获取新连接的时间,Default: (100毫秒)   
checkoutTimeout=100  
#当没有可用链接时,尝试获取链接的次数   
checkTimes=30 


不知道我说的是不是你的意思。

明显不是我的意思。
前几天也有人发了一个连接池。跟你这个几乎一样。

0 请登录后投票
   发表时间:2011-03-12  
kimmking 写道
yy59750901 写道
kimmking 写道
个人觉得,一个数据库连接池,必须是非侵入性的,和jdbc本身兼容。

即我只需要把jdbc/datasource配置改成连接池的设置,
原来用jdbc写的代码什么都不用改。

比如只换掉一个jdbc driver类。


首先 谢谢你的耐心观看 和 回复

我是通过properties来改变数据库的driver,就在最下面的properties配置文件中,如下:
driver=oracle.jdbc.driver.OracleDriver   
url=jdbc:oracle:thin:@localhost:1521:orcl   
username=test   
password=password   
minConnections=10  
maxConnections=30  
#当链接关闭的时候自动将未提交的内容提交   
autoCommitOnClose=false  
#当连接池用完时客户端调用getConn()后等待获取新连接的时间,Default: (100毫秒)   
checkoutTimeout=100  
#当没有可用链接时,尝试获取链接的次数   
checkTimes=30 


不知道我说的是不是你的意思。

明显不是我的意思。
前几天也有人发了一个连接池。跟你这个几乎一样。


啊。。是么。。。
那你的意思我没太明白,像我这样,只要把各种数据库的驱动放进去,然后通过修改properties不就能达到变换数据库的作用么,取得连接只需要 ConnectionPool.getConn();就可以了。
0 请登录后投票
   发表时间:2011-03-12  
yy59750901 写道
kimmking 写道
yy59750901 写道
kimmking 写道
个人觉得,一个数据库连接池,必须是非侵入性的,和jdbc本身兼容。

即我只需要把jdbc/datasource配置改成连接池的设置,
原来用jdbc写的代码什么都不用改。

比如只换掉一个jdbc driver类。


首先 谢谢你的耐心观看 和 回复

我是通过properties来改变数据库的driver,就在最下面的properties配置文件中,如下:
driver=oracle.jdbc.driver.OracleDriver   
url=jdbc:oracle:thin:@localhost:1521:orcl   
username=test   
password=password   
minConnections=10  
maxConnections=30  
#当链接关闭的时候自动将未提交的内容提交   
autoCommitOnClose=false  
#当连接池用完时客户端调用getConn()后等待获取新连接的时间,Default: (100毫秒)   
checkoutTimeout=100  
#当没有可用链接时,尝试获取链接的次数   
checkTimes=30 


不知道我说的是不是你的意思。

明显不是我的意思。
前几天也有人发了一个连接池。跟你这个几乎一样。


啊。。是么。。。
那你的意思我没太明白,像我这样,只要把各种数据库的驱动放进去,然后通过修改properties不就能达到变换数据库的作用么,取得连接只需要 ConnectionPool.getConn();就可以了。

我的意思是,我现在用jdbc或其他连接池写了个程序,
替换成你的连接池,不需要改我原来的代码。
0 请登录后投票
   发表时间:2011-03-12  
kimmking 写道
yy59750901 写道
kimmking 写道
yy59750901 写道
kimmking 写道
个人觉得,一个数据库连接池,必须是非侵入性的,和jdbc本身兼容。

即我只需要把jdbc/datasource配置改成连接池的设置,
原来用jdbc写的代码什么都不用改。

比如只换掉一个jdbc driver类。


首先 谢谢你的耐心观看 和 回复

我是通过properties来改变数据库的driver,就在最下面的properties配置文件中,如下:
driver=oracle.jdbc.driver.OracleDriver   
url=jdbc:oracle:thin:@localhost:1521:orcl   
username=test   
password=password   
minConnections=10  
maxConnections=30  
#当链接关闭的时候自动将未提交的内容提交   
autoCommitOnClose=false  
#当连接池用完时客户端调用getConn()后等待获取新连接的时间,Default: (100毫秒)   
checkoutTimeout=100  
#当没有可用链接时,尝试获取链接的次数   
checkTimes=30 


不知道我说的是不是你的意思。

明显不是我的意思。
前几天也有人发了一个连接池。跟你这个几乎一样。


啊。。是么。。。
那你的意思我没太明白,像我这样,只要把各种数据库的驱动放进去,然后通过修改properties不就能达到变换数据库的作用么,取得连接只需要 ConnectionPool.getConn();就可以了。

我的意思是,我现在用jdbc或其他连接池写了个程序,
替换成你的连接池,不需要改我原来的代码。

明白点你的意思了 。。。
是说普通jdbc:
CLass.forName(driver);
现在我写一个类似driver的东西么?加载这个driver之后
原来的 DriverManager.getConnection(url,name,password);
取得就是我这个driver弄出来的一个连接?
这个确实很高深!!
DriverManager.getConnection(url,name,password);的机制我还没研究过。。
感觉很复杂 像写一个数据库驱动一样。。
0 请登录后投票
   发表时间:2011-03-12   最后修改:2011-03-12
如果使用的某一个数据库连接未归还,超过一定时间,没有相关的线程代码监控?
0 请登录后投票
   发表时间:2011-03-12  
实现DataSource
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics