JDBC从入门到放弃
07-JDBC的数据库连接池
传统数据库连接
一般的数据库连接操作
①获取或建立数据库连接。
②进行sql操作
③断开数据库连接。
这样的搞发存在很多问题
- 耗费时间
普通的JDBC数据库连接使用 DriverManager 来获取,每次向数据库建立连接的时候都要将 Connection 加载到内存中,再验证用户名和密码(得花费0.05s~1s的时间)。
- 太频繁链接数据库,服务器遭不住!
数据库的连接资源并没有得到很好的重复利用,用了马上就断开了.若同时有几百人甚至几千人在线,频繁的进行数据库连接操作将占用很多的系统资源,严重的甚至会造成服务器的崩溃。
- 太多数据库连接不能被正常关闭,服务器遭不住!
对于每一次数据库连接,使用完后都必须得断开。因为如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。总结:太多数据库连接不能被正常关闭,服务器遭不住!
- 数据库被连接的数量失控,服务器遭不住!
这种开发不能控制被创建的连接对象数,系统资源会被毫无顾及的分配出去,如连接过多,也可能导致内存泄漏,服务器崩溃。
数据库连接池
为解决传统开发中的数据库连接问题,可以采用数据库连接池技术。
数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。
连接池里有些设置:最小数据库连接数,最大数据库连接数量,连接池通过这种机制,来控制数据库连接的!
JDK为数据库连接提供了一个接口:java.sql.DataSource,这个接口,和数据库驱动中的Driver接口一样,不用我们去实现,我们需要会用它的实现类就好了!它的实现类会有服务器开发商(Tomcat)来提供实现,也有一些开源组织做了写免费实现类:
- DBCP(Apache)
- C3P0
- DRUID(阿里巴巴)
所以DataSource虽然被称为数据源,但它却是包含了连接池和连接池的管理两个部分的功能,实质上我们称为数据库连接池!
数据库连接池-DBCP的使用
引入相关依赖jar包
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> <version>2.5.0</version> </dependency> |
常规硬编码
- 创建DBCP数据源实例(BasicDataSource是DataSource接口的实现类)
BasicDataSource dataSource = new org.apache.commons.dbcp.BasicDataSource();
- 为DBCP数据源实例设置连接数据库必要的属性
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/study");
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
- 设置可选的数据源属性
a.数据库连接池中初始化的连接数
dataSource.setInitialSize(10);
b.同一时刻可以向数据库申请的最大的连接数
dataSource.setMaxTotal (50);
c.数据库连接池中保存的最少的空闲连接数
dataSource.setMinIdle(5);
d.等待数据连接池分配的最长时间,毫秒,超时抛出异常
dataSource. setMaxWaitMillis (1000*5);
- 从数据源中获取数据库连接
Connection conn = dataSource.getConnection();
测试代码
/** * 测试dbcp数据库连接池(硬编码实现) */ @SuppressWarnings("resource") @Test public void testDBCPHardCode() { try { BasicDataSource dataSource = new BasicDataSource(); //设置必选的数据源属性 dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setUrl("jdbc:mysql://localhost:3306/study"); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); //设置可选的数据源属性 dataSource.setInitialSize(10);//数据库连接池中初始化的连接数 dataSource.setMaxTotal(50);//同一时刻可以向数据库申请的最大的连接数 dataSource.setMinIdle(5);//.数据库连接池中保存的最少的空闲连接数 dataSource.setMaxWaitMillis(1000*5);// //获取数据源连接 Connection con = dataSource.getConnection(); System.out.println(con); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } |
运行结果,正确
配置文件
以上使用的是硬编码的方式,将datasource的属性值写死在程序中,不利于后期维护和扩展。现在我们学习下,如何使用配置文件的方式来进行DBCP连接池的创建和连接的获取。
总体过程如下
Properties pop = new Properties();
InputStream in = DataSourceTest.class.getClassLoader().
getResourceAsStream("datasource.properties");
pop.load(in);
DataSource dataSource = BasicDataSourceFactory.createDataSource(pop);
Connection conn = dataSource.getConnection();
- :创建配置文件datasource.properties
注意:配置文件的"键"是BasicDataSource类的setXxx()或getXxx()方法中的Xxx首写字母变小写的属性:xxx
#必选的属性 username=root password=123456 url=jdbc:mysql://127.0.0.1:3306/study driverClassNames=com.mysql.jdbc.Driver #可选的属性 initialSize=10 maxTotal=5 minIdle=5 maxWaitMillis=5000 |
- 获取属性和连接池并测试
/** * 测试dbcp数据库连接池(配置文件) */ @Test public void testDBCPConfigFile() { try { //加载配置文件 Properties pop = new Properties(); InputStream in = UseServiceTest.class.getClassLoader(). getResourceAsStream("datasource.properties"); pop.load(in); //设置配置文件 DataSource dataSource = BasicDataSourceFactory.createDataSource(pop); //获取连接 Connection con = dataSource.getConnection(); System.out.println(con); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } |
参考文献
http://commons.apache.org/proper/commons-dbcp/guide/index.html
数据库连接池-c3p0的使用
引入相关依赖jar包
<dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> <version>0.9.5.2</version> </dependency> |
常规硬编码
/** * 测试c3p0数据库连接池(硬编码实现) */ @Test public void testC3p0HardCode() { try { ComboPooledDataSource cpds = new ComboPooledDataSource(); cpds.setDriverClass("com.mysql.jdbc.Driver");// loads the jdbc driver cpds.setJdbcUrl("jdbc:mysql://localhost:3306/study"); cpds.setUser("root"); cpds.setPassword("123456");
cpds.setMaxStatements( 180 ); // 获取数据源连接 Connection con = cpds.getConnection(); System.out.println(con); } catch (PropertyVetoException e) { // TODO Auto-generated catch block e.printStackTrace(); }catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } |
关于其他配置参数以及含义,请查阅
https://www.mchange.com/projects/c3p0/#configuration_properties
这里不再赘述。
测试结果如下,正确
配置文件
这里我们使用一下XML配置方式
- 创建c3p0-config.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <c3p0-config> <named-config name="mysql"> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql://localhost:3306/study</property> <property name="user">root</property> <property name="password">123456</property>
<property name="acquireIncrement">50</property> <property name="initialPoolSize">100</property> <property name="minPoolSize">50</property> <property name="maxPoolSize">1000</property> <property name="maxStatements">0</property> <property name="maxStatementsPerConnection">5</property> <user-overrides user="master-of-the-universe"> <property name="acquireIncrement">1</property> <property name="initialPoolSize">1</property> <property name="minPoolSize">1</property> <property name="maxPoolSize">5</property> <property name="maxStatementsPerConnection">50</property> </user-overrides> </named-config> </c3p0-config> |
- 使用和编写测试代码
/** * 测试c3p0数据库连接池(配置文件) */ @Test public void testC3p0ConfigFile() { try { // 加载配置文件 DataSource dataSource = new ComboPooledDataSource("mysql"); // 获取连接 Connection con = dataSource.getConnection(); System.out.println(con); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } |
测试结果如下,可用。
参考文献
https://www.mchange.com/projects/c3p0/
至此我们的数据库连接池的部分已经介绍完毕,关于DRUID的使用,有兴趣的话可用自行研究一下,这里就不赘述了。
封装我们自己的数据库连接
现在学习数据库连接池的使用,我们就也可以对我们的工具类中获取数据库连接部分做相应的改动了。
DataSourceUtil:1
/** * 硬编码方式获取数据源 * * @author liujia * */ public class DataSourceUtil {
/** * DBCP dataSource * * @return */ public static DataSource getDBCPDataSource() { BasicDataSource dataSource = null; dataSource = new BasicDataSource(); // 设置必选的数据源属性 dataSource.setUsername("root"); dataSource.setPassword("123456"); dataSource.setUrl("jdbc:mysql://localhost:3306/study"); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); // 设置可选的数据源属性 dataSource.setInitialSize(10);// 数据库连接池中初始化的连接数 dataSource.setMaxTotal(50);// 同一时刻可以向数据库申请的最大的连接数 dataSource.setMinIdle(5);// .数据库连接池中保存的最少的空闲连接数 dataSource.setMaxWaitMillis(1000 * 5);//最大连接等待时间 return dataSource; }
/** * C3p0 DataSource * * @return */ public static DataSource getC3p0DataSource() { ComboPooledDataSource dataSource = null; try { dataSource = new ComboPooledDataSource(); dataSource.setDriverClass("com.mysql.jdbc.Driver");// loads the jdbc driver dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/study"); dataSource.setUser("root"); dataSource.setPassword("123456"); dataSource.setMaxStatements(180); } catch (PropertyVetoException e) { // TODO Auto-generated catch block e.printStackTrace(); } return dataSource; } } |
DataSourceUtil:2
/** * 配置文件方式获取数据源 * * @author liujia * */ public class DataSourceUtil {
/** * DBCP dataSource * * @return */ public static DataSource getDBCPDataSource() { DataSource dataSource = null; try { // 加载配置文件 Properties pop = new Properties(); InputStream in = DataSourceUtil.class.getClassLoader().getResourceAsStream("datasource.properties"); pop.load(in); // 设置配置文件 dataSource = BasicDataSourceFactory.createDataSource(pop); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return dataSource; }
/** * C3p0 DataSource * * @return */ public static DataSource getC3p0DataSource() { DataSource dataSource = null; try { // 加载配置文件 dataSource = new ComboPooledDataSource("mysql"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return dataSource; } } |
DataSourceBBUtil:
/** * 数据源数据库连接 * * @author liujia * */ public class DataSourceDBUtils {
/** * 创建数据库连接 * * @return */ public static Connection getConnection(DataSource dataSource) { Connection connection = null; try { connection = dataSource.getConnection(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return connection; }
public static void close(Connection con,Statement stat,ResultSet rs) { closeResultSet(rs); closeStatement(stat); closeCon(con); }
/** * 关闭数据库连接 * * @param con */ private static void closeCon(Connection con) { if (con != null) { try { con.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
/** * 关闭statement * * @param stat */ private static void closeStatement(Statement stat) { if (stat != null) { try { stat.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
/** * 关闭result * * @param rs */ private static void closeResultSet(ResultSet rs) { if (rs != null) { try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } |