数据源

不管采用何种持久化技术,都必须拥有数据连接。在 Spring 中,数据连接是通过数据源获得的。在以往的应用中,数据源一般是由 Web 应用服务器提供的。在 Spring 中,不但可以通过 JNDI 获取应用服务器的数据源,也可以直接在 Spring 容器中配置数据源。此外,还可以通过代码的方式创建一个数据源,以便进行无容器依赖的单元测试。

1.配置一个数据源

Spring 在第三方依赖包中包含了两个数据源的实现类包:其一是 Apache 的 DBCP;其二是 C3P0。可以在 Spring 配置文件中利用二者中的任何一个配置数据源。

1)DBCP数据源

DBCP是一个依赖 Jakarta commons-pool 对象池机制的数据库连接池,所以在类路径下还必须包括 commons-pool 的类包。下面是使用 DBCP 配置 MySQL 数据源的片段:

<bean id="dataSource"
    class="org.apache.commons.dbcp.BasicDataSource" destory-method="close"
    p:driverC1assName="com.mysql.jdbc.Driver"
    p:url="jdbc:mysql://localhost:3309/samp1edb"
    p:username="root"
    p:password="1234"/>

BasicDataSource 提供了 close() 方法关闭数据源,所以必须设定 destroy-method="close" 属性,以便 Spring 容器关闭时,数据源能够正常关闭。

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

除以上必需的数据源属性外,还有一些常用的属性,如下表所示。

如果采用 DBCP 的默认配置,由于 testOnBorrow 属性的默认值为 true,数据源在将连接将交给 DAO 前,会事先检测这个连接是否是好的,如果连接有问题(在数据库端被关闭),则会取一个其他的连接给 DAO,所以并不会有“8小时问题”。如果每次将连接交给 DAO 时都检测连接的有效性,那么在高并发的应用中将会带来性能问题,因为它会需要更多的数据库访问请求。

一种推荐的高效方式是:将 testOnBorrow 设置为 false,而将 testWhileIdle 设置为 true,再设置好 timeBetweenEvictionRunsMillis 值。这样,DBCP 将通过一个后台线程定时地对空闲连接进行检测,当发现无用的空闲连接(那些被数据库关闭的连接)时,就会将它们清除掉。只要将 timeBetweenEvictionRunsMillis 值设置为小于8小时,那些被 MySQL 关闭的空闲连接就可以被清除出去,从而避免了“8小时问题”。

当然,MySQL 本身可以通过调整 interactive-timeout(以秒为单位)配置参数,更改空闲连接的过期时间。所以,在设置 timeBetweenEvictionRunsMillis 值时,必须首先获知 MySQL 空闲连接的最大过期时间。

2)C3P0数据源

C3P0 是一个开放源码的 JDBC 数据源实现项目,实现了 JDBC3 和 JDBC2 扩展规范说明的 Connection 和 Statement 池。下面使用 C3P0 配置一个 Oracle 数据源,代码如下:

<bean id="dataSource"
  class="com.mchange.v2.c3p0.ComboPooledDataSource" destory-method="close"
  p:driverC1assName="oracle.jdbc.driver.OracleDriver"
  p:jdbcUrl="jdbc:oracle:thin:@localhost:1521:ora9i"
  p:use="root"
  p:password="1234"/>

ComboPooledDataSource 和 BasicDataSource 一样提供了一个用于关闭数据源的 close() 方法,这样就可以保证 Spring 容器关闭时数据源能够被成功释放。

C3P0 拥有比 DBCP 更丰富的配置属性,通过这些属性,可以对数据源进行各种有效的控制。

1)acquireIncrement:当连接池中的连接用完时,C3P0 一次性创建新连接的数目。

2)acquireRetryAttempts:定义在从数据库获取新连接失败后重复尝试获取的次数,默认为30。

3)acquireRetryDelay:尝试获取连接的间隔时间,单位为毫秒,默认为1000。

4)autoCommitOnCIose:连接关闭时默认将所有未提交的操作回滚。默认为 false5)automaticTestTabIe:C3P0 将创建一张名为 Test 的空表,并使用其自带的查询语句进行测试。如果定义了这个参数,那么属性 preferredTestQuery 将被忽略。用户不能在这张Test表上进行任何操作,它仅为 C3P0 测试所用。默认为 null6)breakAfterAcquireFailure:获取连接失败将会引起所有等待获取连接的线程抛出异常。但是数据源仍有效保留,并在下次调用 getConnection() 方法时继续尝试获取连接。如果设为 true,那么在尝试获取连接失败后,该数据源将声明已断开并永久关闭。默认为 false7)checkoutTimeout:当连接池用完时,客户端调用 getConnection() 方法后等待获取新连接的时间,超时后将抛出 SQLException,如果设为 0 则无限期等待。单位为毫秒,默认为 08)connectionTesterClassName:通过实现 ConnectionTester 或 QueryConnectionTester 的类来测试连接,类名需要设置为全限定名。默认为 com.mchange.v2.C3P0.impl.DefaultConnectionTester。

9)idleConnectionTestPeriod:间隔多少秒检查所有连接池中的空闲连接。默认为0,表示不检查。

10)initialPooISize:初始化时创建的连接数,应在 minPoolSize 与 maxPoolSize 之间取值。默认为3。

11)maxIdIeTime:最大空闲时间,超过空闲时间的连接将被丢弃。为0或负数则永不丢弃。默认为0。

12)maxPoolSize:连接池中保留的最大连接数。默认为15。

13)maxStatements:JDBC的标准参数,用以控制数据源内加载的 PreparedStatement数量。但由于预缓存的 Statement 属于单个 Connection 而不是整个连接池,所以设置这个参数需要考虑多方面的因素,如果 maxStatements 与 maxStatementsPerConnection 均为 0,则缓存被关闭。默认为0。

14)maxStatementsPerConnection:连接池内单个连接所拥有的最大缓存 Statement 数。默认为0。

15)numHelperThreads:C3P0是异步操作的,缓慢的 JDBC 操作通过帮助进程完成。扩展这些操作可以有效地提升性能,通过多线程实现多个操作同时被执行。默认为3。

16)preferredTestQuery:定义所有连接测试都执行的测试语句。在使用连接测试的情况下,这个参数能显著提高测试速度。测试的表必须在初始数据源的时候就存在。默认为null。

17)propertyCycle:用户修改系统配置参数执行前最多等待的秒数。默认为 30018)testConnectionOnCheckout:因性能消耗大,请只在需要的时候使用它。如果设为 true,那么在每个 connection 提交的时候都将校验其有效性。建议使用 idleConnectionTestPeriod 或 automaticTestTabIe 等方法来提升连接测试的性能。默认为 false19)testConnectionOnCheckin:如果设为true,那么在取得连接的同时将校验连接的有效性。默认为 false。

对于连接有效性的检测,请参照我们推荐的 DBCP 配置方式。

3)使用属性文件

数据源的配置信息有可能经常需要改动,同时可能被其他工程复用。此外,用户名密码等信息比较敏感,可能需要使用特别的安全措施。所以一般将数据源的配置信息独立到一个属性文件中,通过 <context:property-placeholder> 引入属性文件,以 ${xxx} 的方式引用属性。示例代码如下:

<context:property-placeholder
        location="/WEB-INF/jdbc.properties" />

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close" 
        p:driverClassName="${driverClassName}" 
        p:url="${url}"
        p:username="${userName}" 
        p:password="${password}" />

在 jdbc.properties 属性文件中定义属性值,如下:

dbName=sampledb
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/${dbName}
userName=root
password=123456

这里,属性文件的内容是以明文方式存放的。
注意:
经常有开发者在 ${xxx} 前后不小心输入一些空格,这些空格字符将和变量合并后作为属性的值。如 p:username="__${userName}__" 的属性配置项,在前后都有空格,被解析后,username的值为“__root__”了,这将造成最终的错误,因此需要特别小心。

2.获取JNDI数据源

如果应用配置在高性能的应用服务器(如 WebLogic 或 WebSphere 等)上,则可能更希望使用应用服务器本身提供的数据源。应用服务器的数据源使用 JNDI 开放调用者使用,Spring 为此专门提供了引用 JNDI 数据源的 JndiObjectFactoryBean 类。下面是一个简单的配置:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"
    p:jndiName="java:comp/env/jdbc/bbt"/>

通过 jndiName 指定引用的 JNDI 数据源名称。

Spring 为获取 Java EE 资源提供了一个 jee 命名空间,通过 jee 命名空间,可以有效地简化 Java EE 资源的引用。下面是使用 jee 命名空间引用 JNDI 数据源的配置:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    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.xsd">
    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/bbt">
</beans>

3.Spring 的数据源实现类

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

下面来看一下 DriverManagerDataSource 类的简单使用,如下:

DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3309/sampledb");
ds.setUsername("root");
ds.setPassword("1234");
Connection actualCon = ds.getConnection();

当然,也可以通过配置的方式直接使用 DriverManagerDataSource 类。

原文地址:https://www.cnblogs.com/jwen1994/p/11241685.html