2019独角兽企业重金招聘Python工程师标准>>>
connectionPool.DBConnectionManager
[java] view plain copy
- package connectionPool;
- import java.sql.Connection;
- import java.sql.Driver;
- import java.sql.DriverManager;
- import java.sql.SQLException;
- import java.util.ArrayList;
- import java.util.Enumeration;
- import java.util.HashSet;
- import java.util.Hashtable;
- import java.util.List;
- import java.util.Map;
- import java.util.Map.Entry;
- import java.util.Set;
- import connectionPool.util.Logger;
- import connectionPool.util.PropertiesMgr;
- /**
- * 连接池的设计思路
- * 1、
- * 初始化固定数目的连接(空闲连接与活跃连接在同一池中),建立连接的代理类,添加busy与startTime属性以便分发与回收连接
- * 另建立守护线程回收失效连接
- * 2、
- * 维护一空闲连接池,初始为空,需要连接时建立,用完的连接回收进入空闲连接池;
- * 后续所需连接从空闲连接池获取;activeNum记录活跃连接数目;
- * 当空闲连接池为空且活跃连接数达到上限时,请求等待,超时即获取连接失败,超时前有连接被释放方可获得连接
- * 第二个设计巧妙优势明显,采用第二种方式
- *
- * 数据库连接管理类,单例模式
- * 可以管理加载多个数据库驱动,维护多个数据库连接池
- * @author shijin
- *
- */
- public class DBConnectionManager {
- private static DBConnectionManager dbm = null;
- //单例模式里的成员变量都相当于是static了?
- /**
- * 客户数目
- */
- private static int clients = 0;
- /**
- * 加载的驱动器集合
- */
- private Set<Driver> drivers = new HashSet<Driver>();
- /**
- * 数据库连接池字典
- */
- private Hashtable<String,DBConnectionPool> pools = new Hashtable<String,DBConnectionPool>();
- private final Logger log = Logger.getInstance(DBConnectionPool.class);
- private DBConnectionManager() {
- loadDrivers();
- createPools();
- }
- /**
- * 装载和注册所有的JDBC驱动程序
- */
- private void loadDrivers() {
- String str_drivers = PropertiesMgr.getProperty("driver");
- for(String str_driver:str_drivers.split("\\s")) {
- Driver driver = null;
- try {
- driver = (Driver)Class.forName(str_driver).newInstance();
- DriverManager.registerDriver(driver);
- log.info("成功加载JDBC驱动:" + str_driver);
- } catch (InstantiationException e) {
- log.error("加载JDBC驱动" + str_driver + "时初始化异常,请检查配置文件");
- } catch (IllegalAccessException e) {
- log.info("加载JDBC驱动" + str_driver + "时非法访问,请检查配置文件");
- } catch (ClassNotFoundException e) {
- log.info("未找到JDBC驱动" + str_driver + "请引入相关包");
- } catch (SQLException e) {
- log.info("加载JDBC驱动" + str_driver + "失败,请检查引入包的正确性");
- }
- drivers.add(driver);
- }
- }
- /**
- * 根据配置文件创建数据库连接池
- */
- private void createPools() {
- Enumeration<?> elements = PropertiesMgr.propertiesNames();
- while(elements.hasMoreElements()) {
- String element = (String)elements.nextElement();
- if(element.endsWith(".url")) {
- String poolName = element.substring(0, element.lastIndexOf("."));
- String url = PropertiesMgr.getProperty(poolName + ".url");
- if(url == null) {
- log.error("无法连接到数据库" + poolName + "请检查配置文件连接字符串");
- continue;
- }
- String user = PropertiesMgr.getProperty(poolName + ".user");
- String pwd = PropertiesMgr.getProperty(poolName + ".password");
- String str_max = PropertiesMgr.getProperty(poolName + ".maxconn", "0");
- int maxConn = 0;
- try{
- maxConn = Integer.parseInt(str_max);
- }catch(NumberFormatException e) {
- log.error("数据库" + poolName + "最大连接数设置错误,默认设为20");
- maxConn = 20;
- }
- DBConnectionPool pool = new DBConnectionPool(maxConn,url,poolName,user,pwd);
- pools.put(poolName, pool);
- log.info("成功创建数据库连接池" + poolName);
- }
- }
- }
- /**
- * 获得单例
- * @return DBConnectionManager单例
- */
- public synchronized static DBConnectionManager getInstance() {
- if(dbm == null) {
- dbm = new DBConnectionManager();
- }
- clients++;
- return dbm;
- }
- /**
- * 从指定连接池中获取可用连接,无等待
- * @param poolName 要获取连接的连接池名称
- * @return 连接池中的一个可用连接或null
- */
- public Connection getConnection(String poolName) {
- DBConnectionPool pool = (DBConnectionPool)pools.get(poolName);
- return pool.getConnection();
- }
- /**
- * 从指定的连接池获取可用连接,有等待超时
- * @param poolName 要获取连接的连接池名称
- * @param timeout 获取可用连接的超时时间,单位为秒
- * @return 连接池中的一个可用连接或null
- */
- public Connection getConnection(String poolName,long timeout) {
- DBConnectionPool pool = (DBConnectionPool)pools.get(poolName);
- return pool.getConnection(timeout);
- }
- /**
- * 回收指定连接池的连接
- * @param poolName 连接池名称
- * @param conn 要回收的连接
- */
- public void freeConnection(String poolName,Connection conn) {
- DBConnectionPool pool = (DBConnectionPool)pools.get(poolName);
- if(pool != null) {
- pool.freeConnection(conn);
- }
- log.error("找不到连接池,无法回收,请检查参数");
- }
- /**
- * 关闭所有连接,撤销驱动器的注册
- */
- public synchronized void release() {
- //所有客户连接都关闭时才真正关闭连接撤销注册
- if(clients-- != 0) {
- return;
- }
- for(Map.Entry<String,DBConnectionPool> poolEntry:pools.entrySet()) {
- DBConnectionPool pool = poolEntry.getValue();
- pool.releaseAll();
- }
- log.info("已经关闭所有连接");
- for(Driver driver:drivers) {
- try {
- DriverManager.deregisterDriver(driver);
- log.info("撤销JDBC驱动器" + driver.getClass().getName() + "的注册");
- } catch (SQLException e) {
- log.error("撤销JDBC驱动器" + driver.getClass().getName() + "的注册异常");
- }
- }
- log.info("驱动器撤销完成");
- }
- /**
- * 此内部类定义了一个连接池.
- * 它能够获取数据库连接,直到预定的最 大连接数为止
- * 在返回连接给客户程序之前,它能够验证连接的有效性
- * @author shijin
- */
- private class DBConnectionPool {
- private int activeNum = 0;
- private int maxConn = 0;
- private String url = null;
- private String poolName = null;
- private String user = null;
- private String pwd = null;
- private List<Connection> freeConnections = new ArrayList<Connection>();
- /**
- *
- * @param maxConn 设定的连接池允许的最大连接数
- * @param url 数据库连接url
- * @param poolName 连接池名称
- * @param user 数据库用户名,或null
- * @param pwd 数据库用户密码,或null
- */
- public DBConnectionPool(int maxConn, String url, String poolName,
- String user, String pwd) {
- super();
- this.maxConn = maxConn;
- this.url = url;
- this.poolName = poolName;
- this.user = user;
- this.pwd = pwd;
- }
- /**
- * 获得一个可用连接,不保证任何情况都能返回一个连接(比如超过最大连接数的时候返回null)
- * 1、若空闲连接池不为空,从空闲连接池取出一个连接后检查有效性,正常则返回,失效则重新获取连接
- * 2、若空闲连接池为空且未超过最大连接数限制,新建一个连接并返回
- * 3、空闲连接数为空且超过最大连接数限制,返回null
- * @return 获得的可用连接
- */
- public synchronized Connection getConnection() {
- Connection conn = null;
- //空闲连接池中有空闲连接,直接取
- if(freeConnections.size() > 0) {
- //从空闲连接池中取出一个连接
- conn = freeConnections.get(0);
- freeConnections.remove(0);
- //检测连接有效性
- try{
- if(conn.isClosed()) {
- //由于已经从空闲连接池取出,所以不使用无效连接其就无法重新进入
- //空闲连接池,意味着其已经被删除了,记入日志即可
- log.info("从连接池" + poolName + "中取出的连接已关闭,重新获取连接");
- //继续从连接池尝试获取连接
- conn = getConnection();
- }
- }catch(SQLException e) {
- log.info("从连接池" + poolName + "中取出的发生服务器访问错误,重新获取连接");
- conn = getConnection();
- }
- } else if(activeNum < maxConn) {
- conn = newConnection();
- } else {
- //未获得连接
- }
- if(conn != null) {
- activeNum++;
- }
- return conn;
- }
- /**
- * 当无空闲连接而又未达到最大连接数限制时创建新的连接
- * @return 新创建的连接
- */
- private Connection newConnection() {
- Connection conn = null;
- try{
- if(user == null) {
- conn = DriverManager.getConnection(url);
- } else {
- conn = DriverManager.getConnection(url, user, pwd);
- }
- log.info("与数据库" + poolName + "创建一个新连接");
- }catch(SQLException e) {
- log.error("无法根据\"" + url + "\"与数据库" + poolName + "建立新连接");
- }
- return conn;
- }
- /**
- * 获得一个可用连接,超过最大连接数时线程等待,直到有有连接释放时返回一个可用连接或者超时返回null
- * @param timeout 等待连接的超时时间,单位为秒
- * @return
- */
- public synchronized Connection getConnection(long timeout) {
- Connection conn = null;
- long startTime = System.currentTimeMillis();
- while((conn = getConnection()) == null) {
- try{
- //被notify(),notifyALL()唤醒或者超时自动苏醒
- wait(timeout);
- }catch(InterruptedException e) {
- log.error("等待连接的线程被意外打断");
- }
- //若线程在超时前被唤醒,则不会返回null,继续循环尝试获取连接
- if(System.currentTimeMillis() - startTime > timeout*1000000)
- return null;
- }
- return conn;
- }
- /**
- * 将释放的空闲连接加入空闲连接池,活跃连接数减一并激活等待连接的线程
- * @param conn 释放的连接
- */
- public synchronized void freeConnection(Connection conn) {
- freeConnections.add(conn);
- activeNum--;
- notifyAll();//通知正在由于达到最大连接数限制而wait的线程获取连接
- }
- /**
- * 关闭空闲连接池中的所有连接
- */
- public synchronized void releaseAll() {
- for(Connection conn:freeConnections) {
- try{
- conn.close();
- log.info("关闭空闲连接池" + poolName + "中的一个连接");
- }catch(SQLException e) {
- log.error("关闭空闲连接池" + poolName + "中的连接失败");
- }
- }
- freeConnections.clear();
- }
- }
- }
connectionpool.util.Logger
[java] view plain copy
- package connectionPool.util;
- import java.io.FileWriter;
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.util.Date;
- /**
- * 日志文件创建维护类,单例模式
- * @author shijin
- *
- */
- public class Logger {
- private static Logger logger= null;
- private PrintWriter log = null;
- private static int level = 0;
- private Class<?> c = null;
- private static final int DEBUGLEVEL = 1;
- private static final int INFOLEVEL = 2;
- private static final int ERRORLEVEL = 3;
- private Logger(Class<?> c) {
- String logFileName = PropertiesMgr.getProperty("logfile","DBlog.txt");
- String str_level = PropertiesMgr.getProperty("loglevel", "3");
- this.c = c;
- level = Integer.parseInt(str_level);
- try {
- log = new PrintWriter(new FileWriter(logFileName),true);
- } catch (IOException e) {
- System.err.println("无法打开日志文件" + logFileName);
- log = new PrintWriter(System.err);
- }
- }
- public synchronized static Logger getInstance(Class<?> c) {
- if(logger == null) {
- logger = new Logger(c);
- }
- return logger;
- }
- public void debug(String msg) {
- if(level > DEBUGLEVEL) {
- msg = "DEBUG:" + new Date() + "-" + msg;
- System.out.println(msg);
- log.println(msg);
- }
- }
- public void info(String msg) {
- if(level > INFOLEVEL) {
- msg = "INFO:" + new Date() + "-" + msg;
- System.out.println(msg);
- log.println(msg);
- }
- }
- public void error(String msg) {
- if(level > ERRORLEVEL) {
- msg = "ERROR:" + new Date() + "-" + c + "-" + msg;
- System.out.println(msg);
- log.println(msg);
- }
- }
- }
connection.util.PropertiesMgr
[java] view plain copy
- package connectionPool.util;
- import java.io.File;
- import java.io.IOException;
- import java.util.Enumeration;
- import java.util.Properties;
- /**
- * 属性文件加载管理类,单例模式
- * @author shijin
- *
- */
- public class PropertiesMgr {
- private static Properties pro = new Properties();
- private PropertiesMgr(){}
- static {
- try {
- pro.load(PropertiesMgr.class.getClassLoader().getResourceAsStream("config" + File.separator + "DB.properties"));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- public static String getProperty(String key) {
- return pro.getProperty(key);
- }
- public static String getProperty(String key,String defaultValue) {
- //找不到key用defaultValue,而不是说后面为空字符串采用defaultValue
- return pro.getProperty(key, defaultValue);
- }
- public static Enumeration<?> propertiesNames() {
- return pro.propertyNames();
- }
- }
DB.properties
[plain] view plain copy
- driver=com.mysql.jdbc.Driver
- mysql.url=jdbc:mysql://127.0.0.1/caiwu?useUnicode=true&characterEncoding=gb2312
- mysql.user=root
- mysql.password=123
- mysql.maxconn=1000
- #\u65E5\u5FD7\u6587\u4EF6\u7684\u540D\u79F0
- logfile=
- #\u65E5\u5FD7\u7EA7\u522B
- loglevel=