简易连接池原理以及装饰者设计模式

首先当然得简要说说为什么要有连接池

有文章说第一位设计者是去游泳的时候,进去穿共用的拖鞋,出来的时候还回去。因此称之为池,顾名思义,连接池就是存放多个连接(拖鞋)的池了。当服务需要的时候到这里取一个连接,用完还回来。
哦对,还等讲一下为什么要还,每一次都买一双,用完就扔掉(你可别说带回家,共用有香港脚啥的,哥打死你,记得多洗脚),拆包装费时间,丢掉浪费钱吧。同理,频繁获取连接是会耗费一定的开销,在面对大量请求时,就需要考虑连接池的设计了。

在不用连接池之前

我们需要连接的时候我们都是拿着账号密码去登录连接到数据库,用完了直接将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’ 的方法填充就好,如果要修改的就自由发挥了。

下面看下这个connection的装饰者类

/**
 * 
 * @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的方法,其它的都照样调用就完了。

呀!写到这就完了,下期见,下期写个注解的使用吧,哎,蛮看了去吧!还行的话点个赞吧,不行也不要喷了,我会难过的,哈哈。

猜你喜欢

转载自blog.csdn.net/qq_39150049/article/details/108360356