有关数据库连接池的模拟实现——丑九怪
有关数据库连接池的概念以及存在意义
- 概念:数据库连接池,顾名思义,是存放数据库连接的地方,当我们打开了一个数据库连接,就可以将他放在一个特有的“池子”里,当我们要使用的时候,就直接在这个“池子”里寻找链接使用。这个池子同时也实现了对数据库连接的添加,删除,监控等管理
- 存在的意义:首先数据库连接是重要资源,同时大部分对数据库的操作持续时间都不是那么长,没有必要每次操作都打开新的连接,完毕之后在进行关闭(这个是很费时间的)这将会浪费很多资源,所以我们建立数据库连接池。
- 这里暂且不实现多线程的问题,当两个或更多线程同时拥有同一个数据库连接,然后执行了不同的SQL语句。这种情况,我们可以让一个事物只占一个数据库连接,这样虽然使用了比较多的数据库连接资源,却也大大减小了对数据库连接管理的繁琐和复杂性
数据库连接池的大概实现
- 这里我用两个类去实现它,第一个用来创建最基本的连接,并且提供得到连接的方法,如下:
static final List<Connection> connectionPool = new ArrayList<>();
// 创建列表存储连接
// 这里的connection是我们自己创建的connection类
private static String driver;
private static String url;
private static String user;
private static String password;
private static int minCount; // 最大以及最小连接个数
private static int maxCount;
private static int deltaCount; // 当发现数据库连接池中没有连接时,要增加的连接个数
private static long timeOut; // 最长的连接空闲时间
// 初始化数据库连接池,当然要一个properties文件
static void initDatabase(String path) throws Exception {
if (path.equals(null)) {
PropertiesParser.loadProperties("/database.cfg.properties");
} else {
PropertiesParser.loadProperties(path);
}
driver = PropertiesParser.value("driver");
url = PropertiesParser.value("url");
user = PropertiesParser.value("user");
password = PropertiesParser.value("password");
String minCountStr = PropertiesParser.value("minCount");
minCount = minCountStr == null ? 3 : Integer.valueOf(minCountStr);
String maxCountStr = PropertiesParser.value("maxCount");
maxCount = maxCountStr == null ? 50 : Integer.valueOf(maxCountStr);
String deltaCountStr = PropertiesParser.value("deltaCount");
deltaCount = deltaCountStr == null ? 3 : Integer.valueOf(deltaCountStr);
String timeoutStr = PropertiesParser.value("timeout");
timeOut = timeoutStr == null ? 60000 : Long.valueOf(timeoutStr);
try {
Class.forName(driver); // 加载数据库驱动类
createConnection(minCount); // 创建数据库连接池
} catch (Exception e) {
e.printStackTrace();
}
}
创建连接池以及对外提供获取连接的方法
private static boolean createConnection(int count) throws Exception {
int i = 0;
while (connectionPool.size() < maxCount && i < count) {
Connection connection = new Connection();
connection.setConnection(DriverManager.getConnection(url, user, password));
connectionPool.add(connection);
i++;
}
return connectionPool.size() < maxCount;
}
// 对外提供的方法,取得数据库连接
Connection getConnection() throws Exception {
for (int i = 0; i < connectionPool.size(); i++) {
Connection connection = connectionPool.get(i);
if (connection.isUsed()) {
continue;
} else {
return connection;
}
}
// 能走到这里说明已经没有可以用的链接了,所以创建新的链接之后,递归调用自己
if (createConnection(deltaCount)) {
return getConnection();
}
return null; // 返回null说明数据库连接池已经满了
}
下面是有关连接超时的处理,我用一个私有方法处理,用到了之前我做过的工具:滴答滴答(这个工具在我之前的博文里有)(滴答滴答改良版本也可以实现)
private static void scanPool() {
new Didadida() {
@Override
public void doIt() {
for (int index = 0; index < connectionPool.size(); index++) {
Connection connection = connectionPool.get(index);
System.out.println(connection);
// 一个连接没有被使用,并且也已经超时
if (!connection.isUsed()
&& connection.isTimeOut(System.currentTimeMillis(), timeOut)) {
if ((connectionPool.size() - 1) >= minCount) {
connection.getConnection().close();
connectionPool.remove(connection);
}
}
}
}
@Override
public void beforeDida() {
}
@Override
public void afterStopDida() {
}
}.setDelayTime(timeOut).start();
}
- 用另一个类处理连接应该处理的操作(类名称:connection)
private java.sql.Connection connection;
private boolean isUsed;
private long time; // 这个时间用来记录
public Connection() {
isUsed = false;
}
Connection getConnection() {
time = System.currentTimeMillis();
isUsed = true;
return this;
}
这里也提供了强制关闭,判断是否连接超时,执行sql语句的方法,一句对于连接的set方法
// 可以超出的时间和当前时间都由外面提供,算出时间差,判断是否为超时连接(超出时间有默认值)
void ForceClose(long timeOut, long curTime) {
if (!isUsed) { // 如果没有被使用,呢么默认为空闲连接,也就无需关闭
return;
}
long delayTime = curTime - time;
if (timeOut <= delayTime) {
isUsed = false;
}
}
// 判断是否超时
boolean isTimeOut(long timeOut, long curTime) {
long delayTime = curTime - time;
if (timeOut <= delayTime) {
return true;
}
return false;
}
// 执行sql语句的方法
PreparedStatement preparedStatement(String SQLString) throws SQLException {
return connection.prepareStatement(SQLString);
}
void setConnection(java.sql.Connection connection) {
this.connection = connection;
}
这两个类之间的联系:第二个connection类,是第一个数据库连接池中的list成员,我们用自己写的connection将对数据库的连接封装起来,用于判断是否超时等等
总结
- 数据库连接池是对于重要的资源进行有效管理的方法,这本身并没有什么,重要的是我们要有工具思想,大量重复出现的代码要抽象成工具类,关键代码也要做成工具类,这将大大提高我们的注意力,让我们更关心业务本身的逻辑。