前述:
对之前JDBC的操作,不管是原始的操作步骤,还是JDBCUtils工具类的写法,都存在一个严重的问题:每次我们都需要去创建一个连接对象,然后再释放掉资源的操作。
从效率和内存的角度,这种重复创建和释放的操作方式是不可取的。那么该如何优化?
既然知道了问题是出现在重复创建和释放的操作上,那我们便可以对症下药--让重复不再重复。
于是,结合线程池的思想,我们也可以创建一个容器来存放一定数量的数据库连接对象,在使用时获取,在关闭时归还。---我们称之为数据库连接池。
一、数据库连接池
1.1 概述
存放数据库连接的容器(集合)。
作用:高效率,节约资源。
1.2 代码实现
接口标准:javax.sql.DataSource
-
- 获取连接:getConnection()
- 归还连接:Connection.close()。 // 如果连接对象Connection是从连接池中获取的,那么调用Connection.close()方法,则不会再关闭连接了,而是归还连接。
小贴士:一般我们不去实现它,由数据库厂商来实现,比如:
- C3P0:数据库连接池技术
- Druid:数据库连接池实现技术(由阿里巴巴提供的)
二、C3P0 数据库连接池技术
2.1 步骤
1. 导入jar包 (两个: c3p0-0.9.5.2.jar、mchange-commons-java-0.2.12.jar)
2. 定义配置文件:
* 名称:c3p0.properties 或者 c3p0-config.xml
* 路径:直接将文件放在src目录下即可。
3. 创建核心对象:数据库连接池对象 ComboPooledDataSource
4. 获取连接:getConnection
2.2 代码实现
1. 创建数据库连接池对象
DataSource ds = new ComboPooledDataSource();
2. 获取连接对象
Connection conn = ds.getConnection();
三、Druid 数据库连接池实现技术(由阿里巴巴提供)
3.1 步骤
1. 导入jar包 druid-1.0.9.jar
2. 定义配置文件:
* 是properties形式的,如druid.properties
* 可以叫任意名称,可以放在任意目录下
3. 加载配置文件。Properties
4. 获取数据库连接池对象:通过工厂类来获取 DruidDataSourceFactory
5. 获取连接:getConnection
3.2 代码实现
1.加载配置文件
Properties pro = new Properties();
InputStream is = DruidDemo.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
2.获取连接池对象
DataSource ds = DruidDataSourceFactory.createDataSource(pro);
3.获取连接对象
Connection conn = ds.getConnection();
四、重新定义JDBCUtils工具类
4.1 步骤
1. 定义一个工具类 JDBCUtils
2. 提供静态代码块加载配置文件,初始化连接池对象
3. 提供方法
* 获取连接方法:通过数据库连接池获取连接
* 释放资源
* 获取连接池的方法
4.2 代码实现(使用Druid连接池)
public class JDBCUtils { //1.定义成员变量 DataSource private static DataSource ds ; static{ try { //1.加载配置文件 Properties pro = new Properties(); pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties")); //2.获取DataSource ds = DruidDataSourceFactory.createDataSource(pro); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } } /** * 获取连接 */ public static Connection getConnection() throws SQLException { return ds.getConnection(); } /** * 释放资源 */ public static void close(Statement stmt,Connection conn){ /* if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close(); //归还连接 } catch (SQLException e) { e.printStackTrace(); } }*/ close(null,stmt,conn); } /** * 释放资源 */ public static void close(ResultSet rs , Statement stmt, Connection conn){ if(rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if(stmt != null){ try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if(conn != null){ try { conn.close(); //归还连接 } catch (SQLException e) { e.printStackTrace(); } } } /** * 获取连接池方法 */ public static DataSource getDataSource(){ return ds; } }
五、
面试题:数据库连接池的工作机制是什么?
J2EE服务器启动时会建立一定数量的连接池,并一直维持不少于此数目的池连接。
客户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其标记为忙。
如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量由配置参数决定。
当使用的池连接调用完成后,池驱动程序将此连接标记为闲,其他调用就可以使用这个连接。
实现方式,返回的Connection是原始Connection的代理,代理Connection的close方法不是真正关闭连接,而是把它代理的Connection对象还回到连接池中。