浅谈在Spring中如何使用数据源(DBCP、C3P0、JNDI)
在 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 数据源的配置片段:
<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 数据源的配置片段:
<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 类 。 下面是一个简单的配置:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean" p:jndiName="java:comp/env/jdbc/ds"/>
Spring2.0+ 为获取 J2EE 资源提供了一个 jee 命名空间,通过 jee 命名空间,可以有效地简化 J2EE 资源的引用:
<?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 数据源实现类既可以通过配置直接使用,也可以在代码中实例化调用:
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(); }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。