数据库连接池的概念
- 数据库连接背景数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的网页应用程序中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的。
- 数据库连接池数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个。这项技术能明显提高对数据库操作的性能。
- 数据库连接池的原理没有使用数据库连接池:一个访问创建一个连接,使用完关闭连接。而频繁的创建和关闭连接非常耗时
- 使用数据库连接池之后:提前准备一些数据库连接,使用时从池中取出,用完归还连接池
自定义连接池
初探连接池
自定义JDBC工具类
配置文件 config.properties
driverClass=com.mysql.jdbc.Driverurl=jdbc:mysql://主机名:3306/数据库名username=用户名password=密码
JDBCUtils工具类
public class JDBCUtils { private JDBCUtils() {} //构造函数私有化 private static String driverClass; private static String url; private static String username; private static String password; private static Connection con; static { try { InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("config.properties"); Properties properties = new Properties(); properties.load(is); driverClass = properties.getProperty("driverClass"); url = properties.getProperty("url"); username = properties.getProperty("username"); password = properties.getProperty("password"); Class.forName(driverClass); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() { //获取连接对象 try { con = DriverManager.getConnection(url, username, password); } catch (SQLException e) { e.printStackTrace(); } return con; } //关闭连接(有查询结果集) public static void close(Connection con, Statement stat, ResultSet res) { if (con != null) { try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stat != null) { try { stat.close(); } catch (SQLException e) { e.printStackTrace(); } } if (res != null) { try { res.close(); } catch (SQLException e) { e.printStackTrace(); } } } //关闭连接(无查询结果集) public static void close(Connection con, Statement stat) { close(con, stat, null); }}
实现连接池类
定义一个连接池类并实现 java.sql.DataSource 接口。
Connection getConnection(); //获取数据库连接对象
public class MyDataSource implements DataSource{ //定义集合容器,用于保存多个数据库连接对象 //使用Collections 工具类实现集合的线程同步 private static List<Connection> pool = Collections.synchronizedList(new ArrayList<Connection>()); //静态代码块,生成10个数据库连接保存到集合中 static { for (int i = 0; i < 10; i++) { Connection con = JDBCUtils.getConnection(); pool.add(con); } } //返回连接池的大小 public int getSize() { return pool.size(); } //从池中返回一个数据库连接 @Override public Connection getConnection() { if(pool.size() > 0) { //从池中获取数据库连接 return pool.remove(0); }else { throw new RuntimeException("连接数量已用尽"); } } @Override public Connection getConnection(String username, String password) throws SQLException { return null; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { return null; } @Override public boolean isWrapperFor(Class<?> iface) throws SQLException { return false; } @Override public PrintWriter getLogWriter() throws SQLException { return null; } @Override public void setLogWriter(PrintWriter out) throws SQLException { } @Override public void setLoginTimeout(int seconds) throws SQLException { } @Override public int getLoginTimeout() throws SQLException { return 0; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return null; }}
自定义连接池的测试
public class MyDataSourceTest { public static void main(String[] args) throws SQLException { MyDataSource dataSource = new MyDataSource(); System.out.println("使用前连接池数量:" + dataSource.getSize()); Connection con = dataSource.getConnection(); String sql = "select * from emp"; PreparedStatement pst = con.prepareStatement(sql); ResultSet res = pst.executeQuery(); while (res.next()) { String ename = res.getString("ename"); String job = res.getString("job"); String hiredate = res.getString("hiredate"); System.out.println("ename:" + ename + "\t job:" + job + "\t hiredate:" + hiredate); } res.close(); pst.close(); con.close(); System.out.println("使用后连接池数量:" + dataSource.getSize()); }}
输出:
使用前连接池数量:10ename:SMITH job:CLERK hiredate:1980-12-17ename:ALLEN job:SALESMAN hiredate:1981-02-20ename:WARD job:SALESMAN hiredate:1981-02-22ename:JONES job:MANAGER hiredate:1981-04-02ename:MARTIN job:SALESMAN hiredate:1981-09-28ename:BLAKE job:MANAGER hiredate:1981-05-01ename:CLARK job:MANAGER hiredate:1981-06-09ename:SCOTT job:ANALYST hiredate:1987-04-19ename:KING job:PRESIDENT hiredate:1981-11-17ename:TURNER job:SALESMAN hiredate:1981-09-08ename:ADAMS job:CLERK hiredate:1987-05-23ename:JAMES job:CLERK hiredate:1981-12-03ename:FORD job:ANALYST hiredate:1981-12-03ename:MILLER job:CLERK hiredate:1982-01-23使用后连接池数量:9
问题:虽然我们自定义了数据库连接池,但是连接关闭以后并没有归还给数据库连接池,还需要改进 归还连接 的问题,更多资料点击此处领取
继承方式改进连接池
System.out.println(JDBCUtils.getConnection());//com.mysql.jdbc.JDBC4Connection@470e2030
通过输出 Connection 的地址发现 Connection 类的实现类是 JDBC4Connection ,是否能够通过编写一个类继承 JDBC4Connection ,然后重写 close() 方法,在关闭连接的同时归还连接?
/* 自定义Connection类 */ public class MyConnection1 extends JDBC4Connection { //声明连接对象和连接池集合对象 private Connection con; private List<Connection> pool; //通过构造方法给成员变量赋值 public MyConnection1(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url,Connection con,List<Connection> pool) throws SQLException { super(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url); this.con = con; this.pool = pool; } //重写close()方法,将连接