有文章说第一位设计者是去游泳的时候,进去穿共用的拖鞋,出来的时候还回去。因此称之为池,顾名思义,连接池就是存放多个连接(拖鞋)的池了。当服务需要的时候到这里取一个连接,用完还回来。
哦对,还等讲一下为什么要还,每一次都买一双,用完就扔掉(你可别说带回家,共用有香港脚啥的,哥打死你,记得多洗脚),拆包装费时间,丢掉浪费钱吧。同理,频繁获取连接是会耗费一定的开销,在面对大量请求时,就需要考虑连接池的设计了。
我们需要连接的时候我们都是拿着账号密码去登录连接到数据库,用完了直接将connection关闭,也就是调用connection接口的close方法,至于怎么close我们这里就不讲了,暂且也不需要知道。
这里连接的是oracle数据库,今天的主题和什么数据库其实关系不大。所以我就随便拉了个之前写的工具来看下了。(哎,懒呐)
/**
*
* @author T_Antry
* @describe 数据库连接的工具
* Oracle0716
* 2020年7月16日
*/
public class DBUtils {
public static String driver;
public static String url;
public static String user;
public static String password;
static {
Properties p = new Properties();//实例
try {
//获得类的位置
String path = URLDecoder.decode(DBUtils.class.getClassLoader().getResource("").getPath());
path += "conf/dbconfig.properties";
p.load(new FileInputStream(path));
driver = p.getProperty("driver");
url = p.getProperty("url");
user = p.getProperty("user");
password = p.getProperty("password");
Class.forName(driver);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
*
* @return 返回连接对象
* @describe 获取数据库连接
*/
public static Connection getConn()
{
Connection c = null;
try {
c = DriverManager.getConnection(url, user, password);
System.out.println("连接成功");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return c;
}
/**
*
* @param pstmt
* @param rs
* @describe 关闭
*/
public static void close(PreparedStatement pstmt,ResultSet rs,Connection conn)
{
if(pstmt!=null)
{
try {
pstmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(rs!=null)
{
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn!=null)
{
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
这里面静态块就是读取properties配置参数,然后检查一下驱动。
然后就是getConn()的方法用于其他类调用拿取连接。
这里省去数据库的增删改查,这里边也有很多设计方法,DAO啊,这个这边用不用和连接池也没关系,所以就不讲了,我想应该可以明白。(有疑问可以私信哈,毕竟我也不太会讲)
/**
* @author T_Antry
* @describe 测试
* T_Antry-Hospital
* 2020年8月13日
*/
public class Test1 {
public static void main(String[] args) {
Connection conn = DBUtils.getConn();//获取连接
//--------------------
//这里面执行增删改查
//--------------------
conn.close();
}
}
既然要变成池,那首先,我们在静态块在原本的基础上就要先new上适量的连接,本次案例就是简单的获取10个连接存放到集合中。随后改写下获取连接的方法,改成从集合里拿走连接。当遇到集合里没有的时候再获取几个连接放到集合中。下面先看一下更改完之后的工具类,至于归还的事一会儿再说哈。
/**
*
* @author T_Antry
* @describe 数据库连接的工具
* Oracle0716
* 2020年7月16日
*/
@SuppressWarnings("deprecation")
public class DBUtils {
public static String driver;
public static String url;
public static String user;
public static String password;
private static ArrayList<Connection> list = new ArrayList<Connection>();
static {
Properties p = new Properties();//实例
try {
//获得类的位置
String path = URLDecoder.decode(DBUtils.class.getClassLoader().getResource("").getPath());
path += "conf/dbconfig.properties";
p.load(new FileInputStream(path));
driver = p.getProperty("driver");
url = p.getProperty("url");
user = p.getProperty("user");
password = p.getProperty("password");
Class.forName(driver);
for (int i = 0; i < 10; i++) {
Connection c = DriverManager.getConnection(url, user, password);
System.out.println("连接成功");
list.add(c);
}
System.out.println("首次初始化"+list.size()+"个连接");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
*
* @return 返回连接对象
* @describe 获取数据库连接
*/
public static Connection getConn()
{
if(list.isEmpty())
{
for (int i = 0; i < 5; i++) {
Connection c;
try {
c = DriverManager.getConnection(url, user, password);
System.out.println("连接成功");
list.add(c);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("增加5个连接,现有"+list.size()+"个连接");
}
Connection c = list.remove(0);
c = new MyConnection(c, list);//这个MyConnection是我们等下要说的装饰者类,现在你可以直接return c;不用这一行
return c;
}
/**
*
* @param pstmt
* @param rs
* @describe 关闭
*/
public static void close(PreparedStatement pstmt,ResultSet rs,Connection conn)
{
if(pstmt!=null)
{
try {
pstmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(rs!=null)
{
try {
rs.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(conn!=null)
{
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
那接下来就要讲一下归还了。我们前面讲到了,正常情况下我们用完连接就关闭了。那么我们现在不关闭,只要把它归还到集合中给下一个用户用。
/**
*
* @param conn
* @describe 归还
*/
public static void close(Connection conn)
{
list.add(conn);
}
到这里其实连接池就可以实现了,那么大家可以发现,这样你可能还得去修改很多代码,因为你原本使用conn.close()就搞定了。
怎么说这个装饰者呢,可能我也讲不好,我就从我的理解去说一下这个装饰者模式。
假设
1、‘C’ 是一个接口
2、‘B’ 是实现 ‘C’ 接口的一个实现类
需求
要去更改B实现类中一个方法。
此时
这个B恰好又是源码,我们不想去修改源码怎么办?(
又或者是底层写好的,不能轻易更改)
修改源码当然是可以达到这个效果。
所以
我们再写一个实现类 ‘A’ 它也去实现 ‘C’ 接口,同时,我们把实现类 ‘B’ 传入 实现类 ‘A’ 中,其它需要的参数也传入。例如本次要讲的连接池,我们就需要再传入一个集合,我们把连接回收到这个集合中去。
参数传好了,然后呢?
填充方法,把你用到的方法找出来填充一下,如果功能不改变,直接用传入的实现类 ‘B’ 的方法填充就好,如果要修改的就自由发挥了。
/**
*
* @author T_Antry
* @describe 我的连接,伪装者模式
* T_Antry-Hospital
* 2020年8月27日
*/
public class MyConnection implements Connection{
private Connection conn = null;
private ArrayList<Connection> list = null;
public MyConnection(Connection conn,ArrayList<Connection> list) {
this.conn = conn;
this.list = list;
}
@Override
public void close() throws SQLException {
list.add(conn);
}
@Override
public void commit() throws SQLException {
conn.commit();
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
conn.rollback();
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
conn.setAutoCommit(autoCommit);
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
// TODO Auto-generated method stub
return conn.prepareStatement(sql);
}
@Override
public boolean isWrapperFor(Class<?> arg0) throws SQLException {
// TODO Auto-generated method stub
return false;
}
}
后面还有非常多的方法,因为我没有用到,我就都放空了,当然你也可以写一个适配器,就不会看那么多那么杂了。
可以看到,这里面我就改写了close的方法,其它的都照样调用就完了。