Java之中JDBC数据库连接池实现方法

作为一名初级Java程序员都很羡慕Windows ADO ,只需要new Connection 就可以直接从数据库连接池中返回Connection。并且 ADO Connection 是线程安全的,多个线程可以共用一个Connection,所以ASP程序一般都把getConnection 放在 Global.asa 文件中,在 IIS 启动时建立数据库连接。ADO 的Connection 和Result 都有很好的缓冲,并且很容易使用。

其实我们可以自己写一个JDBC数据库连接池。

写JDBC connection pool 的注意事项有:

1. 有一个简单的函数从连接池中得到一个 Connection。

2. close 函数必须将connection 放回 数据库连接池。

3. 当数据库连接池中没有空闲的connection,数据库连接池必须能够自动增加connection 个数。

4. 当数据库连接池中的connection 个数在某一个特别的时间变得很大,但是以后很长时间只用其中一小部分,应该可以自动将多余的connection 关闭掉。

5. 如果可能,应该提供debug 信息报告没有关闭的new Connection 。

如果要new Connection 就可以直接从数据库连接池中返回Connection, 可以这样写( Mediator pattern ) (以下代码中使用了中文全角空格):

代码如下:

  1. public class EasyConnection implements java.sql.Connection{
  2. private Connection m_delegate = null;
  3. public EasyConnection(){
  4. m_delegate = getConnectionFromPool();
  5. }
  6.  public void close(){
  7. putConnectionBackToPool(m_delegate);
  8. }
  9. public PreparedStatement prepareStatement(String sql) throws SQLException{
  10. m_delegate.prepareStatement(sql);
  11. }
  12. //...... other method
  13. }

看来并不难。不过不建议这种写法,因为应该尽量避免使用Java Interface, 关于Java Interface 的缺点我另外再写文章讨论。大家关注的是Connection Pool 的实现方法。下面给出一种实现方法。

  1. import java.sql.*;
  2. import java.lang.reflect.*;
  3. import java.util.*;
  4. import java.io.*;
  5. public class SimpleConnetionPool {
  6. private static LinkedList m_notUsedConnection = new LinkedList();
  7. private static HashSet m_usedUsedConnection = new HashSet();
  8. private static String m_url = "";
  9. private static String m_user = "";
  10. private static String m_password = "";
  11. static final boolean DEBUG = true;
  12. static private long m_lastClearClosedConnection = System.currentTimeMillis();
  13. public static long CHECK_CLOSED_CONNECTION_TIME = 4 * 60 * 60 * 1000; //4 hours
  14. static {
  15. initDriver();
  16. }
  17. private SimpleConnetionPool() {
  18. }
  19. private static void initDriver() {
  20. Driver driver = null;
  21. //load mysql driver
  22. try {
  23. driver = (Driver) Class.forName("com.mysql.jdbc.Driver").newInstance();
  24. installDriver(driver);
  25. } catch (Exception e) {
  26. }
  27. //load postgresql driver
  28. try {
  29. driver = (Driver) Class.forName("org.postgresql.Driver").newInstance();
  30. installDriver(driver);
  31. } catch (Exception e) {
  32. }
  33. }
  34. public static void installDriver(Driver driver) {
  35. try {
  36. DriverManager.registerDriver(driver);
  37. } catch (Exception e) {
  38. e.printStackTrace();
  39. }
  40. }
  41. public static synchronized Connection getConnection() {
  42. clearClosedConnection();
  43. while (m_notUsedConnection.size() > 0) {
  44. try {
  45. ConnectionWrapper wrapper = (ConnectionWrapper) m_notUsedConnection.removeFirst();
  46. if (wrapper.connection.isClosed()) {
  47. continue;
  48. }
  49. m_usedUsedConnection.add(wrapper);
  50. if (DEBUG) {
  51. wrapper.debugInfo = new Throwable("Connection initial statement");
  52. }
  53. return wrapper.connection;
  54. } catch (Exception e) {
  55. }
  56. }
  57. int newCount = getIncreasingConnectionCount();
  58. LinkedList list = new LinkedList();
  59. ConnectionWrapper wrapper = null;
  60. for (int i = 0; i < newCount; i++) {
  61. wrapper = getNewConnection();
  62. if (wrapper != null) {
  63. list.add(wrapper);
  64. }
  65. }
  66. if (list.size() == 0) {
  67. return null;
  68. }
  69. wrapper = (ConnectionWrapper) list.removeFirst();
  70. m_usedUsedConnection.add(wrapper);
  71. m_notUsedConnection.addAll(list);
  72. list.clear();
  73. return wrapper.connection;
  74. }
  75. private static ConnectionWrapper getNewConnection() {
  76. try {
  77. Connection con = DriverManager.getConnection(m_url, m_user, m_password);
  78. ConnectionWrapper wrapper = new ConnectionWrapper(con);
  79. return wrapper;
  80. } catch (Exception e) {
  81. e.printStackTrace();
  82. }
  83. return null;
  84. }
  85. static synchronized void pushConnectionBackToPool(ConnectionWrapper con) {
  86. boolean exist = m_usedUsedConnection.remove(con);
  87. if (exist) {
  88. m_notUsedConnection.addLast(con);
  89. }
  90. }
  91. public static int close() {
  92. int count = 0;
  93. Iterator iterator = m_notUsedConnection.iterator();
  94. while (iterator.hasNext()) {
  95. try {
  96. ( (ConnectionWrapper) iterator.next()).close();
  97. count++;
  98. } catch (Exception e) {
  99. }
  100. }
  101. m_notUsedConnection.clear();
  102. iterator = m_usedUsedConnection.iterator();
  103. while (iterator.hasNext()) {
  104. try {
  105. ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
  106. wrapper.close();
  107. if (DEBUG) {
  108. wrapper.debugInfo.printStackTrace();
  109. }
  110. count++;
  111. } catch (Exception e) {
  112. }
  113. }
  114. m_usedUsedConnection.clear();
  115. return count;
  116. }
  117. private static void clearClosedConnection() {
  118. long time = System.currentTimeMillis();
  119. //sometimes user change system time,just return
  120. if (time < m_lastClearClosedConnection) {
  121. time = m_lastClearClosedConnection;
  122. return;
  123. }
  124. //no need check very often
  125. if (time - m_lastClearClosedConnection < CHECK_CLOSED_CONNECTION_TIME) {
  126. return;
  127. }
  128. m_lastClearClosedConnection = time;
  129. //begin check
  130. Iterator iterator = m_notUsedConnection.iterator();
  131. while (iterator.hasNext()) {
  132. ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
  133. try {
  134. if (wrapper.connection.isClosed()) {
  135. iterator.remove();
  136. }
  137. } catch (Exception e) {
  138. iterator.remove();
  139. if (DEBUG) {
  140. System.out.println("connection is closed, this connection initial StackTrace");
  141. wrapper.debugInfo.printStackTrace();
  142. }
  143. }
  144. }
  145. //make connection pool size smaller if too big
  146. int decrease = getDecreasingConnectionCount();
  147. if (m_notUsedConnection.size() < decrease) {
  148. return;
  149. }
  150. while (decrease-- > 0) {
  151. ConnectionWrapper wrapper = (ConnectionWrapper) m_notUsedConnection.removeFirst();
  152. try {
  153. wrapper.connection.close();
  154. } catch (Exception e) {
  155. }
  156. }
  157. }
  158. /**
  159. * get increasing connection count, not just add 1 connection
  160. * @return count
  161. */
  162. public static int getIncreasingConnectionCount() {
  163. int count = 1;
  164. int current = getConnectionCount();
  165. count = current / 4;
  166. if (count < 1) {
  167. count = 1;
  168. }
  169. return count;
  170. }
  171. /**
  172. * get decreasing connection count, not just remove 1 connection
  173. * @return count
  174. */
  175. public static int getDecreasingConnectionCount() {
  176. int count = 0;
  177. int current = getConnectionCount();
  178. if (current < 10) {
  179. return 0;
  180. }
  181. return current / 3;
  182. }
  183. public synchronized static void printDebugMsg() {
  184. printDebugMsg(System.out);
  185. }
  186. public synchronized static void printDebugMsg(PrintStream out) {
  187. if (DEBUG == false) {
  188. return;
  189. }
  190. StringBuffer msg = new StringBuffer();
  191. msg.append("debug message in " + SimpleConnetionPool.class.getName());
  192. msg.append("\r\n");
  193. msg.append("total count is connection pool: " + getConnectionCount());
  194. msg.append("\r\n");
  195. msg.append("not used connection count: " + getNotUsedConnectionCount());
  196. msg.append("\r\n");
  197. msg.append("used connection, count: " + getUsedConnectionCount());
  198. out.println(msg);
  199. Iterator iterator = m_usedUsedConnection.iterator();
  200. while (iterator.hasNext()) {
  201. ConnectionWrapper wrapper = (ConnectionWrapper) iterator.next();
  202. wrapper.debugInfo.printStackTrace(out);
  203. }
  204. out.println();
  205. }
  206. public static synchronized int getNotUsedConnectionCount() {
  207. return m_notUsedConnection.size();
  208. }
  209. public static synchronized int getUsedConnectionCount() {
  210. return m_usedUsedConnection.size();
  211. }
  212. public static synchronized int getConnectionCount() {
  213. return m_notUsedConnection.size() + m_usedUsedConnection.size();
  214. }
  215. public static String getUrl() {
  216. return m_url;
  217. }
  218. public static void setUrl(String url) {
  219. if (url == null) {
  220. return;
  221. }
  222. m_url = url.trim();
  223. }
  224. public static String getUser() {
  225. return m_user;
  226. }
  227. public static void setUser(String user) {
  228. if (user == null) {
  229. return;
  230. }
  231. m_user = user.trim();
  232. }
  233. public static String getPassword() {
  234. return m_password;
  235. }
  236. public static void setPassword(String password) {
  237. if (password == null) {
  238. return;
  239. }
  240. m_password = password.trim();
  241. }
  242. }
  243. class ConnectionWrapper implements InvocationHandler {
  244. private final static String CLOSE_METHOD_NAME = "close";
  245. public Connection connection = null;
  246. private Connection m_originConnection = null;
  247. public long lastAccessTime = System.currentTimeMillis();
  248. Throwable debugInfo = new Throwable("Connection initial statement");
  249. ConnectionWrapper(Connection con) {
  250. this.connection = (Connection) Proxy.newProxyInstance(
  251. con.getClass().getClassLoader(),
  252. con.getClass().getInterfaces(), this);
  253. m_originConnection = con;
  254. }
  255. void close() throws SQLException {
  256. m_originConnection.close();
  257. }
  258. public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
  259. Object obj = null;
  260. if (CLOSE_METHOD_NAME.equals(m.getName())) {
  261. SimpleConnetionPool.pushConnectionBackToPool(this);
  262. }
  263. else {
  264. obj = m.invoke(m_originConnection, args);
  265. }
  266. lastAccessTime = System.currentTimeMillis();
  267. return obj;
  268. }
  269. }

使用方法

  1. public class TestConnectionPool{
  2. public static void main(String[] args) {
  3. SimpleConnetionPool.setUrl(DBTools.getDatabaseUrl());
  4. SimpleConnetionPool.setUser(DBTools.getDatabaseUserName());
  5. SimpleConnetionPool.setPassword(DBTools.getDatabasePassword());
  6. Connection con = SimpleConnetionPool.getConnection();
  7. Connection con1 = SimpleConnetionPool.getConnection();
  8. Connection con2 = SimpleConnetionPool.getConnection();
  9. //do something with con ...
  10. try {
  11. con.close();
  12. } catch (Exception e) {}
  13. try {
  14. con1.close();
  15. } catch (Exception e) {}
  16. try {
  17. con2.close();
  18. } catch (Exception e) {}
  19. con = SimpleConnetionPool.getConnection();
  20. con1 = SimpleConnetionPool.getConnection();
  21. try {
  22. con1.close();
  23. } catch (Exception e) {}
  24. con2 = SimpleConnetionPool.getConnection();
  25. SimpleConnetionPool.printDebugMsg();
  26. }
  27. }

最后一点就是
运行测试程序后打印JDBC数据库连接池中Connection状态,以及正在使用的没有关闭Connection信息。

end,动手练习一下。

时间: 2024-08-25 03:34:22

Java之中JDBC数据库连接池实现方法的相关文章

Java使用C3P0数据库连接池的方法

因不想继续在PHP花更多的时间,所以入手JAVA,从零学至此利用C3P0实现JDBC连接池,提高数据库处理的能力,加快性能.一般情况下,操作数据库的过程都是连接数据库->执行操作->释放资源.这样的操作在一般情况下是没什么问题的,但是如果出现频繁的操作就会造成一些性能上的问题,因为频繁打开关闭连接是个耗时的操作,所以就Java目前用得最多的是C3P0实现JDBC连接池,由于刚入手Java希望各位小伙伴海涵. 使用C3P0先得引包:c3p0-0.9.1.2.jar和mysql-connector

在JAVA中实现JDBC数据库连接池

[转自e良师益友网]Java程序员都很羡慕Windows ADO ,只需要new Connection 就可以直接从数据库连接池中返回Connection.并且 ADO Connection 是线程安全的,多个线程可以共用一个Connection,所以ASP程序一般都把getConnection 放在 Global.asa 文件中,在 IIS 启动时建立数据库连接.ADO 的Connection 和Result 都有很好的缓冲,并且很容易使用.推荐学习尚硅谷JDBC视频教程. 其实我们可以自己写

JAVA JDBC 数据库连接池

1.1 JDBC数据库连接池的必要性 在使用开发基于数据库的web程序时,传统的模式基本是按以下步骤: 在主程序(如servlet.beans)中建立数据库连接 进行sql操作 断开数据库连接 这种模式开发,存在的问题: 普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s-1s的时间).需要数据库连接的时候,就向数据库要求一个,执行完成后再断开连接.这样的方式将会消耗大量的资

【Java123】JDBC数据库连接池建立

需求场景:多SQL任务多线程并行执行 解决方案:建立JDBC数据库连接池,将线程与连接一对一绑定 https://www.cnblogs.com/panxuejun/p/5920845.html https://blog.csdn.net/sunbo94/article/details/79409298 https://www.cnblogs.com/hanfight/p/4701763.html 需求场景:单次查询的ResultSet数据量过大 解决方案:分页查询+多线程任务执行 https:

java攻城狮之路--复习JDBC(数据库连接池 : C3P0、DBCP)

复习数据库连接池 : C3P0.DBCP 1.数据库连接池技术的优点: •资源重用: 由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销.在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性. •更快的系统反应速度: 数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用.此时连接的初始化工作均已完成.对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间. •新的资源分配手段: 对于多应用共享同

JDBC数据库连接池技术

在JDBC中,获得连接或释放资源是非常消耗系统资源的两个过程,为了解决此类性能问题,通常采用连接池技术,来共享连接.这样我们就不需要每次都创建连接.释放连接了,这些操作都交给了连接池. 用池的概念来管理Connection,这样可以重复使用Connection.有了连接池以后就不用自己亲自创建连接而是通过连接池来获得Connection对象.当使用完Connection之后,调用Connection的close()方法不是真的将连接关闭,而是把Connection归还给连接池.连接池就可以继续保

JDBC 数据库连接池 小结

原文:http://www.cnblogs.com/lihuiyy/archive/2012/02/14/2351768.html 当对数据库的访问不是很频繁时,可以在每次访问数据库时建立一个连接,用完之后关闭.但是,对于一个复杂的数据库应用,频繁的建立.关闭连接,会极大的减低系统性能,造成瓶颈.所以可以使用数据库连接池来达到连接资源的共享,使得对于数据库的连接可以使高效.安全的复用. 1.通过一个小测试来了解数据库连接池的原理 (1)创建一个属性文件  dbpool.properties 1

Java语言链接数据库连接池配置的两种技巧

对于对性能要求较高的企业级应用来说用JDBC连接数据库的方式一般满足不了要求,这时就要用到数据库连接池了. 数据库连接池负责分配.管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而再不是重新建立一个:释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏.这项技术能明显提高对数据库操作的性能. 配置数据库连接池的两种方法 Tomcat服务器配置步骤: 1.把下面这段代码粘贴D:\apache-tomcat-6.0.14\conf contex

编写自己的jdbc数据库连接池

1.为什么要使用数据库连接池 在使用jdbc的一般开发中,每次都要从数据库获取连接,典例的查询的做法如下: Connection conn = null; PreparedStatement st = null; ResultSet rs = null; try{ Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/tes