JDBC 数据库连接池
基本概念
先看一张普通连接数据库图:
多个用户操作数据库,向数据库获得链接。数据库创建连接需要较大的资源,而且创建时间长,假设一个网站一天有超 10 万的访问量,则数据库就需要创建更多的连接,这样极大的浪费资源,而且很容易造成数据库服务器内存溢出、拓机。
如何解决此类问题,出现了数据库连接池。如图所示:
连接池相当于一个容器 (集合),存放数据库连接的容器。当系统初始化好了以后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,当用户访问完,会将连接对象归还给容器。
详细点就是:
使用连接对象之前先创建好规定数量(根据服务器内存的承载能力制定)的连接对象存到放连接池(实现池子的方式一般是用链表结构的集合来实现)中,当应用服务器需要连接对象的时候就从连接池中获取,用完该连接对象时归还连接对象到连接池中。当应用服务器需要连接对象而当前池子中没有连接对象可取时,就让其先等待,如果等待超时还没有回获取到连接对象,就新建一个连接对象给服务器让其使用,用完后销毁该创建的对象。
我的理解
就是,当你需要获取数据库连接时,你只需要创建一个连接池对象,定义好 sql 语句,给到连接池,就可以操作数据库。连接池对象就是个工具类,我们需要使用它就创建其对象,写好 SQL 语句就可实现操作数据库。数据库连接池中已经定义好了连接信息,而且我们可以通过修改其配置文件,实现不同数据之间的访问,也可以自定义最大连接量。
数据库连接池的好处
- 节约资源(不是关闭连接而是归还)
- 用户访问高效(直接获取连接对象,不用创建)
一个最基本的连接池:
public class PersonalConnectionPool {</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 用户名 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> String user; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 密码 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> String password; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 连接数据库的URL </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> String url; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 连接池 * 规定最大连接数为3 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> LinkedList<Connection><span style="color: rgba(0, 0, 0, 1)"> pool; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 从属性文件中加载数据库驱动,初始化连接池 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)">{ </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { Properties properties </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Properties(); pool </span>= <span style="color: rgba(0, 0, 255, 1)">new</span> LinkedList<Connection><span style="color: rgba(0, 0, 0, 1)">(); Class.forName(</span>"com.mysql.jdbc.Driver"<span style="color: rgba(0, 0, 0, 1)">); ClassLoader classLoader </span>= PersonalConnectionPool.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.getClassLoader(); InputStream iStream </span>= classLoader.getResourceAsStream("mysqlCongfig.properties"<span style="color: rgba(0, 0, 0, 1)">); properties.load(iStream); user </span>= properties.getProperty("user"<span style="color: rgba(0, 0, 0, 1)">); password </span>= properties.getProperty("password"<span style="color: rgba(0, 0, 0, 1)">); url </span>= properties.getProperty("url"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建三个连接对象(包装类对象)放到池子中</span> <span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = 0; i < 3; i++<span style="color: rgba(0, 0, 0, 1)">) { Connection connection </span>=<span style="color: rgba(0, 0, 0, 1)"> DriverManager.getConnection(url, user, password); Connection connectionWrapper </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ConnectionWapper(connection,pool); pool.add(connectionWrapper); } } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception e) { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> TODO Auto-generated catch block</span>
e.printStackTrace();
}} </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * </span><span style="color: rgba(128, 128, 128, 1)">@throws</span><span style="color: rgba(0, 128, 0, 1)"> SQLException * @method 向外提供连接对象 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> Connection getConnection() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SQLException { Connection connection; </span><span style="color: rgba(0, 0, 255, 1)">if</span>(pool.size()>0<span style="color: rgba(0, 0, 0, 1)">) { connection </span>=<span style="color: rgba(0, 0, 0, 1)"> pool.removeFirst(); } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">等待超时,返回一个新创建的对象</span> connection =<span style="color: rgba(0, 0, 0, 1)"> DriverManager.getConnection(url, user, password); } System.out.println(</span>"当前池子中有 "+pool.size()+" 个对象"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> connection; } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 归还连接对象 * 直接简化在包装类的close方法中 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
}
池子中用了到了一个包装类,包装了通过 DriverManager.getConnection 获取到的 Connection 的实现类对象,该包装也实现了 Connection 接口,重写了案例中需要的方法。该类结构如下:
public class ConnectionWapper implements Connection {</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * Connection接口的实现类对象的引用 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> Connection connection; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 存放连接包装对象的池子的引用 * </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> LinkedList<Connection><span style="color: rgba(0, 0, 0, 1)"> pool; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * * </span><span style="color: rgba(128, 128, 128, 1)">@param</span><span style="color: rgba(0, 128, 0, 1)"> connection 的实现类对象 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> ConnectionWapper(Connection connection, LinkedList<Connection><span style="color: rgba(0, 0, 0, 1)"> pool) { </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">(); </span><span style="color: rgba(0, 0, 255, 1)">this</span>.connection =<span style="color: rgba(0, 0, 0, 1)"> connection; </span><span style="color: rgba(0, 0, 255, 1)">this</span>.pool =<span style="color: rgba(0, 0, 0, 1)"> pool; } @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span> PreparedStatement prepareStatement(String sql) <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SQLException { PreparedStatement prepareStatement </span>=<span style="color: rgba(0, 0, 0, 1)"> connection.prepareStatement(sql); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> prepareStatement; } @Override </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> close() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SQLException { pool.add(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">); System.out.println(</span>"当前池子中有 "+pool.size()+" 个对象"<span style="color: rgba(0, 0, 0, 1)">); } @Override ...
}
基于统一,JAVA 为数据库连接池提供了公共接口,要求所有项目开发的连接池必须实现 DataSource 接口,可一统一用一套接口的方法使用不同开发商的数据库连接池。
常用连接池
dbcp 连接池
- 封装 dbcp 连接池创建一个数据库工具类
/** * (封装 dbcp 连接池来创建数据库工具类) * (属性文件为源文件夹 properties 下的 dbcpconfig.properties) */ public class DatabaseUtil {</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 数据库连接池(dbcp连接池)对象引用 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> DataSource dbcpPoor; </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 只执行一次 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> { Properties properties </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Properties(); ClassLoader classLoader </span>= DatabaseUtil.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.getClassLoader(); InputStream resourceAsStream </span>= classLoader.getResourceAsStream("dbcpconfig.properties"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { properties.load(resourceAsStream); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">通过直接创建连接池对象并设置参数方法创建连接池对象
// BasicDataSource basicDataSource = new BasicDataSource();
// basicDataSource.setUsername(properties.getProperty("username"));
// basicDataSource.setPassword(properties.getProperty("password"));
// basicDataSource.setDriverClassName(properties.getProperty("driverClassName"));
// basicDataSource.setUrl(properties.getProperty("url"));
//
// dbcpPoor = basicDataSource;
//通过工厂的方法创建连接池对象
dbcpPoor = BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 获取连接对象 * </span><span style="color: rgba(128, 128, 128, 1)">@throws</span><span style="color: rgba(0, 128, 0, 1)"> SQLException </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Connection getConnection() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SQLException{ Connection connection </span>=<span style="color: rgba(0, 0, 0, 1)"> dbcpPoor.getConnection(); System.out.println(</span>"获取连接对象成功"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> connection; } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 关闭资源 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">关闭建立的连接对象,释放资源</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> closeSourceConnection(Connection connection, Statement statement, ResultSet resultSet){ </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (connection!=<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { connection.close(); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (statement!=<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { statement.close(); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (resultSet!=<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { resultSet.close(); } } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SQLException e) { e.printStackTrace(); } } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> DataSource getDbcpPoor() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> dbcpPoor; }
}
c3p0 连接池
- 封装 c3p0 连接池创建一个数据库工具类
/** * (封装 c3p0 连接池来创建数据库工具类) * @author YanoHao * */ public class DatabaseUtil { /** * c3p0 连接池对象的引用 */ private static DataSource c3p0Poor; /** * 创建一次 */ static { //直接创建连接池对象(自动加载配置文件,无需设置参数) c3p0Poor = new ComboPooledDataSource(); //通过工厂的方式创建连接池对象 // try { // DataSource unpooledDataSource = DataSources.unpooledDataSource(); // c3p0Poor = DataSources.pooledDataSource(unpooledDataSource); // } catch (SQLException e) { // // TODO Auto-generated catch block // e.printStackTrace(); // } } /** * 获取连接对象 * @throws SQLException */ public static Connection getConnection() throws SQLException{Connection connection </span>=<span style="color: rgba(0, 0, 0, 1)"> c3p0Poor.getConnection(); System.out.println(</span>"获取连接对象成功"<span style="color: rgba(0, 0, 0, 1)">); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> connection; } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 关闭资源 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">关闭建立的连接对象,释放资源</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> closeSourceConnection(Connection connection, Statement statement, ResultSet resultSet){ </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (connection!=<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { connection.close(); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (statement!=<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { statement.close(); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (resultSet!=<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { resultSet.close(); } } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SQLException e) { e.printStackTrace(); } } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> DataSource getC3p0Poor() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> c3p0Poor; }
}
druid 连接池
- 封装 druid 连接池创建一个数据库工具类
/* * 封装 druid 连接池创建工具类 */ public class DatabaseUtil {</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * 数据库连接池(druid连接池)对象引用 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> DataSource druidPool; </span><span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> { </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { Properties properties </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> Properties(); ClassLoader classLoader </span>= DatabaseUtil.<span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)">.getClassLoader(); InputStream resourceAsStream </span>= classLoader.getResourceAsStream("druidconfig.properties"<span style="color: rgba(0, 0, 0, 1)">); properties.load(resourceAsStream); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">通过直接创建连接池对象的方式创建连接池对象
// DruidDataSource druidDataSource = new DruidDataSource();
// druidDataSource.setUsername(properties.getProperty("username"));
// druidDataSource.setPassword(properties.getProperty("password"));
// druidDataSource.setUrl(properties.getProperty("url"));
// druidDataSource.setDriverClassName(properties.getProperty("driverClassName"));
// druidPool = druidDataSource;
//通过工厂的方式创建连接池对象
druidPool = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> Connection getConnection() <span style="color: rgba(0, 0, 255, 1)">throws</span><span style="color: rgba(0, 0, 0, 1)"> SQLException{ Connection connection </span>=<span style="color: rgba(0, 0, 0, 1)"> druidPool.getConnection(); </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> connection; } </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> * * 关闭建立的连接对象,释放资源 </span><span style="color: rgba(0, 128, 0, 1)">*/</span> <span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> closeSourceConnection(Connection connection, Statement statement, ResultSet resultSet){ </span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)"> { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (connection!=<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { connection.close(); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (statement!=<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { statement.close(); } </span><span style="color: rgba(0, 0, 255, 1)">if</span> (resultSet!=<span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) { resultSet.close(); } } </span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (SQLException e) { e.printStackTrace(); } } </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span><span style="color: rgba(0, 0, 0, 1)"> DataSource getDruidPool() { </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> druidPool; }
}