数据库自写连接池



[java]  view plain copy
  1. import java.sql.Connection;  
  2. import java.sql.DatabaseMetaData;  
  3. import java.sql.Driver;  
  4. import java.sql.DriverManager;  
  5. import java.sql.SQLException;  
  6. import java.sql.Statement;  
  7. import java.util.Enumeration;  
  8. import java.util.Vector;  
  9.   
  10. /*虽然现在用APACHE COMMONS DBCP可以非常方便的建立数据库连接池, 
  11.  但是像这篇文章把数据库连接池的内部原理写的这么透彻,注视这么完整, 
  12.  真是非常难得,让开发人员可以更深层次的理解数据库连接池,真是非常感 
  13.  谢这篇文章的作者。*/  
  14.   
  15. public class ConnectionPool {  
  16.   
  17.     private String jdbcDriver = ""// 数据库驱动  
  18.   
  19.     private String dbUrl = ""// 数据 URL  
  20.   
  21.     private String dbUsername = ""// 数据库用户名  
  22.   
  23.     private String dbPassword = ""// 数据库用户密码  
  24.   
  25.     private String testTable = ""// 测试连接是否可用的测试表名,默认没有测试表  
  26.   
  27.     private int initialConnections = 10// 连接池的初始大小  
  28.   
  29.     private int incrementalConnections = 5;// 连接池自动增加的大小  
  30.   
  31.     private int maxConnections = 50// 连接池最大的大小  
  32.   
  33.     private Vector connections = null// 存放连接池中数据库连接的向量 , 初始时为 null  
  34.   
  35.     //   它中存放的对象为 PooledConnection 型  
  36.   
  37.     /** 
  38.  
  39.      * 构造函数 
  40.  
  41.      * 
  42.  
  43.      * @param jdbcDriver String JDBC 驱动类串 
  44.  
  45.      * @param dbUrl String 数据库 URL 
  46.  
  47.      * @param dbUsername String 连接数据库用户名 
  48.  
  49.      * @param dbPassword String 连接数据库用户的密码 
  50.  
  51.      * 
  52.  
  53.      */  
  54.   
  55.     public ConnectionPool(String jdbcDriver, String dbUrl, String dbUsername,  
  56.             String dbPassword) {  
  57.   
  58.         this.jdbcDriver = jdbcDriver;  
  59.   
  60.         this.dbUrl = dbUrl;  
  61.   
  62.         this.dbUsername = dbUsername;  
  63.   
  64.         this.dbPassword = dbPassword;  
  65.           
  66.         try {  
  67.             createPool();  
  68.         } catch (Exception e) {  
  69.             e.printStackTrace();  
  70.         }   
  71.   
  72.     }  
  73.   
  74.     /** 
  75.  
  76.      * 返回连接池的初始大小 
  77.  
  78.      * 
  79.  
  80.      * @return 初始连接池中可获得的连接数量 
  81.  
  82.      */  
  83.   
  84.     public int getInitialConnections() {  
  85.   
  86.         return this.initialConnections;  
  87.   
  88.     }  
  89.   
  90.     /** 
  91.  
  92.      * 设置连接池的初始大小 
  93.  
  94.      * 
  95.  
  96.      * @param 用于设置初始连接池中连接的数量 
  97.  
  98.      */  
  99.   
  100.     public void setInitialConnections(int initialConnections) {  
  101.   
  102.         this.initialConnections = initialConnections;  
  103.   
  104.     }  
  105.   
  106.     /** 
  107.  
  108.      * 返回连接池自动增加的大小 、 
  109.  
  110.      * 
  111.  
  112.      * @return 连接池自动增加的大小 
  113.  
  114.      */  
  115.   
  116.     public int getIncrementalConnections() {  
  117.   
  118.         return this.incrementalConnections;  
  119.   
  120.     }  
  121.   
  122.     /** 
  123.  
  124.      * 设置连接池自动增加的大小 
  125.  
  126.      * @param 连接池自动增加的大小 
  127.  
  128.      */  
  129.   
  130.     public void setIncrementalConnections(int incrementalConnections) {  
  131.   
  132.         this.incrementalConnections = incrementalConnections;  
  133.   
  134.     }  
  135.   
  136.     /** 
  137.  
  138.      * 返回连接池中最大的可用连接数量 
  139.  
  140.      * @return 连接池中最大的可用连接数量 
  141.  
  142.      */  
  143.   
  144.     public int getMaxConnections() {  
  145.   
  146.         return this.maxConnections;  
  147.   
  148.     }  
  149.   
  150.     /** 
  151.  
  152.      * 设置连接池中最大可用的连接数量 
  153.  
  154.      * 
  155.  
  156.      * @param 设置连接池中最大可用的连接数量值 
  157.  
  158.      */  
  159.   
  160.     public void setMaxConnections(int maxConnections) {  
  161.   
  162.         this.maxConnections = maxConnections;  
  163.   
  164.     }  
  165.   
  166.     /** 
  167.  
  168.      * 获取测试数据库表的名字 
  169.  
  170.      * 
  171.  
  172.      * @return 测试数据库表的名字 
  173.  
  174.      */  
  175.   
  176.     public String getTestTable() {  
  177.   
  178.         return this.testTable;  
  179.   
  180.     }  
  181.   
  182.     /** 
  183.  
  184.      * 设置测试表的名字 
  185.  
  186.      * @param testTable String 测试表的名字 
  187.  
  188.      */  
  189.   
  190.     public void setTestTable(String testTable) {  
  191.   
  192.         this.testTable = testTable;  
  193.   
  194.     }  
  195.   
  196.     /** 
  197.  
  198.      * 
  199.  
  200.      * 创建一个数据库连接池,连接池中的可用连接的数量采用类成员 
  201.  
  202.      * initialConnections 中设置的值 
  203.  
  204.      */  
  205.   
  206.     public synchronized void createPool() throws Exception {  
  207.   
  208.         // 确保连接池没有创建  
  209.   
  210.         // 如果连接池己经创建了,保存连接的向量 connections 不会为空  
  211.   
  212.         if (connections != null) {  
  213.   
  214.             return// 如果己经创建,则返回  
  215.   
  216.         }  
  217.   
  218.         // 实例化 JDBC Driver 中指定的驱动类实例  
  219.   
  220.         Driver driver = (Driver) (Class.forName(this.jdbcDriver).newInstance());  
  221.   
  222.         DriverManager.registerDriver(driver); // 注册 JDBC 驱动程序  
  223.   
  224.         // 创建保存连接的向量 , 初始时有 0 个元素  
  225.   
  226.         connections = new Vector();  
  227.   
  228.         // 根据 initialConnections 中设置的值,创建连接。  
  229.   
  230.         createConnections(this.initialConnections);  
  231.   
  232.         System.out.println(" 数据库连接池创建成功! ");  
  233.   
  234.     }  
  235.   
  236.     /** 
  237.  
  238.      * 创建由 numConnections 指定数目的数据库连接 , 并把这些连接 
  239.  
  240.      * 放入 connections 向量中 
  241.  
  242.      * 
  243.  
  244.      * @param numConnections 要创建的数据库连接的数目 
  245.  
  246.      */  
  247.     private void createConnections(int numConnections) throws SQLException {  
  248.   
  249.         // 循环创建指定数目的数据库连接  
  250.   
  251.         for (int x = 0; x < numConnections; x++) {  
  252.   
  253.             // 是否连接池中的数据库连接的数量己经达到最大?最大值由类成员 maxConnections  
  254.   
  255.             // 指出,如果 maxConnections 为 0 或负数,表示连接数量没有限制。  
  256.   
  257.             // 如果连接数己经达到最大,即退出。  
  258.   
  259.             if (this.maxConnections > 0  
  260.                     && this.connections.size() >= this.maxConnections) {  
  261.   
  262.                 break;  
  263.   
  264.             }  
  265.   
  266.             //add a new PooledConnection object to connections vector  
  267.   
  268.             // 增加一个连接到连接池中(向量 connections 中)  
  269.   
  270.             try {  
  271.   
  272.                 connections.addElement(new PooledConnection(newConnection()));  
  273.   
  274.             } catch (SQLException e) {  
  275.   
  276.                 System.out.println(" 创建数据库连接失败! " + e.getMessage());  
  277.   
  278.                 throw new SQLException();  
  279.   
  280.             }  
  281.   
  282.             System.out.println(" 数据库连接己创建 ......");  
  283.   
  284.         }  
  285.   
  286.     }  
  287.   
  288.     /** 
  289.  
  290.      * 创建一个新的数据库连接并返回它 
  291.  
  292.      * 
  293.  
  294.      * @return 返回一个新创建的数据库连接 
  295.  
  296.      */  
  297.   
  298.     private Connection newConnection() throws SQLException {  
  299.   
  300.         // 创建一个数据库连接  
  301.   
  302.         Connection conn = DriverManager.getConnection(dbUrl, dbUsername,  
  303.                 dbPassword);  
  304.   
  305.         // 如果这是第一次创建数据库连接,即检查数据库,获得此数据库允许支持的  
  306.   
  307.         // 最大客户连接数目  
  308.   
  309.         //connections.size()==0 表示目前没有连接己被创建  
  310.   
  311.         if (connections.size() == 0) {  
  312.   
  313.             DatabaseMetaData metaData = conn.getMetaData();  
  314.   
  315.             int driverMaxConnections = metaData.getMaxConnections();  
  316.   
  317.             // 数据库返回的 driverMaxConnections 若为 0 ,表示此数据库没有最大  
  318.   
  319.             // 连接限制,或数据库的最大连接限制不知道  
  320.   
  321.             //driverMaxConnections 为返回的一个整数,表示此数据库允许客户连接的数目  
  322.   
  323.             // 如果连接池中设置的最大连接数量大于数据库允许的连接数目 , 则置连接池的最大  
  324.   
  325.             // 连接数目为数据库允许的最大数目  
  326.   
  327.             if (driverMaxConnections > 0  
  328.                     && this.maxConnections > driverMaxConnections) {  
  329.   
  330.                 this.maxConnections = driverMaxConnections;  
  331.   
  332.             }  
  333.   
  334.         }  
  335.   
  336.         return conn; // 返回创建的新的数据库连接  
  337.   
  338.     }  
  339.   
  340.     /** 
  341.  
  342.      * 通过调用 getFreeConnection() 函数返回一个可用的数据库连接 , 
  343.  
  344.      * 如果当前没有可用的数据库连接,并且更多的数据库连接不能创 
  345.  
  346.      * 建(如连接池大小的限制),此函数等待一会再尝试获取。 
  347.  
  348.      * 
  349.  
  350.      * @return 返回一个可用的数据库连接对象 
  351.  
  352.      */  
  353.   
  354.     public synchronized Connection getConnection() throws SQLException {  
  355.   
  356.         // 确保连接池己被创建  
  357.   
  358.         if (connections == null) {  
  359.   
  360.             return null// 连接池还没创建,则返回 null  
  361.   
  362.         }  
  363.   
  364.         Connection conn = getFreeConnection(); // 获得一个可用的数据库连接  
  365.   
  366.         // 如果目前没有可以使用的连接,即所有的连接都在使用中  
  367.   
  368.         while (conn == null) {  
  369.   
  370.             // 等一会再试  
  371.   
  372.             wait(250);  
  373.   
  374.             conn = getFreeConnection(); // 重新再试,直到获得可用的连接,如果  
  375.   
  376.             //getFreeConnection() 返回的为 null  
  377.   
  378.             // 则表明创建一批连接后也不可获得可用连接  
  379.   
  380.         }  
  381.   
  382.         return conn;// 返回获得的可用的连接  
  383.   
  384.     }  
  385.   
  386.     /** 
  387.  
  388.      * 本函数从连接池向量 connections 中返回一个可用的的数据库连接,如果 
  389.  
  390.      * 当前没有可用的数据库连接,本函数则根据 incrementalConnections 设置 
  391.  
  392.      * 的值创建几个数据库连接,并放入连接池中。 
  393.  
  394.      * 如果创建后,所有的连接仍都在使用中,则返回 null 
  395.  
  396.      * @return 返回一个可用的数据库连接 
  397.  
  398.      */  
  399.   
  400.     private Connection getFreeConnection() throws SQLException {  
  401.   
  402.         // 从连接池中获得一个可用的数据库连接  
  403.   
  404.         Connection conn = findFreeConnection();  
  405.   
  406.         if (conn == null) {  
  407.   
  408.             // 如果目前连接池中没有可用的连接  
  409.   
  410.             // 创建一些连接  
  411.   
  412.             createConnections(incrementalConnections);  
  413.   
  414.             // 重新从池中查找是否有可用连接  
  415.   
  416.             conn = findFreeConnection();  
  417.   
  418.             if (conn == null) {  
  419.   
  420.                 // 如果创建连接后仍获得不到可用的连接,则返回 null  
  421.   
  422.                 return null;  
  423.   
  424.             }  
  425.   
  426.         }  
  427.   
  428.         return conn;  
  429.   
  430.     }  
  431.   
  432.     /** 
  433.  
  434.      * 查找连接池中所有的连接,查找一个可用的数据库连接, 
  435.  
  436.      * 如果没有可用的连接,返回 null 
  437.  
  438.      * 
  439.  
  440.      * @return 返回一个可用的数据库连接 
  441.  
  442.      */  
  443.   
  444.     private Connection findFreeConnection() throws SQLException {  
  445.   
  446.         Connection conn = null;  
  447.   
  448.         PooledConnection pConn = null;  
  449.   
  450.         // 获得连接池向量中所有的对象  
  451.   
  452.         Enumeration enumerate = connections.elements();  
  453.   
  454.         // 遍历所有的对象,看是否有可用的连接  
  455.   
  456.         while (enumerate.hasMoreElements()) {  
  457.   
  458.             pConn = (PooledConnection) enumerate.nextElement();  
  459.   
  460.             if (!pConn.isBusy()) {  
  461.   
  462.                 // 如果此对象不忙,则获得它的数据库连接并把它设为忙  
  463.   
  464.                 conn = pConn.getConnection();  
  465.   
  466.                 pConn.setBusy(true);  
  467.   
  468.                 // 测试此连接是否可用  
  469.   
  470.                 if (!testConnection(conn)) {  
  471.   
  472.                     // 如果此连接不可再用了,则创建一个新的连接,  
  473.   
  474.                     // 并替换此不可用的连接对象,如果创建失败,返回 null  
  475.   
  476.                     try {  
  477.   
  478.                         conn = newConnection();  
  479.   
  480.                     } catch (SQLException e) {  
  481.   
  482.                         System.out.println(" 创建数据库连接失败! " + e.getMessage());  
  483.   
  484.                         return null;  
  485.   
  486.                     }  
  487.   
  488.                     pConn.setConnection(conn);  
  489.   
  490.                 }  
  491.   
  492.                 break// 己经找到一个可用的连接,退出  
  493.   
  494.             }  
  495.   
  496.         }  
  497.   
  498.         return conn;// 返回找到到的可用连接  
  499.   
  500.     }  
  501.   
  502.     /** 
  503.  
  504.      * 测试一个连接是否可用,如果不可用,关掉它并返回 false 
  505.  
  506.      * 否则可用返回 true 
  507.  
  508.      * 
  509.  
  510.      * @param conn 需要测试的数据库连接 
  511.  
  512.      * @return 返回 true 表示此连接可用, false 表示不可用 
  513.  
  514.      */  
  515.   
  516.     private boolean testConnection(Connection conn) {  
  517.   
  518.         try {  
  519.   
  520.             // 判断测试表是否存在  
  521.   
  522.             if (testTable.equals("")) {  
  523.   
  524.                 // 如果测试表为空,试着使用此连接的 setAutoCommit() 方法  
  525.   
  526.                 // 来判断连接否可用(此方法只在部分数据库可用,如果不可用 ,  
  527.   
  528.                 // 抛出异常)。注意:使用测试表的方法更可靠  
  529.   
  530.                 conn.setAutoCommit(true);  
  531.   
  532.             } else {// 有测试表的时候使用测试表测试  
  533.   
  534.                 //check if this connection is valid  
  535.   
  536.                 Statement stmt = conn.createStatement();  
  537.   
  538.                 stmt.execute("select count(*) from " + testTable);  
  539.   
  540.             }  
  541.   
  542.         } catch (SQLException e) {  
  543.   
  544.             // 上面抛出异常,此连接己不可用,关闭它,并返回 false;  
  545.   
  546.             closeConnection(conn);  
  547.   
  548.             return false;  
  549.   
  550.         }  
  551.   
  552.         // 连接可用,返回 true  
  553.   
  554.         return true;  
  555.   
  556.     }  
  557.   
  558.     /** 
  559.  
  560.      * 此函数返回一个数据库连接到连接池中,并把此连接置为空闲。 
  561.  
  562.      * 所有使用连接池获得的数据库连接均应在不使用此连接时返回它。 
  563.  
  564.      * 
  565.  
  566.      * @param 需返回到连接池中的连接对象 
  567.  
  568.      */  
  569.   
  570.     public void returnConnection(Connection conn) {  
  571.   
  572.         // 确保连接池存在,如果连接没有创建(不存在),直接返回  
  573.   
  574.         if (connections == null) {  
  575.   
  576.             System.out.println(" 连接池不存在,无法返回此连接到连接池中 !");  
  577.   
  578.             return;  
  579.   
  580.         }  
  581.   
  582.         PooledConnection pConn = null;  
  583.   
  584.         Enumeration enumerate = connections.elements();  
  585.   
  586.         // 遍历连接池中的所有连接,找到这个要返回的连接对象  
  587.   
  588.         while (enumerate.hasMoreElements()) {  
  589.   
  590.             pConn = (PooledConnection) enumerate.nextElement();  
  591.   
  592.             // 先找到连接池中的要返回的连接对象  
  593.   
  594.             if (conn == pConn.getConnection()) {  
  595.   
  596.                 // 找到了 , 设置此连接为空闲状态  
  597.   
  598.                 pConn.setBusy(false);  
  599.   
  600.                 break;  
  601.   
  602.             }  
  603.   
  604.         }  
  605.   
  606.     }  
  607.   
  608.     /** 
  609.  
  610.      * 刷新连接池中所有的连接对象 
  611.  
  612.      * 
  613.  
  614.      */  
  615.   
  616.     public synchronized void refreshConnections() throws SQLException {  
  617.   
  618.         // 确保连接池己创新存在  
  619.   
  620.         if (connections == null) {  
  621.   
  622.             System.out.println(" 连接池不存在,无法刷新 !");  
  623.   
  624.             return;  
  625.   
  626.         }  
  627.   
  628.         PooledConnection pConn = null;  
  629.   
  630.         Enumeration enumerate = connections.elements();  
  631.   
  632.         while (enumerate.hasMoreElements()) {  
  633.   
  634.             // 获得一个连接对象  
  635.   
  636.             pConn = (PooledConnection) enumerate.nextElement();  
  637.   
  638.             // 如果对象忙则等 5 秒 ,5 秒后直接刷新  
  639.   
  640.             if (pConn.isBusy()) {  
  641.   
  642.                 wait(5000); // 等 5 秒  
  643.   
  644.             }  
  645.   
  646.             // 关闭此连接,用一个新的连接代替它。  
  647.   
  648.             closeConnection(pConn.getConnection());  
  649.   
  650.             pConn.setConnection(newConnection());  
  651.   
  652.             pConn.setBusy(false);  
  653.   
  654.         }  
  655.   
  656.     }  
  657.   
  658.     /** 
  659.  
  660.      * 关闭连接池中所有的连接,并清空连接池。 
  661.  
  662.      */  
  663.   
  664.     public synchronized void closeConnectionPool() throws SQLException {  
  665.   
  666.         // 确保连接池存在,如果不存在,返回  
  667.   
  668.         if (connections == null) {  
  669.   
  670.             System.out.println(" 连接池不存在,无法关闭 !");  
  671.   
  672.             return;  
  673.   
  674.         }  
  675.   
  676.         PooledConnection pConn = null;  
  677.   
  678.         Enumeration enumerate = connections.elements();  
  679.   
  680.         while (enumerate.hasMoreElements()) {  
  681.   
  682.             pConn = (PooledConnection) enumerate.nextElement();  
  683.   
  684.             // 如果忙,等 5 秒  
  685.   
  686.             if (pConn.isBusy()) {  
  687.   
  688.                 wait(5000); // 等 5 秒  
  689.   
  690.             }  
  691.   
  692.             //5 秒后直接关闭它  
  693.   
  694.             closeConnection(pConn.getConnection());  
  695.   
  696.             // 从连接池向量中删除它  
  697.   
  698.             connections.removeElement(pConn);  
  699.   
  700.         }  
  701.   
  702.         // 置连接池为空  
  703.   
  704.         connections = null;  
  705.   
  706.     }  
  707.   
  708.     /** 
  709.  
  710.      * 关闭一个数据库连接 
  711.  
  712.      * 
  713.  
  714.      * @param 需要关闭的数据库连接 
  715.  
  716.      */  
  717.   
  718.     private void closeConnection(Connection conn) {  
  719.   
  720.         try {  
  721.   
  722.             conn.close();  
  723.   
  724.         } catch (SQLException e) {  
  725.   
  726.             System.out.println(" 关闭数据库连接出错: " + e.getMessage());  
  727.   
  728.         }  
  729.   
  730.     }  
  731.   
  732.     /** 
  733.  
  734.      * 使程序等待给定的毫秒数 
  735.  
  736.      * 
  737.  
  738.      * @param 给定的毫秒数 
  739.  
  740.      */  
  741.   
  742.     private void wait(int mSeconds) {  
  743.   
  744.         try {  
  745.   
  746.             Thread.sleep(mSeconds);  
  747.   
  748.         } catch (InterruptedException e) {  
  749.   
  750.         }  
  751.   
  752.     }  
  753.   
  754.     /** 
  755.  
  756.      * 内部使用的用于保存连接池中连接对象的类 
  757.  
  758.      * 此类中有两个成员,一个是数据库的连接,另一个是指示此连接是否 
  759.  
  760.      * 正在使用的标志。 
  761.  
  762.      */  
  763.   
  764.     class PooledConnection {  
  765.   
  766.         Connection connection = null;// 数据库连接  
  767.   
  768.         boolean busy = false// 此连接是否正在使用的标志,默认没有正在使用  
  769.   
  770.         // 构造函数,根据一个 Connection 构告一个 PooledConnection 对象  
  771.   
  772.         public PooledConnection(Connection connection) {  
  773.   
  774.             this.connection = connection;  
  775.   
  776.         }  
  777.   
  778.         // 返回此对象中的连接  
  779.   
  780.         public Connection getConnection() {  
  781.   
  782.             return connection;  
  783.   
  784.         }  
  785.   
  786.         // 设置此对象的,连接  
  787.   
  788.         public void setConnection(Connection connection) {  
  789.   
  790.             this.connection = connection;  
  791.   
  792.         }  
  793.   
  794.         // 获得对象连接是否忙  
  795.   
  796.         public boolean isBusy() {  
  797.   
  798.             return busy;  
  799.   
  800.         }  
  801.   
  802.         // 设置对象的连接正在忙  
  803.   
  804.         public void setBusy(boolean busy) {  
  805.   
  806.             this.busy = busy;  
  807.   
  808.         }  
  809.   
  810.     }  
  811.   
  812. }  
  813.   
  814. /*======================================= 
  815.  
  816.  这个例子是根据POSTGRESQL数据库写的, 
  817.  请用的时候根据实际的数据库调整。 
  818.  
  819.  调用方法如下: 
  820.  
  821.  ① ConnectionPool connPool  
  822.  = new ConnectionPool("org.postgresql.Driver" 
  823.  ,"jdbc:postgresql://dbURI:5432/DBName" 
  824.  ,"postgre" 
  825.  ,"postgre"); 
  826.  
  827.  ② connPool .createPool(); 
  828.  Connection conn = connPool .getConnection(); 
  829.  */  
[java]  view plain copy
  1. import java.sql.Connection;  
  2. import java.sql.DatabaseMetaData;  
  3. import java.sql.Driver;  
  4. import java.sql.DriverManager;  
  5. import java.sql.SQLException;  
  6. import java.sql.Statement;  
  7. import java.util.Enumeration;  
  8. import java.util.Vector;  
  9.   
  10. /*虽然现在用APACHE COMMONS DBCP可以非常方便的建立数据库连接池, 
  11.  但是像这篇文章把数据库连接池的内部原理写的这么透彻,注视这么完整, 
  12.  真是非常难得,让开发人员可以更深层次的理解数据库连接池,真是非常感 
  13.  谢这篇文章的作者。*/  
  14.   
  15. public class ConnectionPool {  
  16.   
  17.     private String jdbcDriver = ""// 数据库驱动  
  18.   
  19.     private String dbUrl = ""// 数据 URL  
  20.   
  21.     private String dbUsername = ""// 数据库用户名  
  22.   
  23.     private String dbPassword = ""// 数据库用户密码  
  24.   
  25.     private String testTable = ""// 测试连接是否可用的测试表名,默认没有测试表  
  26.   
  27.     private int initialConnections = 10// 连接池的初始大小  
  28.   
  29.     private int incrementalConnections = 5;// 连接池自动增加的大小  
  30.   
  31.     private int maxConnections = 50// 连接池最大的大小  
  32.   
  33.     private Vector connections = null// 存放连接池中数据库连接的向量 , 初始时为 null  
  34.   
  35.     //   它中存放的对象为 PooledConnection 型  
  36.   
  37.     /** 
  38.  
  39.      * 构造函数 
  40.  
  41.      * 
  42.  
  43.      * @param jdbcDriver String JDBC 驱动类串 
  44.  
  45.      * @param dbUrl String 数据库 URL 
  46.  
  47.      * @param dbUsername String 连接数据库用户名 
  48.  
  49.      * @param dbPassword String 连接数据库用户的密码 
  50.  
  51.      * 
  52.  
  53.      */  
  54.   
  55.     public ConnectionPool(String jdbcDriver, String dbUrl, String dbUsername,  
  56.             String dbPassword) {  
  57.   
  58.         this.jdbcDriver = jdbcDriver;  
  59.   
  60.         this.dbUrl = dbUrl;  
  61.   
  62.         this.dbUsername = dbUsername;  
  63.   
  64.         this.dbPassword = dbPassword;  
  65.           
  66.         try {  
  67.             createPool();  
  68.         } catch (Exception e) {  
  69.             e.printStackTrace();  
  70.         }   
  71.   
  72.     }  
  73.   
  74.     /** 
  75.  
  76.      * 返回连接池的初始大小 
  77.  
  78.      * 
  79.  
  80.      * @return 初始连接池中可获得的连接数量 
  81.  
  82.      */  
  83.   
  84.     public int getInitialConnections() {  
  85.   
  86.         return this.initialConnections;  
  87.   
  88.     }  
  89.   
  90.     /** 
  91.  
  92.      * 设置连接池的初始大小 
  93.  
  94.      * 
  95.  
  96.      * @param 用于设置初始连接池中连接的数量 
  97.  
  98.      */  
  99.   
  100.     public void setInitialConnections(int initialConnections) {  
  101.   
  102.         this.initialConnections = initialConnections;  
  103.   
  104.     }  
  105.   
  106.     /** 
  107.  
  108.      * 返回连接池自动增加的大小 、 
  109.  
  110.      * 
  111.  
  112.      * @return 连接池自动增加的大小 
  113.  
  114.      */  
  115.   
  116.     public int getIncrementalConnections() {  
  117.   
  118.         return this.incrementalConnections;  
  119.   
  120.     }  
  121.   
  122.     /** 
  123.  
  124.      * 设置连接池自动增加的大小 
  125.  
  126.      * @param 连接池自动增加的大小 
  127.  
  128.      */  
  129.   
  130.     public void setIncrementalConnections(int incrementalConnections) {  
  131.   
  132.         this.incrementalConnections = incrementalConnections;  
  133.   
  134.     }  
  135.   
  136.     /** 
  137.  
  138.      * 返回连接池中最大的可用连接数量 
  139.  
  140.      * @return 连接池中最大的可用连接数量 
  141.  
  142.      */  
  143.   
  144.     public int getMaxConnections() {  
  145.   
  146.         return this.maxConnections;  
  147.   
  148.     }  
  149.   
  150.     /** 
  151.  
  152.      * 设置连接池中最大可用的连接数量 
  153.  
  154.      * 
  155.  
  156.      * @param 设置连接池中最大可用的连接数量值 
  157.  
  158.      */  
  159.   
  160.     public void setMaxConnections(int maxConnections) {  
  161.   
  162.         this.maxConnections = maxConnections;  
  163.   
  164.     }  
  165.   
  166.     /** 
  167.  
  168.      * 获取测试数据库表的名字 
  169.  
  170.      * 
  171.  
  172.      * @return 测试数据库表的名字 
  173.  
  174.      */  
  175.   
  176.     public String getTestTable() {  
  177.   
  178.         return this.testTable;  
  179.   
  180.     }  
  181.   
  182.     /** 
  183.  
  184.      * 设置测试表的名字 
  185.  
  186.      * @param testTable String 测试表的名字 
  187.  
  188.      */  
  189.   
  190.     public void setTestTable(String testTable) {  
  191.   
  192.         this.testTable = testTable;  
  193.   
  194.     }  
  195.   
  196.     /** 
  197.  
  198.      * 
  199.  
  200.      * 创建一个数据库连接池,连接池中的可用连接的数量采用类成员 
  201.  
  202.      * initialConnections 中设置的值 
  203.  
  204.      */  
  205.   
  206.     public synchronized void createPool() throws Exception {  
  207.   
  208.         // 确保连接池没有创建  
  209.   
  210.         // 如果连接池己经创建了,保存连接的向量 connections 不会为空  
  211.   
  212.         if (connections != null) {  
  213.   
  214.             return// 如果己经创建,则返回  
  215.   
  216.         }  
  217.   
  218.         // 实例化 JDBC Driver 中指定的驱动类实例  
  219.   
  220.         Driver driver = (Driver) (Class.forName(this.jdbcDriver).newInstance());  
  221.   
  222.         DriverManager.registerDriver(driver); // 注册 JDBC 驱动程序  
  223.   
  224.         // 创建保存连接的向量 , 初始时有 0 个元素  
  225.   
  226.         connections = new Vector();  
  227.   
  228.         // 根据 initialConnections 中设置的值,创建连接。  
  229.   
  230.         createConnections(this.initialConnections);  
  231.   
  232.         System.out.println(" 数据库连接池创建成功! ");  
  233.   
  234.     }  
  235.   
  236.     /** 
  237.  
  238.      * 创建由 numConnections 指定数目的数据库连接 , 并把这些连接 
  239.  
  240.      * 放入 connections 向量中 
  241.  
  242.      * 
  243.  
  244.      * @param numConnections 要创建的数据库连接的数目 
  245.  
  246.      */  
  247.     private void createConnections(int numConnections) throws SQLException {  
  248.   
  249.         // 循环创建指定数目的数据库连接  
  250.   
  251.         for (int x = 0; x < numConnections; x++) {  
  252.   
  253.             // 是否连接池中的数据库连接的数量己经达到最大?最大值由类成员 maxConnections  
  254.   
  255.             // 指出,如果 maxConnections 为 0 或负数,表示连接数量没有限制。  
  256.   
  257.             // 如果连接数己经达到最大,即退出。  
  258.   
  259.             if (this.maxConnections > 0  
  260.                     && this.connections.size() >= this.maxConnections) {  
  261.   
  262.                 break;  
  263.   
  264.             }  
  265.   
  266.             //add a new PooledConnection object to connections vector  
  267.   
  268.             // 增加一个连接到连接池中(向量 connections 中)  
  269.   
  270.             try {  
  271.   
  272.                 connections.addElement(new PooledConnection(newConnection()));  
  273.   
  274.             } catch (SQLException e) {  
  275.   
  276.                 System.out.println(" 创建数据库连接失败! " + e.getMessage());  
  277.   
  278.                 throw new SQLException();  
  279.   
  280.             }  
  281.   
  282.             System.out.println(" 数据库连接己创建 ......");  
  283.   
  284.         }  
  285.   
  286.     }  
  287.   
  288.     /** 
  289.  
  290.      * 创建一个新的数据库连接并返回它 
  291.  
  292.      * 
  293.  
  294.      * @return 返回一个新创建的数据库连接 
  295.  
  296.      */  
  297.   
  298.     private Connection newConnection() throws SQLException {  
  299.   
  300.         // 创建一个数据库连接  
  301.   
  302.         Connection conn = DriverManager.getConnection(dbUrl, dbUsername,  
  303.                 dbPassword);  
  304.   
  305.         // 如果这是第一次创建数据库连接,即检查数据库,获得此数据库允许支持的  
  306.   
  307.         // 最大客户连接数目  
  308.   
  309.         //connections.size()==0 表示目前没有连接己被创建  
  310.   
  311.         if (connections.size() == 0) {  
  312.   
  313.             DatabaseMetaData metaData = conn.getMetaData();  
  314.   
  315.             int driverMaxConnections = metaData.getMaxConnections();  
  316.   
  317.             // 数据库返回的 driverMaxConnections 若为 0 ,表示此数据库没有最大  
  318.   
  319.             // 连接限制,或数据库的最大连接限制不知道  
  320.   
  321.             //driverMaxConnections 为返回的一个整数,表示此数据库允许客户连接的数目  
  322.   
  323.             // 如果连接池中设置的最大连接数量大于数据库允许的连接数目 , 则置连接池的最大  
  324.   
  325.             // 连接数目为数据库允许的最大数目  
  326.   
  327.             if (driverMaxConnections > 0  
  328.                     && this.maxConnections > driverMaxConnections) {  
  329.   
  330.                 this.maxConnections = driverMaxConnections;  
  331.   
  332.             }  
  333.   
  334.         }  
  335.   
  336.         return conn; // 返回创建的新的数据库连接  
  337.   
  338.     }  
  339.   
  340.     /** 
  341.  
  342.      * 通过调用 getFreeConnection() 函数返回一个可用的数据库连接 , 
  343.  
  344.      * 如果当前没有可用的数据库连接,并且更多的数据库连接不能创 
  345.  
  346.      * 建(如连接池大小的限制),此函数等待一会再尝试获取。 
  347.  
  348.      * 
  349.  
  350.      * @return 返回一个可用的数据库连接对象 
  351.  
  352.      */  
  353.   
  354.     public synchronized Connection getConnection() throws SQLException {  
  355.   
  356.         // 确保连接池己被创建  
  357.   
  358.         if (connections == null) {  
  359.   
  360.             return null// 连接池还没创建,则返回 null  
  361.   
  362.         }  
  363.   
  364.         Connection conn = getFreeConnection(); // 获得一个可用的数据库连接  
  365.   
  366.         // 如果目前没有可以使用的连接,即所有的连接都在使用中  
  367.   
  368.         while (conn == null) {  
  369.   
  370.             // 等一会再试  
  371.   
  372.             wait(250);  
  373.   
  374.             conn = getFreeConnection(); // 重新再试,直到获得可用的连接,如果  
  375.   
  376.             //getFreeConnection() 返回的为 null  
  377.   
  378.             // 则表明创建一批连接后也不可获得可用连接  
  379.   
  380.         }  
  381.   
  382.         return conn;// 返回获得的可用的连接  
  383.   
  384.     }  
  385.   
  386.     /** 
  387.  
  388.      * 本函数从连接池向量 connections 中返回一个可用的的数据库连接,如果 
  389.  
  390.      * 当前没有可用的数据库连接,本函数则根据 incrementalConnections 设置 
  391.  
  392.      * 的值创建几个数据库连接,并放入连接池中。 
  393.  
  394.      * 如果创建后,所有的连接仍都在使用中,则返回 null 
  395.  
  396.      * @return 返回一个可用的数据库连接 
  397.  
  398.      */  
  399.   
  400.     private Connection getFreeConnection() throws SQLException {  
  401.   
  402.         // 从连接池中获得一个可用的数据库连接  
  403.   
  404.         Connection conn = findFreeConnection();  
  405.   
  406.         if (conn == null) {  
  407.   
  408.             // 如果目前连接池中没有可用的连接  
  409.   
  410.             // 创建一些连接  
  411.   
  412.             createConnections(incrementalConnections);  
  413.   
  414.             // 重新从池中查找是否有可用连接  
  415.   
  416.             conn = findFreeConnection();  
  417.   
  418.             if (conn == null) {  
  419.   
  420.                 // 如果创建连接后仍获得不到可用的连接,则返回 null  
  421.   
  422.                 return null;  
  423.   
  424.             }  
  425.   
  426.         }  
  427.   
  428.         return conn;  
  429.   
  430.     }  
  431.   
  432.     /** 
  433.  
  434.      * 查找连接池中所有的连接,查找一个可用的数据库连接, 
  435.  
  436.      * 如果没有可用的连接,返回 null 
  437.  
  438.      * 
  439.  
  440.      * @return 返回一个可用的数据库连接 
  441.  
  442.      */  
  443.   
  444.     private Connection findFreeConnection() throws SQLException {  
  445.   
  446.         Connection conn = null;  
  447.   
  448.         PooledConnection pConn = null;  
  449.   
  450.         // 获得连接池向量中所有的对象  
  451.   
  452.         Enumeration enumerate = connections.elements();  
  453.   
  454.         // 遍历所有的对象,看是否有可用的连接  
  455.   
  456.         while (enumerate.hasMoreElements()) {  
  457.   
  458.             pConn = (PooledConnection) enumerate.nextElement();  
  459.   
  460.             if (!pConn.isBusy()) {  
  461.   
  462.                 // 如果此对象不忙,则获得它的数据库连接并把它设为忙  
  463.   
  464.                 conn = pConn.getConnection();  
  465.   
  466.                 pConn.setBusy(true);  
  467.   
  468.                 // 测试此连接是否可用  
  469.   
  470.                 if (!testConnection(conn)) {  
  471.   
  472.                     // 如果此连接不可再用了,则创建一个新的连接,  
  473.   
  474.                     // 并替换此不可用的连接对象,如果创建失败,返回 null  
  475.   
  476.                     try {  
  477.   
  478.                         conn = newConnection();  
  479.   
  480.                     } catch (SQLException e) {  
  481.   
  482.                         System.out.println(" 创建数据库连接失败! " + e.getMessage());  
  483.   
  484.                         return null;  
  485.   
  486.                     }  
  487.   
  488.                     pConn.setConnection(conn);  
  489.   
  490.                 }  
  491.   
  492.                 break// 己经找到一个可用的连接,退出  
  493.   
  494.             }  
  495.   
  496.         }  
  497.   
  498.         return conn;// 返回找到到的可用连接  
  499.   
  500.     }  
  501.   
  502.     /** 
  503.  
  504.      * 测试一个连接是否可用,如果不可用,关掉它并返回 false 
  505.  
  506.      * 否则可用返回 true 
  507.  
  508.      * 
  509.  
  510.      * @param conn 需要测试的数据库连接 
  511.  
  512.      * @return 返回 true 表示此连接可用, false 表示不可用 
  513.  
  514.      */  
  515.   
  516.     private boolean testConnection(Connection conn) {  
  517.   
  518.         try {  
  519.   
  520.             // 判断测试表是否存在  
  521.   
  522.             if (testTable.equals("")) {  
  523.   
  524.                 // 如果测试表为空,试着使用此连接的 setAutoCommit() 方法  
  525.   
  526.                 // 来判断连接否可用(此方法只在部分数据库可用,如果不可用 ,  
  527.   
  528.                 // 抛出异常)。注意:使用测试表的方法更可靠  
  529.   
  530.                 conn.setAutoCommit(true);  
  531.   
  532.             } else {// 有测试表的时候使用测试表测试  
  533.   
  534.                 //check if this connection is valid  
  535.   
  536.                 Statement stmt = conn.createStatement();  
  537.   
  538.                 stmt.execute("select count(*) from " + testTable);  
  539.   
  540.             }  
  541.   
  542.         } catch (SQLException e) {  
  543.   
  544.             // 上面抛出异常,此连接己不可用,关闭它,并返回 false;  
  545.   
  546.             closeConnection(conn);  
  547.   
  548.             return false;  
  549.   
  550.         }  
  551.   
  552.         // 连接可用,返回 true  
  553.   
  554.         return true;  
  555.   
  556.     }  
  557.   
  558.     /** 
  559.  
  560.      * 此函数返回一个数据库连接到连接池中,并把此连接置为空闲。 
  561.  
  562.      * 所有使用连接池获得的数据库连接均应在不使用此连接时返回它。 
  563.  
  564.      * 
  565.  
  566.      * @param 需返回到连接池中的连接对象 
  567.  
  568.      */  
  569.   
  570.     public void returnConnection(Connection conn) {  
  571.   
  572.         // 确保连接池存在,如果连接没有创建(不存在),直接返回  
  573.   
  574.         if (connections == null) {  
  575.   
  576.             System.out.println(" 连接池不存在,无法返回此连接到连接池中 !");  
  577.   
  578.             return;  
  579.   
  580.         }  
  581.   
  582.         PooledConnection pConn = null;  
  583.   
  584.         Enumeration enumerate = connections.elements();  
  585.   
  586.         // 遍历连接池中的所有连接,找到这个要返回的连接对象  
  587.   
  588.         while (enumerate.hasMoreElements()) {  
  589.   
  590.             pConn = (PooledConnection) enumerate.nextElement();  
  591.   
  592.             // 先找到连接池中的要返回的连接对象  
  593.   
  594.             if (conn == pConn.getConnection()) {  
  595.   
  596.                 // 找到了 , 设置此连接为空闲状态  
  597.   
  598.                 pConn.setBusy(false);  
  599.   
  600.                 break;  
  601.   
  602.             }  
  603.   
  604.         }  
  605.   
  606.     }  
  607.   
  608.     /** 
  609.  
  610.      * 刷新连接池中所有的连接对象 
  611.  
  612.      * 
  613.  
  614.      */  
  615.   
  616.     public synchronized void refreshConnections() throws SQLException {  
  617.   
  618.         // 确保连接池己创新存在  
  619.   
  620.         if (connections == null) {  
  621.   
  622.             System.out.println(" 连接池不存在,无法刷新 !");  
  623.   
  624.             return;  
  625.   
  626.         }  
  627.   
  628.         PooledConnection pConn = null;  
  629.   
  630.         Enumeration enumerate = connections.elements();  
  631.   
  632.         while (enumerate.hasMoreElements()) {  
  633.   
  634.             // 获得一个连接对象  
  635.   
  636.             pConn = (PooledConnection) enumerate.nextElement();  
  637.   
  638.             // 如果对象忙则等 5 秒 ,5 秒后直接刷新  
  639.   
  640.             if (pConn.isBusy()) {  
  641.   
  642.                 wait(5000); // 等 5 秒  
  643.   
  644.             }  
  645.   
  646.             // 关闭此连接,用一个新的连接代替它。  
  647.   
  648.             closeConnection(pConn.getConnection());  
  649.   
  650.             pConn.setConnection(newConnection());  
  651.   
  652.             pConn.setBusy(false);  
  653.   
  654.         }  
  655.   
  656.     }  
  657.   
  658.     /** 
  659.  
  660.      * 关闭连接池中所有的连接,并清空连接池。 
  661.  
  662.      */  
  663.   
  664.     public synchronized void closeConnectionPool() throws SQLException {  
  665.   
  666.         // 确保连接池存在,如果不存在,返回  
  667.   
  668.         if (connections == null) {  
  669.   
  670.             System.out.println(" 连接池不存在,无法关闭 !");  
  671.   
  672.             return;  
  673.   
  674.         }  
  675.   
  676.         PooledConnection pConn = null;  
  677.   
  678.         Enumeration enumerate = connections.elements();  
  679.   
  680.         while (enumerate.hasMoreElements()) {  
  681.   
  682.             pConn = (PooledConnection) enumerate.nextElement();  
  683.   
  684.             // 如果忙,等 5 秒  
  685.   
  686.             if (pConn.isBusy()) {  
  687.   
  688.                 wait(5000); // 等 5 秒  
  689.   
  690.             }  
  691.   
  692.             //5 秒后直接关闭它  
  693.   
  694.             closeConnection(pConn.getConnection());  
  695.   
  696.             // 从连接池向量中删除它  
  697.   
  698.             connections.removeElement(pConn);  
  699.   
  700.         }  
  701.   
  702.         // 置连接池为空  
  703.   
  704.         connections = null;  
  705.   
  706.     }  
  707.   
  708.     /** 
  709.  
  710.      * 关闭一个数据库连接 
  711.  
  712.      * 
  713.  
  714.      * @param 需要关闭的数据库连接 
  715.  
  716.      */  
  717.   
  718.     private void closeConnection(Connection conn) {  
  719.   
  720.         try {  
  721.   
  722.             conn.close();  
  723.   
  724.         } catch (SQLException e) {  
  725.   
  726.             System.out.println(" 关闭数据库连接出错: " + e.getMessage());  
  727.   
  728.         }  
  729.   
  730.     }  
  731.   
  732.     /** 
  733.  
  734.      * 使程序等待给定的毫秒数 
  735.  
  736.      * 
  737.  
  738.      * @param 给定的毫秒数 
  739.  
  740.      */  
  741.   
  742.     private void wait(int mSeconds) {  
  743.   
  744.         try {  
  745.   
  746.             Thread.sleep(mSeconds);  
  747.   
  748.         } catch (InterruptedException e) {  
  749.   
  750.         }  
  751.   
  752.     }  
  753.   
  754.     /** 
  755.  
  756.      * 内部使用的用于保存连接池中连接对象的类 
  757.  
  758.      * 此类中有两个成员,一个是数据库的连接,另一个是指示此连接是否 
  759.  
  760.      * 正在使用的标志。 
  761.  
  762.      */  
  763.   
  764.     class PooledConnection {  
  765.   
  766.         Connection connection = null;// 数据库连接  
  767.   
  768.         boolean busy = false// 此连接是否正在使用的标志,默认没有正在使用  
  769.   
  770.         // 构造函数,根据一个 Connection 构告一个 PooledConnection 对象  
  771.   
  772.         public PooledConnection(Connection connection) {  
  773.   
  774.             this.connection = connection;  
  775.   
  776.         }  
  777.   
  778.         // 返回此对象中的连接  
  779.   
  780.         public Connection getConnection() {  
  781.   
  782.             return connection;  
  783.   
  784.         }  
  785.   
  786.         // 设置此对象的,连接  
  787.   
  788.         public void setConnection(Connection connection) {  
  789.   
  790.             this.connection = connection;  
  791.   
  792.         }  
  793.   
  794.         // 获得对象连接是否忙  
  795.   
  796.         public boolean isBusy() {  
  797.   
  798.             return busy;  
  799.   
  800.         }  
  801.   
  802.         // 设置对象的连接正在忙  
  803.   
  804.         public void setBusy(boolean busy) {  
  805.   
  806.             this.busy = busy;  
  807.   
  808.         }  
  809.   
  810.     }  
  811.   
  812. }  
  813.   
  814. /*======================================= 
  815.  
  816.  这个例子是根据POSTGRESQL数据库写的, 
  817.  请用的时候根据实际的数据库调整。 
  818.  
  819.  调用方法如下: 
  820.  
  821.  ① ConnectionPool connPool  
  822.  = new ConnectionPool("org.postgresql.Driver" 
  823.  ,"jdbc:postgresql://dbURI:5432/DBName" 
  824.  ,"postgre" 
  825.  ,"postgre"); 
  826.  
  827.  ② connPool .createPool(); 
  828.  Connection conn = connPool .getConnection(); 
  829.  */  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值