浅谈在Spring中如何使用数据源(DBCP、C3P0、JNDI)

时间:2022-09-10 14:05:28

在 spring 中,有以下三种方式来创建数据源:

  • 通过 jndi 获取应用服务器中的数据源;
  • 在 spring 容器中配置数据源;
  • 通过代码来创建数据源,这种方式适用于无容器依赖的单元测试。

1 配置数据源

spring 在第三方依赖包中包含了 2 种数据源的实现包 一个是 apache 的 dbcp;另一个是 c3p0。 我们可以在 spring 配置文件中直接配置这些数据源 。

1.1 dbcp

dbcp (database connection pool)是一个依赖 jakarta commons-pool 对象池机制的数据库连接池,所以在类路径下还必须包括 commons-pool.jar。 下面是使用 dbcp 配置 mysql 数据源的配置片段:

?
1
2
3
4
5
6
<bean id="datasource" class="org.apache.commons.dbcp.basicdatasource" destroy-method="close">   
  <property name="driverclassname" value="com.mysql.jdbc.driver" />   
  <property name="url" value="jdbc:mysql://localhost:3309/db" />   
  <property name="username" value="root" />   
  <property name="password" value="xxxxxx" />   
</bean> 

basicdatasource 提供了 close() 方法用于关闭数据源,所以必须设定 destroy-method=”close”, 以便 spring 容器关闭时,能够正常关闭数据源。

除以上必须的数据源属性外,还有一些常用的属性。

事务属性:

 

属性 默认值 说明
defaultautocommit true 连接默认为 auto-commit 状态。
defaultreadonly 驱动默认值 连接默认的 read-only 状态 。如果没有设置则 setreadonly 方法将不会被调用。( 某些驱动不支持只读模式 , 比如:informix)
defaulttransactionisolation 驱动默认值 连接默认的 transactionisolation 状态。有这些值:none、read_committed、read_uncommitted、repeatable_read、serializable。

 

连接数相关属性:

 

属性 默认值 说明
initialsize 0 初始化连接数:连接池启动时创建的初始化连接数量。
maxactive 8 最大活动连接 : 连接池在同一时间内能够分配的最大活动连接的数量。如果设置为非正数,则表示不限制。
maxidle 8 最大空闲连接 : 连接池中容许保持空闲状态的最大连接数量 , 超过的空闲连接将被释放 , 如果设置为负数,则表示不限制。
minidle 0 最小空闲连接 : 连接池中容许保持空闲状态的最小连接数量 , 低于这个数量将创建新的连接 , 如果设置为 0,则表示不创建。
maxwait 无限 最大等待时间 : 当没有可用连接时 , 连接池等待连接被归还的最大时间 ( 单位为毫秒 ) , 超出时间将抛出异常 , 如果设置为 -1,则表示无限等待。

 

连接监测与维护相关属性:

 

属性 默认值 说明
validationquery 配置 sql 查询语句 , 用于验证从连接池取出的连接是否可用。如果指定 , 则查询必须是一个 sql select,并且必须返回至少一行记录。mysql 中是 “select 1”;在 oracle 中是 "select 1 from dual"。
testonborrow true 指明是否从连接池中取出连接之前进行检测 , 如果检测失败 , 则从池中去除连接并尝试取出另一个新的连接。 注意 : 设置为 true 后如果要生效,则 validationquery 参数必须正确被设置。
testonreturn false 指明是否在归还到池中前进行检测。 注意 : 与 testonborrow 一样,设置为 true 后如果要生效,则 validationquery 参数必须正确被设置。
testwhileidle false 指明连接是否会被空闲连接回收器 ( 如果有 ) 所检测。 如果检测失败 , 则连接将从池中被移除。 注意 : 设置为 true 后如果要生效,则 validationquery 参数必须正确被设置。
timebetweenevictionrunsmillis -1 空闲连接回收器线程运行的周期 , 以毫秒为单位。如果设置为非正数 , 则不运行空闲连接回收器线程。 注意 : 启用该参数时,则 validationquery 参数必须正确被设置。
numtestsperevictionrun 3 在每次空闲连接回收器线程 ( 如果有 ) 运行时需要检测的连接数量。
minevictableidletimemillis 1000 * 60 * 30 连接在池中保持空闲而不被空闲连接回收器线程回收的最小时间值,以毫秒为单位。

 

缓存相关属性:

 

属性 默认值 说明
poolpreparedstatements false 开启连接池的 prepared statement 功能设置为 true 后,所有的 callablestatement 和 preparedstatement 都会被缓存起来。
maxopenpreparedstatements 不限制 能够同时分配打开的 statements 的最大数量。0 表示不限制。

 

连接泄露回收相关属性:

 

属性 默认值 说明
removeabandoned false 是否删除泄露的连接。如果设置为 true, 那么那些可能存在泄露的连接会被删除。假设 maxactive 为 10 个,活动连接为 8 个,空闲连接为 1 个,10-8-1=1,那么就会把删除这个连接(会先检测该活动连接未被使用的时间是否超过 removeabandonedtimeout)。如果需要一个长连接操作,那么 removeabandoned 需要设置的长一些,否则正常使用的连接可能会被误删除。
removeabandonedtimeout 300 泄露的连接可以被删除的时间段,单位为秒。
logabandoned false 当 statement 或连接被泄露时是否打印堆栈日志 。

 

假设数据库用的是 mysql,那么如果数据源配置不当,将可能会发生经典的 “8 小时问题 ”。 原因是 mysql 在默认情况下如果发现一个连接的空闲时间超过 8 小时,那么会在数据库端自动关闭这个连接 。 而数据源并不知道这个连接已经被关闭了,所以当它将这个无用的连接返回给某个 dao 时, dao 就会抛出无法获取 connection 的异常 。

dbcp 的 testonborrow 默认设置为 true,所以从连接池中取出连接之前会先进行检测,因为不会发生 “8 小时问题 ”。 但如果每次取连接时都进行检测,那么在高并发应用下就会产生性能问题。

因此建议在高并发下,将 testonborrow 设置为 false;然后将 testwhileidle 设置为 true,打开空闲连接回收器;最后把 timebetweenevictionrunsmillis 的值设定为小于 8 小时,这样那些被 mysql 所关闭的空闲连接,就会被清除出去。这样不仅解决了 “8 小时问题 ”,而且还保证了高性能 o(∩_∩)o哈哈~

注意:因为 mysql 本身的 interactive-timeout(单位为 s)参数,可以设定空闲连接的过期时间,所以我们要想获取到这个参数值,然后再设定 dbcp 的 timebetweenevictionrunsmillis 属性值。

1.2 c3p0

c3p0 是一个开放源代码的 jdbc 数据源实现项目,它实现了 jdbc3 和 jdbc2 扩展规范说明的 connection 和 statement 池。

下面是使用 c3p0 配置 mysql 数据源的配置片段:

?
1
2
3
4
5
6
<bean id="datasource" class="com.mchange.v2.c3p0.combopooleddatasource" destroy-method="close">   
  <property name="driverclass" value="oracle.jdbc.driver.oracledriver" />   
  <property name="jdbcurl" value="jdbc:mysql://localhost:3309/db" />   
  <property name="use" value="xxx" />   
  <property name="password" value="xxxxxx" />   
</bean> 

c3p0 也提供了一个用于关闭数据源的 close() 方法,这样我们就可以保证 spring 容器被关闭时,能够成功关闭数据源 。

 

属性 默认值 说明
acquireincrement   当连接池中无空闲连接时, 一次性创建新连接的数量。
acquireretryattempts 30 在从数据库获取新连接失败后,重复尝试的次数。
acquireretrydelay 1000 尝试获取连接之间的间隔时间,单位为毫秒。
autocommitonclose false 连接关闭时,将所有未提交的操作回滚 。
automatictesttable null 会创建一张名为 test 的空表,并使用其自带的查询语句进行测试 。 如果定义了这个参数,那么 preferredtestquery 属性 将被忽略 。 我们不能在这张 test 表上进行任何操作,它仅为 c3p0 测试所用。
breakafteracquirefailure false 获取连接失败时,将会引起所有等待获取连接的线程抛出异常 。 但是数据源仍有效保留,并在下次调用 getconnection() 时继续尝试获取连接 。 在尝试获取连接失败后,该数据源将申明已断开并永久关闭。
checkouttimeout 0 当连接池中的连接用完时,客户端调用 getconnection() 后等待获取新连接的时间,单位:毫秒。超时后将抛出 sqlexception 。设为 0 表示无限期等待 。
connectiontesterclassname com.mchange.v2.c3p0.impl.defaultconnectiontester 通过实现 connectiontester 或 queryconnectiontester 的类来测试连接,类名需设置为全限定名 。
idleconnectiontestperiod 0 隔多少秒,检查连接池中的所有空闲连接。0 表示不检查。
initialpoolsize 3 初始化时创建的连接数,应在 minpoolsize 与 maxpoolsize 之间取值 。
maxidletime 0 最大空闲时间,超过空闲时间的连接将会被丢弃 。 为 0 或负数则表示永不丢弃 。
maxpoolsize 15 连接池中保留的最大连接数 。
maxstatements 0 jdbc 标准参数,用以控制数据源内加载的 preparedstatement 数量 。 但由于预缓存的 statement 属于单个 connection 而不是整个连接池 。 所以设置这个参数需要多方面的考虑,如果 maxstatements 与 maxstatementsperconnection 均为 0 ,则缓存被关闭 。
maxstatementsperconnection 0 连接池内单个连接所拥有的最大缓存 statement 数 。
numhelperthreads 3 c3p0 是异步操作的,缓慢的 jdbc 操作通过 helperthreads 完成 。 通过多线程实现多个操作同时被执行,这样可以有效地提升性能。
preferredtestquery null 定义所有连接测试都执行的测试语句。在使用连接测试的情况下,这个参数能够显著地提高测试速度。测试的表必须在初始数据源时就存在。
propertycycle 300 修改系统配置参数生效时长,单位为 s。
testconnectiononcheckout false 因性能消耗大,所以请只在需要时开启 。 如果设为 true 那么在每个 connection 提交的时候都将校验其有效性 。 建议使用 idleconnectiontestperiod 或 automatictesttable 等方法来提升连接测试的性能 。
testconnectiononcheckin false 如果设为 true,那么在取得连接的同时将校验其连接的有效性。

 

2 jndi 数据源

如果应用配置在高性能的应用服务器(如 weblogic 或 websphere 等)上,我们可能更希望使用应用服务器所提供的数据源 。 应用服务器的数据源使用 jndi 方式来供调用者使用, spring 为此专门提供了引用 jndi 资源的 jndiobjectfactorybean 类 。 下面是一个简单的配置:

?
1
2
<bean id="datasource" class="org.springframework.jndi.jndiobjectfactorybean"
   p:jndiname="java:comp/env/jdbc/ds"/>

spring2.0+ 为获取 j2ee 资源提供了一个 jee 命名空间,通过 jee 命名空间,可以有效地简化 j2ee 资源的引用:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
   http://www.springframework.org/schema/jee
   http://www.springframework.org/schema/jee/spring-jee-4.0.xsd
   ">
 
  <jee:jndi-lookup id="datasource" jndi-name="java:comp/env/jdbc/ds"/>
 
</beans>

3 spring 数据源实现类

spring 本身也提供了一个简单的数据源实现类 drivermanagerdatasource ,它位于 org.springframework.jdbc.datasource 包中 。 这个类实现了 javax.sql.datasource 接口,但它并没有提供池化连接机制,每次调用 getconnection() 方法获取新连接时,只是简单地创建一个新的连接 。它不需要额外的依赖类,所以,这个数据源类比较适合在单元测试中使用 。

spring 数据源实现类既可以通过配置直接使用,也可以在代码中实例化调用:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
drivermanagerdatasource datasource=new drivermanagerdatasource();
datasource.setdriverclassname("com.mysql.jdbc.driver");
datasource.seturl("jdbc:mysql://127.0.0.1:3306/spring4");
datasource.setusername("root");
datasource.setpassword("");
 
try {
  connection connection=datasource.getconnection();
  if(connection.isclosed()){
    system.out.println("连接已关闭");
  }else{
    system.out.println("连接已开启");
  }
} catch (sqlexception e) {
  e.printstacktrace();
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://www.jianshu.com/p/d23f1d418fe6