数据库连接管理器

数据库连接管理器,能够管理不同种类数据库的连接池 :JDBCConnectionManager.java

package com.zhihua.database.pool;

import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * 数据库连接管理器,能够管理不同种类数据库的连接池
 * <请替换成功能描述> <br>
 * <请替换成详细描述>
 * @author caizh
 * @since [1.0.0]
 * @version [1.0.0,2017年2月14日]
 */
public class JDBCConnectionManager {

    //数据库连接池配置文件
    private static final String DEFAULT_DB_PROPERTIES = "db.properties";
    //唯一实例
    private static JDBCConnectionManager instance;
    //当前连接到该数据库连接管理器上的客户端数目
    private static int clients;
    //存放驱动类,对于每种数据库都有一个驱动类
    @SuppressWarnings("rawtypes")
    private Vector drivers = new Vector<>();
    //存放已经建立的连接池,每种数据库都有一个连接池
    @SuppressWarnings("rawtypes")
    private Hashtable pools = new Hashtable<>();

    /***
     * 构造函数私有化,以防止其他对象创建本类实例
     */
    private JDBCConnectionManager(){
        init();
    }

    @SuppressWarnings("rawtypes")
    private JDBCConnectionManager(LinkedList drivers, Hashtable jdbcs){
        init2(drivers, jdbcs);
    }

    /**
     * 单例模式,返回唯一实例,如果是第一次调用此方法,则创建实例,
     * 根据数据库连接池配置文件创建连接池
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @return JDBCConnectionManager 唯一实例
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    public static synchronized JDBCConnectionManager getInstance(){
        if(instance != null){
            clients++;
            return instance;
        }else{
            instance = new JDBCConnectionManager();
            return instance;
        }
    }

    /**
     * 获取数据库连接管理器实例,如果不存在,便创建实例,并指定了创建连接池的参数
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @param jdbcInfo 连接数据库的参数
     * @return
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public static synchronized JDBCConnectionManager getInstance(JDBCInfo jdbcInfo){

        if(instance != null){
            clients++;
            return instance;
        }
        LinkedList drivers = new LinkedList<>();
        drivers.add(jdbcInfo.getDriver());
        Hashtable jdbcs = new Hashtable<>();
        jdbcs.put(jdbcInfo.getName(),jdbcInfo);
        return getInstance(drivers, jdbcs);
    }

    /**
     * 获取数据库连接管理器实例,如果不存在,便创建实例,可以一次创建多个连接池
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @param drivers 每个连接池的数据库驱动类
     * @param jdbcs 每个连接池的连接参数
     * @return
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    @SuppressWarnings("rawtypes")
    public static JDBCConnectionManager getInstance(LinkedList drivers, Hashtable jdbcs) {
        if (instance == null) {
            instance = new JDBCConnectionManager(drivers, jdbcs);
        }
        clients++;
        return instance;
    }

    /**
     * 创建多个数据库连接池
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @param drivers 每个连接池的数据库驱动类
     * @param jdbcInfo 每个连接池的连接参数
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    @SuppressWarnings("rawtypes")
    private void init2(LinkedList drivers, Hashtable jdbcInfo) {
        loadDrivers(drivers);
        createPools(jdbcInfo);
    }

    /**
     * 
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @param jdbcInfo
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    private void createPools(Hashtable jdbcInfos) {

        Iterator it = jdbcInfos.entrySet().iterator();
        while(it.hasNext()){
            Map.Entry en = (Map.Entry) it.next();
            JDBCInfo info = (JDBCInfo) en.getValue();
            if(info.getUrl() == null){
                continue;
            }
            System.out.println("准备创建数据库连接池: " + (String) en.getKey());
            DBConnectionPool pool = new DBConnectionPool((String) en.getKey(),
                    info.getUrl(), info.getUser(), info.getPassword(), info
                    .getMaxConn());
            pools.put(en.getKey(), pool);
        }
    }

    /**
     * 动态加载数据库连接驱动类
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @param drivers
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    private void loadDrivers(LinkedList mdrivers) {
        for(int i=0;i<mdrivers.size();i++){
            try{
                //根据数据库连接驱动类,利用反射机制创建驱动类对象
                Driver dirver = (Driver) Class
                        .forName((String) mdrivers.get(i)).newInstance();
                DriverManager.registerDriver(dirver);
                drivers.addElement(dirver);
                System.out.println("成功加载数据库连接驱动:  "
                        + mdrivers.get(i));
            }catch (Exception e) {
                System.err.println("加载数据库连接驱动失败:  " 
                        + mdrivers.get(i) + ". 错误信息:" + e);
            }
        }
    }

    /**
     * 读取缺省的数据库连接池配置文件,根据缺省值完成初始化,创建数据库连接池
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    private void init() {      
        InputStream is = null;
        Properties dbProps = new Properties();
        try {
            is = new FileInputStream(DEFAULT_DB_PROPERTIES);
            dbProps.load(is);
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("不能够读取默认数据库连接池配置文件,请确认文件是否存在:"
                    + DEFAULT_DB_PROPERTIES);
            return;
        }  
        loadDrivers(dbProps);
        createPools(dbProps);
    }

    /**
     * 根据指定的数据库连接池配置文件创建连接池实例.
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @param props 连接池属性
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    @SuppressWarnings({ "rawtypes", "unchecked" })
    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){
                    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) {
                    System.err.println("最大连接数限制数错误: "
                            + maxConn + " .连接池名: " + poolName);
                    max = 0;
                }
                System.out.println("准备创建数据库连接池:" + poolName);
                DBConnectionPool pool = new DBConnectionPool(poolName, url,
                        user, password, max);
                pools.put(poolName, pool);
                System.out.println("创建数据库连接池: "
                        + poolName + "成功");
            }
        }
    }

    /**
     * 装载和注册数据库连接配置文件中所有的JDBC驱动程序
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @param props 属性
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    @SuppressWarnings("unchecked")
    private void loadDrivers(Properties props) {
        String driverClasses = null;
        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);
                System.out.println("成功加载数据库连接驱动: "
                        + driverClassName);
            }catch (Exception e) {
                System.err.println("加载数据库连接驱动失败: "
                        + driverClassName + ". 错误信息: " + e);
            }
        }
    }

    /**
     * 获得一个可用的(空闲的)链接,如果没有可用连接,且已有连接数小于最大连接数
     * 限制,则创建并返回新连接
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @param name 在属性文件中定义的连接池名字
     * @return
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    public Connection getConnetion(String name){
        DBConnectionPool pool = (DBConnectionPool) pools.get(name);
        if (pool != null) {
            System.out.println("从数据库连接池:" + pool.getName() + "获取一个连接!");
            return pool.getConnection();
        }
        return null;
    }

    /**
     * 获得一个可用连接.若没有可用连接,且已有连接数小于最大连接数限制,
     * 则创建并返回新连接.否则,在指定的时间内等待其它线程释放连接.
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @param name 连接池的名字
     * @param time 以毫秒计的等待时间
     * @return
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    public Connection getConnection(String name,long time){
        DBConnectionPool pool = (DBConnectionPool) pools.get(name);
        if(pool!= null){
            return pool.getConnection(time);
        }
        return null;
    }

    /**
     * 将连接对象返回给由名字指定的连接池
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @param name 在属性文件中定义的连接池名字,即数据源信息中的name字段
     * @param conn 连接对象
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    public void freeConnection(String name,Connection conn){
        DBConnectionPool pool = (DBConnectionPool) pools.get(name);
        if(pool!=null){
            System.out.println("释放了一个数据库连接到连接池:" + pool.getName());
            pool.freeConnection(conn);
        }
    }

    /**
     * 关闭所有连接,撤销驱动程序的注册
     * 只有当连接到该数据库连接管理器的客户端数目为0时,才能够完成撤销
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    @SuppressWarnings("rawtypes")
    public synchronized void release(){
        //等待直到最后一个客户端程序调用
        if(--clients!= 0){
            return;
        }
        //释放连接池
        Enumeration allPools = pools.elements();
        while(allPools.hasMoreElements()){
            DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();
            System.out.println("准备关闭数据库连接池: " + pool.getName());
            pool.release();
            System.out.println("数据库连接池: " + pool.getName() + "已经被关闭!");
        }
        //反注册已经注册的数据库连接驱动类
        Enumeration allDrivers = drivers.elements();
        while(allDrivers.hasMoreElements()){
            Driver driver = (Driver) allDrivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                System.out.println("数据库连接驱动:" + driver.getClass().getName() + "已经被注销了!");
            } catch (SQLException e) {
            }
        }
    }

    /**
     * 此内部类定义了一个连接池.它能够根据要求创建新连接,直到预定的最
     * 大连接数为止.在返回连接给客户程序之前,它能够验证连接的有效性.
     * <请替换成功能描述> <br>
     * <请替换成详细描述>
     * @author caizh
     * @since [1.0.0]
     * @version [1.0.0,2017年2月14日]
     */
    class DBConnectionPool{

        private int checkedOut = 0;//当前已经被取走的数据库连接数,也就是正在被使用的连接数
        @SuppressWarnings("rawtypes")
        private Vector freeConnections = new Vector();//该连接池中可用的数据库连接
        private int maxConn;//该连接池允许的最大数据库连接数
        private String name;//数据库连接池的名字
        private String user;//连接数据库的用户名
        private String password;//连接数据库的密码
        private String URL;//数据库的URL

        //默认构造函数
        public DBConnectionPool(){
            this.maxConn = 0;
            this.password = "";
            this.URL = "";
        }

        /**
         * 创建新的连接池
         * @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;
            this.initConnection();
        }

        /**
         * 预先打开一个连接
         * <请替换成功能描述> <br>
         * <请替换成详细描述>
         * @author caizh
         * @since [1.0.0]
         * @version [1.0.0,2017年2月14日]
         */
        @SuppressWarnings("unchecked")
        private void initConnection() {

            Connection conn = getConnection();
            freeConnections.addElement(conn);
        }

        /**
         * 从连接池获得一个可用连接.如没有空闲的连接且当前使用的连接数小于最大连接
         * 数限制,则创建新连接.如原来登记为可用的连接不再有效,则从向量删除之,
         * 然后递归调用自己以尝试新的可用连接.
         * <请替换成功能描述> <br>
         * <请替换成详细描述>
         * @return
         * @author caizh
         * @since [1.0.0]
         * @version [1.0.0,2017年2月14日]
         */
        private synchronized Connection getConnection() {
            Connection conn = null;
            if(freeConnections.size()>0){
                //获取向量中第一个可用连接
                conn = (Connection) freeConnections.firstElement();
                freeConnections.removeElementAt(0);
                try{
                    //如果存放的这个连接已经过期或者不可用,则继续获取
                    if((conn== null) || (conn.isClosed())){
                        conn = getConnection();
                    }
                }catch(Exception e){
                    e.printStackTrace();
                }
            }else if(maxConn==0 || checkedOut<maxConn){
                //创建新的链接
                System.out.println("数据库连接池: " + this.name + "准备创建一个新的连接");
                conn = newConnection();
            }else{
                System.out.println("数据库连接池" + this.name + "没有可用的连接!");
            }
            if(conn!=null){
                this.checkedOut++;
            }
            return conn;
        }

        /**
         * 从连接池获取可用连接.可以指定客户程序能够等待的最长时间
         * <请替换成功能描述> <br>
         * <请替换成详细描述> 
         * @param timeout 以毫秒计的等待时间限制
         * @return
         * @author caizh
         * @since [1.0.0]
         * @version [1.0.0,2017年2月14日]
         */
        public synchronized Connection getConnection(long timeout){
            long startTime = new Date().getTime();
            Connection conn;
            while((conn = getConnection())==null){
                try{
                    //等待一段时间,期待其他客户端释放连接
                    wait(timeout);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                if((new Date().getTime() - startTime)>=timeout){
                    // wait()返回的原因是超时,表示没有得到可用的连接,返回null
                    return null;
                }
            }
            return conn;
        }

        /**
         * 创建新的链接
         * <请替换成功能描述> <br>
         * <请替换成详细描述>
         * @return
         * @author caizh
         * @since [1.0.0]
         * @version [1.0.0,2017年2月14日]
         */
        private Connection newConnection(){
            Connection conn = null;
            try {
                if(user == null){
                    conn = DriverManager.getConnection(URL.trim());
                }else{
                    conn = DriverManager.getConnection(URL, user,password);
                }
                System.out.println("连接池" + this.name + "创建一个新的数据库连接, 目前共有"
                        + this.checkedOut + "个连接在使用!");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return conn;
        }

        /**
         * 将不再使用的链接返回给连接池
         * <请替换成功能描述> <br>
         * <请替换成详细描述>
         * @param conn
         * @author caizh
         * @since [1.0.0]
         * @version [1.0.0,2017年2月14日]
         */
        @SuppressWarnings("unchecked")
        public synchronized void freeConnection(Connection conn){
            //将制定链接加入到向量末尾
            freeConnections.addElement(conn);
            checkedOut--;
            notifyAll();
        }

        /**
         * 关闭所有连接
         * <请替换成功能描述> <br>
         * <请替换成详细描述>
         * @author caizh
         * @since [1.0.0]
         * @version [1.0.0,2017年2月14日]
         */
        @SuppressWarnings("rawtypes")
        public synchronized void release(){
            Enumeration allConnections = freeConnections.elements();
            while(allConnections.hasMoreElements()){
                Connection conn = (Connection) allConnections.nextElement();
                try{
                    conn.close();
                    System.out.println("关闭了数据库连接池:" + this.name + "中的一个数据库连接!");                   
                }catch(SQLException e){
                    System.err.println("无法关闭连接池" + this.name + "中的连接"
                            + e.getMessage());
                }
            }
            freeConnections.removeAllElements();
        }

        /**
         * 返回数据库连接池的名字
         * <请替换成功能描述> <br>
         * <请替换成详细描述>
         * @return
         * @author caizh
         * @since [1.0.0]
         * @version [1.0.0,2017年2月14日]
         */
        public String getName(){
            return this.name;
        }
    }
}

数据库连接池需要的信息 :JDBCInfo.java

package com.zhihua.database.pool;

/***
 * 创建一个数据库连接池需要的信息:用户名、密码、最大连接数等
 * <请替换成功能描述> <br>
 * <请替换成详细描述>
 * @author caizh
 * @since [1.0.0]
 * @version [1.0.0,2017年2月14日]
 */
public class JDBCInfo {

    /**
     * 连接数据库的相关信息
     */
    private String driver;//连接数据库的驱动类
    private String url;//数据库的路径
    private String user;//用户名
    private String password;//密码
    private String name;//数据库连接池的名字
    private int maxConn;//最大连接数

    public  JDBCInfo(){
        this.driver = "";
        this.url = "";
        this.name = ""; 
        this.maxConn = 0;
    }

    public JDBCInfo(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; 
    }
    //省略get和set方法
    ......

测试类:JDBCConnectionManagerTest.java

package com.zhihua.database.test;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import com.zhihua.database.pool.JDBCConnectionManager;
import com.zhihua.database.pool.JDBCInfo;

public class JDBCConnectionManagerTest {

    public static JDBCInfo getJDBCInfo(){
        JDBCInfo jdbc = new JDBCInfo();
        jdbc.setDriver("com.mysql.jdbc.Driver");
        jdbc.setName("MySQL");
        jdbc.setUrl("jdbc:mysql://127.0.0.1:3306/easyui");
        jdbc.setUser("root");
        jdbc.setPassword("root");
        jdbc.setMaxConn(10);
        return jdbc;
    }

    public static void main(String[] args) {
        JDBCInfo jdbc = getJDBCInfo();
        String sql = "select * from e_user";
        JDBCConnectionManager jdbcManager = JDBCConnectionManager.getInstance(jdbc);
        //从数据库连接池中获取连接
        Connection conn = jdbcManager.getConnetion(jdbc.getName());
        try{
            System.out.println("正在使用刚刚获得的数据库连接"); 
            Statement sm = conn.createStatement();
            ResultSet rs = sm.executeQuery(sql);
            while(rs.next()){  
                String qname = rs.getString("username");  
                String qage = rs.getString(3);  //此种方法比较高效,列是从左向右编号的,编号从1开始  
                System.out.println(qname + " " + qage);  
            }  
            sm.close();
        }catch(SQLException e){
            System.err.println("连接数据库出错!");
        }finally{
            //释放连接到数据库连接池
            jdbcManager.freeConnection(jdbc.getName(), conn);
        }
        jdbcManager.release();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值