一个效果不错的Java数据库连接池

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

你可能感兴趣的:(JAVA(J2EE))