连接池技术
简介
前面写了 JDBC 技术,为了在 java 应用中访问数据库,就需要使用 JDBC,但是每次建立 Connection 实例是需要开销的,每次 SQL 操作都要进行 Connection 的开关,是很消耗资源,多次或并发进行,开销更大,且属于无意义浪费,所以连接池技术
出现。
就是预先准备好多个 Connection 连接实例,这些开销是必须要的,但是在每次使用时只允许一个线程调用,结束后并不关闭连接,而是将其放到连接池中设为公共,等待下一次调用。
结构介绍
完整的连接池是由连接池和连接池管理
两部分组成,这样我们并不需要直接对连接池进行操作,我们只需要调用管理类的方法,因为我们使用者关注的应该是业务逻辑,只需要拿取 Connection 实例以及通过管理类设置相关连接池参数即可。
连接池结构
简单来说就是多个 Connection 实例,存储在容器中,有相关的属性,并且对外提供设置方法。
核心思想就是对 Connection 连接实例的复用。
并且提供了安全性,稳定性,便利性。
也许多个不同的数据库厂商的性质不同决定了连接池的实现有不同,但是原理都是池结构,只是开发者的实现方式不一样。
常见属性
- initSize:初始化时的连接数。
- maxSize:连接池中最大连接数。
- minSize:连接池中最小连接数。
- size:连接池中连接数。
- maxTime:连接空闲超时时间。
- name:连接池名称(可选),一般场景是一个管理类管理多个不同连接池用于区分的。
剩下的属性可能需要自己按照具体场景添加或者在管理类添加更好。
在实际业务场景中还是流行使用业界流行的连接池框架,每个框架特色不同,根据实际业务来取舍。
但是我们不能只是使用,我们当然要来理解一下别人优秀框架的基本原理和优秀的框架设计,帮助我们进行框架功能分析,即使理解不是很透彻,但是需要有这样一个意识,只有看别人优秀的作品才能吸取自己没有的优点,从而反思自己。
连接池实现
这里就对连接池进行一个简陋的实现,主要是理解流程。
![](/qrcode.jpg)
不考虑在多线程情况下进行。
// 简陋的数据库连接池
public class JdbcPool {
private static final int poolInitNum = 10; // 初始化默认池大小为10
private LinkedList<Connection> pool = null; // 用链表形式作为连接池的数据结构,方便存取
private static final int poolMax = 20;
private final String user;
private final String pwd;
private final String url;
public void getPoolNum(){
System.out.println("连接池:-->"+pool+"内连接数量:"+pool.size());
}
public JdbcPool(String user, String pwd, String url) {
this.user = user;
this.pwd = pwd;
this.url = url;
}
/**
* 创建连接池 连接池的数据结构用链表来表示
*/
public void createPool() throws SQLException {
// 保证连接池没有被创建
if (pool != null) {
return;
}
// 创建连接池
pool = new LinkedList<>();
initConnection();
}
/**
* 初始化连接池
*/
private void initConnection() throws SQLException {
for (int i = 0; i < poolInitNum; i++) {
pool.add(newConnection());
}
}
/**
* 创建一个新的Connection并返回
*
* @return Connection
*/
private Connection newConnection() throws SQLException {
return DriverManager.getConnection(url, user, pwd);
}
/**
* 从连接池中获取一个连接实例
*
* @return 连接池中第一个connection
*/
public Connection getConnection() throws SQLException {
if (pool.size() > 0) {
return pool.removeFirst();
} else {
return newConnection();
}
}
/**
* 放回一个connection至连接池尾部,如果池中连接数达到poolMax则不能在放入连接池,必须销毁该connection
*
* @param conn connection
*/
public void returnConnection(Connection conn) throws SQLException {
if (pool!=null){
if (pool.size() < poolMax) {
pool.add(conn);
} else {
conn.close();
}
}else {
conn.close();
}
}
/**
* 关闭连接池
*/
public void closePool() throws SQLException {
// 断开所有Connection
for (int i = pool.size() - 1; i >= 0; i--) {
Connection connection = pool.remove(i);
connection.close();
}
pool = null;
}
}
复制代码
以上主要是看看基本的属性,以及 Connection 连接实例在容器中的存取,回收方法。
篇幅原因只能进行简写,并没有将其按规范进行层层封装了,正确的连接池肯定不是我这样写的,应该按照连接池的基本思想,考虑多个场景进行详细设计并实现。
连接池管理类实现
// 数据库连接池工具类 保证唯一一个数据库连接池对象,使用抽象类保证不能实例化
public abstract class JdbcPoolUtil {
private static String user = "";
private static String pwd = "";
private static String url = "";
private static JdbcPool pool = null;
static {
Properties properties = new Properties();// 配置文件工具类,获取jdbc.properties文件的数据库连接配置信息
InputStream is = DbUtil.class.getClassLoader().getResourceAsStream("jdbc_config.properties");
System.out.println(is);
try {
properties.load(is);
user = properties.getProperty("user");
pwd = properties.getProperty("pwd");
url = properties.getProperty("url");
} catch (IOException e) {
e.printStackTrace();
}finally {
try{
if (is!=null){
is.close();
System.out.println("数据库连接参数读取完毕!");
}else {
System.out.println("数据库连接失败!");
}
}catch (IOException e){
e.printStackTrace();
}finally {
System.out.println(url);
System.out.println(user);
System.out.println(pwd);
}
}
}
/**
* 得到数据库连接池对象
* @return JdbcPool
*/
public static JdbcPool getJdbcPool() {
if (pool == null) {
pool = new JdbcPool(user, pwd, url);
try {
pool.createPool();
} catch (SQLException e) {
e.printStackTrace();
}
}
return pool;
}
/**
* 关闭数据库连接池
*/
public static void closeJdbcPool(){
try {
pool.closePool();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
复制代码
上面管理类,只是极为简略的实现,严格来说并不能称为管理类,管理类应该注重对连接池和里面连接实例的管理,更甚者可能管理多个不同的连接池。所以需要学到后面框架,先熟练使用,巩固基础,再尝试看博客讲解,分析其中设计的每个模块关系,梳理整体架构,这样我们的思路才会更加清晰和完善。
小结
因为只是简单的实现基本效果,没有考虑动态代理、多线程安全、超时策略、资源回收策略等详细功能,同时也是自己学艺不精,只学到了浅层次,对于这些都有想法但是具体实现就摆了。
可能在复习完 Java 所有基础和面试题之后,秋招一个稍好点的公司,再去深入学习原理。
保持不骄不躁,时常反思的心态,一直学下去。