vlambda博客
学习文章列表

JDBC07:数据库连接池

引入

  1. 数据库的连接资源并没有得到很好的重复利用。

  2. 对于每一次数据库连接,使用完后都得断开否则,如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。内存泄漏:内存中有对象不能被回收的情况。

  3. 这种开发不能控制被创建的连接对象数

数据库连接池

  • 为解决传统开发中的数据库连接问题,可以采用数据库连接池技术。

  • 数据库连接池的基本思想:就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。

  • 数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个

  • 数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

优点

1. 资源重用

2. 更快的系统反应速度

3. 新的资源分配手段

4. 统一的连接管理,避免数据库连接泄漏

多种开源的数据库连接池

C3P0数据库连接池

方式一:硬编码

 @Test public void testGetConnection() throws Exception{ //获取c3p0数据库连接池 ComboPooledDataSource cpds = new ComboPooledDataSource(); cpds.setDriverClass( "com.mysql.jdbc.Driver" );  cpds.setJdbcUrl( "jdbc:mysql://localhost:3306/test" ); cpds.setUser("root");  cpds.setPassword("123456");  //通过设置相关的参数,对数据库连接池进行管理: //设置初始时数据库连接池中的连接数 cpds.setInitialPoolSize(10);  Connection conn = cpds.getConnection(); System.out.println(conn);  //销毁c3p0数据库连接池// DataSources.destroy( cpds ); }

方式二:使用配置文件

//方式二:使用配置文件@Testpublic void testGetConnection1() throws SQLException{ ComboPooledDataSource cpds = new ComboPooledDataSource("hellc3p0"); Connection conn = cpds.getConnection(); System.out.println(conn);}

配置文件

<?xml version="1.0" encoding="UTF-8"?><c3p0-config>
<named-config name="hellc3p0"> <!-- 提供获取连接的4个基本信息 --> <property name="driverClass">com.mysql.jdbc.Driver</property> <property name="jdbcUrl">jdbc:mysql:///test</property> <property name="user">root</property> <property name="password">123456</property>
<!-- 进行数据库连接池管理的基本信息 --> <!-- 当数据库连接池中的连接数不够时,c3p0一次性向数据库服务器申请的连接数 --> <property name="acquireIncrement">5</property> <!-- c3p0数据库连接池中初始化时的连接数 --> <property name="initialPoolSize">10</property> <!-- c3p0数据库连接池维护的最少连接数 --> <property name="minPoolSize">10</property> <!-- c3p0数据库连接池维护的最多的连接数 --> <property name="maxPoolSize">100</property> <!-- c3p0数据库连接池最多维护的Statement的个数 --> <property name="maxStatements">50</property> <!-- 每个连接中可以最多使用的Statement的个数 --> <property name="maxStatementsPerConnection">2</property>
</named-config></c3p0-config>

方法二升级:将方式二写进工具类

 /** *  * @Description 使用C3P0的数据库连接池技术 * @author shkstart * @date 下午3:01:25 * @return * @throws SQLException */ //数据库连接池只需提供一个即可。 private static ComboPooledDataSource cpds = new ComboPooledDataSource("hellc3p0"); public static Connection getConnection1() throws SQLException{ Connection conn = cpds.getConnection();  return conn; }
public void testGetCustomerById() { Connection conn = null; try { conn = JDBCUtils.getConnection1();  Customer cust = dao.getCustomerById(conn, 19); System.out.println(cust);   } catch (Exception e) { e.printStackTrace(); }finally{ JDBCUtils.closeResource(conn, null);  }}

dbcp数据库连接池

方式一:硬编码(不推荐)

 @Test public void testGetConnection() throws SQLException{ //创建了DBCP的数据库连接池 BasicDataSource source = new BasicDataSource();  //设置基本信息 source.setDriverClassName("com.mysql.jdbc.Driver"); source.setUrl("jdbc:mysql:///test"); source.setUsername("root"); source.setPassword("123456");  //还可以设置其他涉及数据库连接池管理的相关属性: source.setInitialSize(10); source.setMaxActive(10); //。。。  Connection conn = source.getConnection(); System.out.println(conn);}

方式二:使用配置文件

 @Test public void testGetConnection1() throws Exception{ Properties pros = new Properties();  //方式1:// InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties"); //方式2: FileInputStream is = new FileInputStream(new File("src/dbcp.properties"));   pros.load(is); DataSource source = BasicDataSourceFactory.createDataSource(pros);  Connection conn = source.getConnection(); System.out.println(conn);}

配置文件

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///test
username=root
password=abc123

initialSize=10

方式三:对方式二的升级,写在工具类中

 /* @Description 使用DBCP数据库连接池技术获取数据库连接 * @author shkstart * @date 下午3:35:25 * @return * @throws Exception */ //创建一个DBCP数据库连接池 private static DataSource source; static{ try { Properties pros = new Properties(); FileInputStream is = new FileInputStream(new File("src/dbcp.properties")); pros.load(is); source = BasicDataSourceFactory.createDataSource(pros); } catch (Exception e) { e.printStackTrace(); }} public static Connection getConnection2() throws Exception{  Connection conn = source.getConnection();  return conn;}

Druid数据库连接池

Druid是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、Proxool等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池,可以说是目前最好的连接池之一。

package com.atguigu.druid;import java.sql.Connection;import java.util.Properties;
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSourceFactory;
public class TestDruid { public static void main(String[] args) throws Exception { Properties pro = new Properties(); pro.load(TestDruid.class.getClassLoader().getResourceAsStream("druid.properties")); DataSource ds = DruidDataSourceFactory.createDataSource(pro); Connection conn = ds.getConnection(); System.out.println(conn);}}

其中,src下的配置文件为:【druid.properties】

url=jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true
username=root
password=123456
driverClassName=com.mysql.jdbc.Driver

initialSize=10
maxActive=20
maxWait=1000
filters=wall

JDBCUtils

 /** * 使用Druid数据库连接池技术 */ private static DataSource source1; static{ try { Properties pros = new Properties();  InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");  pros.load(is);  source1 = DruidDataSourceFactory.createDataSource(pros); } catch (Exception e) { e.printStackTrace(); }} public static Connection getConnection3() throws SQLException{  Connection conn = source1.getConnection(); return conn;}
  • 详细配置参数:

配置 缺省 说明
name
配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。  如果没有配置,将会生成一个名字,格式是:”DataSource-” +   System.identityHashCode(this)
url
连接数据库的url,不同数据库不一样。例如:mysql :   jdbc:mysql://10.20.153.104:3306/druid2      oracle :   jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username
连接数据库的用户名
password
连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName
根据url自动识别   这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle
最小连接池数量
maxWait
获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements false 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements -1 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery
用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle false 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis
有两个含义:1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun
不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis

connectionInitSqls
物理连接初始化的时候执行的sql
exceptionSorter
根据dbType自动识别   当数据库抛出一些不可恢复的异常时,抛弃连接
filters
属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:  监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters
类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系