这次写数据库连接池主要想解决的还是servlet访问数据库获取数据的稳定性问题,于是便研究了一下,下面来讲一讲如何用java来写一个适合自己用的数据库连接池。这个东西大家并不陌生,通过使用数据连接池我们能够更好地控制程序和数据库之间建立的连接,减小数据库访问压力,也便于管理连接,提高了利用率和工作性能。
设计数据库连接池,个人认为应该注意以下几点:
1、能够控制连接池的大小
2、有一个统一的接口用于获得连接
3、使用后的连接要有一个接口能够接受并处理掉
4、连接池要有自我维护能力,比如说暂时提高连接池大小以应对可能的连接小高潮,或者处理多余的连接
ok,我们先确定连接池的数据结构:
1 public class SimpleConnetionPool { 2 private static LinkedList m_notUsedConnection = new LinkedList(); 3 private static HashSet m_usedUsedConnection = new HashSet(); 4 private static String m_url = ""; 5 private static String m_user = ""; 6 private static String m_password = ""; 7 private static int m_maxConnect = 3; 8 static final boolean DEBUG = false; 9 static private long m_lastClearClosedConnection = System 10 .currentTimeMillis(); 11 public static long CHECK_CLOSED_CONNECTION_TIME = 5000; // 5秒 12 }
然后我们看看数据库连接池的核心部分,首先是清除连接池中多余的连接。我们每隔一段时间就对连接池中的所有连接进行检查,第一轮循环判断这些链接是否已经关闭,如果关闭了则直接移除它们,第二轮循环则是根据目前规定的最大数量裁撤空闲连接。
1 private static void clearClosedConnection() { 2 long time = System.currentTimeMillis(); 3 4 // 时间不合理,没有必要检查 5 if (time < m_lastClearClosedConnection) { 6 time = m_lastClearClosedConnection; 7 return; 8 } 9 10 // 时间太短,没有必要检查 11 if (time - m_lastClearClosedConnection < CHECK_CLOSED_CONNECTION_TIME) { 12 return; 13 } 14 15 m_lastClearClosedConnection = time; 16 17 // 开始检查没有使用的Connection 18 Iterator iterator = m_notUsedConnection.iterator(); 19 while (iterator.hasNext()) { 20 Connection con = (Connection) iterator.next(); 21 22 try { 23 if (con.isClosed()) { 24 iterator.remove(); 25 } 26 } catch (SQLException e) { 27 iterator.remove(); 28 29 if (DEBUG) { 30 System.out.println("问题连接已断开"); 31 } 32 } 33 } 34 35 // 清除多余的Connection 36 int decrease = getDecreasingConnectionCount(); 37 38 while (decrease > 0 && m_notUsedConnection.size() > 0) { 39 Connection con = (Connection) m_notUsedConnection.removeFirst(); 40 41 try { 42 con.close(); 43 } catch (SQLException e) { 44 45 } 46 47 decrease--; 48 } 49 }
接下来我们看一下申请一个新的连接是如何进行的,首先我们先调用之前的清理器来清除多余的连接和无法使用的连接,之后在空闲连接中寻找是否有可是的连接,如果有符合的则直接分配出去,但是要是没找到的话该怎么办呢?
这时候我们就需要建立新的连接来提供了,建立新的连接后我们将其中一个分配出去,剩下的加入到空闲连接中去等待分配就可以了。
1 public static synchronized Connection getConnection() { 2 // 关闭清除多余的连接 3 clearClosedConnection(); 4 5 // 输出当前总连接数 6 if(DEBUG) 7 System.out.println("当前总连接数:" + getConnectionCount()); 8 9 // 寻找空闲的连接 10 while (m_notUsedConnection.size() > 0) { 11 try { 12 Connection con = (Connection) m_notUsedConnection.removeFirst(); 13 14 if (con.isClosed()) { 15 continue; 16 } 17 18 m_usedUsedConnection.add(con); 19 if (DEBUG) { 20 // System.out.println("连接初始化成功"); 21 } 22 return con; 23 } catch (SQLException e) { 24 } 25 } 26 27 // 没有找到,建立一些新的连接以供使用 28 int newCount = getIncreasingConnectionCount(); 29 LinkedList list = new LinkedList(); 30 Connection con = null; 31 32 for (int i = 0; i < newCount; i++) { 33 con = getNewConnection(); 34 if (con != null) { 35 list.add(con); 36 } 37 } 38 39 // 没有成功建立连接,访问失败 40 if (list.size() == 0) 41 return null; 42 43 // 成功建立连接,使用的加入used队列,剩下的加入notUsed队列 44 con = (Connection) list.removeFirst(); 45 m_usedUsedConnection.add(con); 46 m_notUsedConnection.addAll(list); 47 list.clear(); 48 49 return con; 50 }
根据之前总结的我们还需要一个,就是交还连接了,这个很简单,把占用中的链接移出来放到空闲连接里就可以了~很简单吧~
1 static synchronized void pushConnectionBackToPool(Connection con) { 2 boolean exist = m_usedUsedConnection.remove(con); 3 if (exist) { 4 m_notUsedConnection.addLast(con); 5 } 6 }
这就是这个数据连接池的核心部分了,现在我们来看整套代码就容易多了,其实需要注意的就是刚才说的那些:
1 package cn.com.css.cas.jdbc; 2 3 import java.sql.Connection; 4 import java.sql.Driver; 5 import java.sql.DriverManager; 6 import java.sql.SQLException; 7 import java.util.HashSet; 8 import java.util.Iterator; 9 import java.util.LinkedList; 10 11 /** 12 * JDBC数据库连接池 13 * 14 * @author Woud 15 * 16 */ 17 public class SimpleConnetionPool { 18 private static LinkedList m_notUsedConnection = new LinkedList(); 19 private static HashSet m_usedUsedConnection = new HashSet(); 20 private static String m_url = ""; 21 private static String m_user = ""; 22 private static String m_password = ""; 23 private static int m_maxConnect = 3; 24 static final boolean DEBUG = false; 25 static private long m_lastClearClosedConnection = System 26 .currentTimeMillis(); 27 public static long CHECK_CLOSED_CONNECTION_TIME = 5000; // 5秒 28 29 static { 30 try { 31 initDriver(); 32 } catch (InstantiationException e) { 33 // TODO Auto-generated catch block 34 e.printStackTrace(); 35 } catch (IllegalAccessException e) { 36 // TODO Auto-generated catch block 37 e.printStackTrace(); 38 } catch (ClassNotFoundException e) { 39 // TODO Auto-generated catch block 40 e.printStackTrace(); 41 } 42 } 43 44 public SimpleConnetionPool(String url, String user, String password) { 45 m_url = url; 46 m_user = user; 47 m_password = password; 48 } 49 50 private static void initDriver() throws InstantiationException, 51 IllegalAccessException, ClassNotFoundException { 52 Driver driver = null; 53 54 // 读取MySql的Driver 55 driver = (Driver) Class.forName("com.mysql.jdbc.Driver").newInstance(); 56 installDriver(driver); 57 58 /* 59 * // 读取postgresql的driver driver = (Driver) 60 * Class.forName("org.postgresql.Driver").newInstance(); 61 * installDriver(driver); 62 */ 63 64 } 65 66 public static void installDriver(Driver driver) { 67 try { 68 DriverManager.registerDriver(driver); 69 } catch (SQLException e) { 70 // TODO Auto-generated catch block 71 e.printStackTrace(); 72 } 73 } 74 75 public static synchronized Connection getConnection() { 76 // 关闭清除多余的连接 77 clearClosedConnection(); 78 79 // 输出当前总连接数 80 if(DEBUG) 81 System.out.println("当前总连接数:" + getConnectionCount()); 82 83 // 寻找空闲的连接 84 while (m_notUsedConnection.size() > 0) { 85 try { 86 Connection con = (Connection) m_notUsedConnection.removeFirst(); 87 88 if (con.isClosed()) { 89 continue; 90 } 91 92 m_usedUsedConnection.add(con); 93 if (DEBUG) { 94 // System.out.println("连接初始化成功"); 95 } 96 return con; 97 } catch (SQLException e) { 98 } 99 } 100 101 // 没有找到,建立一些新的连接以供使用 102 int newCount = getIncreasingConnectionCount(); 103 LinkedList list = new LinkedList(); 104 Connection con = null; 105 106 for (int i = 0; i < newCount; i++) { 107 con = getNewConnection(); 108 if (con != null) { 109 list.add(con); 110 } 111 } 112 113 // 没有成功建立连接,访问失败 114 if (list.size() == 0) 115 return null; 116 117 // 成功建立连接,使用的加入used队列,剩下的加入notUsed队列 118 con = (Connection) list.removeFirst(); 119 m_usedUsedConnection.add(con); 120 m_notUsedConnection.addAll(list); 121 list.clear(); 122 123 return con; 124 } 125 126 public static Connection getNewConnection() { 127 try { 128 Connection con = DriverManager.getConnection(m_url, m_user, 129 m_password); 130 return con; 131 } catch (SQLException e) { 132 // TODO Auto-generated catch block 133 e.printStackTrace(); 134 } 135 136 return null; 137 } 138 139 static synchronized void pushConnectionBackToPool(Connection con) { 140 boolean exist = m_usedUsedConnection.remove(con); 141 if (exist) { 142 m_notUsedConnection.addLast(con); 143 } 144 } 145 146 public static int close() { 147 int count = 0; 148 149 Iterator iterator = m_notUsedConnection.iterator(); 150 while (iterator.hasNext()) { 151 try { 152 ((Connection) iterator.next()).close(); 153 count++; 154 } catch (SQLException e) { 155 // TODO Auto-generated catch block 156 e.printStackTrace(); 157 } 158 } 159 m_notUsedConnection.clear(); 160 161 iterator = m_usedUsedConnection.iterator(); 162 while (iterator.hasNext()) { 163 try { 164 ((Connection) iterator.next()).close(); 165 } catch (SQLException e) { 166 // TODO Auto-generated catch block 167 e.printStackTrace(); 168 } 169 } 170 m_usedUsedConnection.clear(); 171 172 return count; 173 } 174 175 private static void clearClosedConnection() { 176 long time = System.currentTimeMillis(); 177 178 // 时间不合理,没有必要检查 179 if (time < m_lastClearClosedConnection) { 180 time = m_lastClearClosedConnection; 181 return; 182 } 183 184 // 时间太短,没有必要检查 185 if (time - m_lastClearClosedConnection < CHECK_CLOSED_CONNECTION_TIME) { 186 return; 187 } 188 189 m_lastClearClosedConnection = time; 190 191 // 开始检查没有使用的Connection 192 Iterator iterator = m_notUsedConnection.iterator(); 193 while (iterator.hasNext()) { 194 Connection con = (Connection) iterator.next(); 195 196 try { 197 if (con.isClosed()) { 198 iterator.remove(); 199 } 200 } catch (SQLException e) { 201 iterator.remove(); 202 203 if (DEBUG) { 204 System.out.println("问题连接已断开"); 205 } 206 } 207 } 208 209 // 清除多余的Connection 210 int decrease = getDecreasingConnectionCount(); 211 212 while (decrease > 0 && m_notUsedConnection.size() > 0) { 213 Connection con = (Connection) m_notUsedConnection.removeFirst(); 214 215 try { 216 con.close(); 217 } catch (SQLException e) { 218 219 } 220 221 decrease--; 222 } 223 } 224 225 public static int getIncreasingConnectionCount() { 226 int count = 1; 227 count = getConnectionCount() / 4; 228 229 if (count < 1) 230 count = 1; 231 232 return count; 233 } 234 235 public static int getDecreasingConnectionCount() { 236 int count = 0; 237 238 if (getConnectionCount() > m_maxConnect) { 239 count = getConnectionCount() - m_maxConnect; 240 } 241 242 return count; 243 } 244 245 public static synchronized int getNotUsedConnectionCount() { 246 return m_notUsedConnection.size(); 247 } 248 249 public static synchronized int getUsedConnectionCount() { 250 return m_usedUsedConnection.size(); 251 } 252 253 public static synchronized int getConnectionCount() { 254 return m_notUsedConnection.size() + m_usedUsedConnection.size(); 255 } 256 257 }
我们做好了这个连接池之后怎么用呢,这很简单,我们用Singleton模式做一个连接池管理器,然后对接口进行简单的封装后就可以进行使用了,管理器调用连接池的getconnection接口获得connect后和数据库建立连接,运行sql后交还connect并把结果反馈回来就可以了。