Controlling Database Connections
本节包括:
- 使用 DataSource
- 使用 DataSourceUtils
- 实现 SmartDataSource(智能数据源)
- 扩展 AbstractDataSource(抽象数据源)
- 使用 SingleConnectionDataSource
- 使用 DriverManagerDataSource
- 使用 TransactionAwareDataSourceProxy
- 使用 DataSourceTransactionManager
使用 DataSource
Using DataSource
Spring 通过 DataSource 获得与数据库的连接。DataSource 是 JDBC 规范的一部分,是一个 通用的连接工厂。它允许容器或框架从应用程序代码中 隐藏连接池 和 事务管理 问题。作为一个开发者,你不需要知道关于如何连接到数据库的细节。那是设置数据源的管理员的责任。在你开发和测试代码时,你很可能同时充当这两个角色,但你不一定要知道生产数据源是如何配置的。
当你使用 Spring 的 JDBC 层时,你可以从 JNDI 获取数据源,也可以用第三方提供的连接池实现来配置自己的数据源。传统的选择是 Apache Commons DBCP 和 C3P0 的 bean 风格的 DataSource 类;对于现代的 JDBC 连接池,可以考虑 HikariCP 及其构建器风格的 API。
:::info 你应该使用 DriverManagerDataSource 和 SimpleDriverDataSource 类(包括在 Spring 发行版中),只用于测试目的!这些变体不提供池,当有多个连接请求时,性能很差。这些变体不提供池,当对一个连接提出多个请求时,性能很差。 :::
下面的部分使用 Spring 的 DriverManagerDataSource 实现。其他几个数据源的变体将在后面介绍。
要配置 DriverManagerDataSource:
- 像通常获得 JDBC 连接一样,获得与 DriverManagerDataSource 的连接。
- 指定 JDBC 驱动的完全合格的类名,以便 DriverManager 可以加载驱动类。
- 提供一个 URL,不同的 JDBC 驱动有不同的 URL。(参见你的驱动程序的文档以获得正确的值)。
- 提供一个用户名和密码以连接到数据库。
下面的例子显示了如何在 Java 中配置一个 DriverManagerDataSource:
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
下面是 Mysql 的方式
package cn.mrcode.study.springdocsread.test;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
/**
* @author mrcode
*/
public class DataSourceTest {
public static void main(String[] args) {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
// 设置 mysql 的 jdbc 驱动
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/spring-read-docs");
dataSource.setUsername("root");
dataSource.setPassword("root");
}
}
:::tips 需要明白的是,像上面的代码,不一定要用在 spring ioc 环境中,可以直接 new 出来使用的 :::
下面的例子显示了相应的 XML 配置:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
接下来的两个例子显示了 DBCP 和 C3P0 的基本连接和配置。要了解更多有助于控制池子功能的选项,请看各自连接池实现的产品文档。
下面的例子显示了 DBCP 的配置:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
下面的例子显示了 C3P0 的配置:
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jdbc.driverClassName}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<context:property-placeholder location="jdbc.properties"/>
:::info
总结:
要实现连接池,可以扩展 DataSource 类,提供连接池功能。如同上面的第三方连接池 DBCP 和 C3P0 一样
:::
使用 DataSourceUtils
Using DataSourceUtils
DataSourceUtils 类是一个方便而强大的辅助类,它提供了静态方法来从 JNDI 获取连接,并在必要时关闭连接。它支持与线程绑定的连接,例如,DataSourceTransactionManager。
实现 SmartDataSource(智能数据源)
Implementing SmartDataSource
SmartDataSource 接口应该由能够提供与关系型数据库连接的类来实现。它扩展了 DataSource 接口,让使用它的类可以查询在给定的操作后是否应该关闭连接。当你知道你需要重复使用一个连接时,这种用法很有效。
扩展 AbstractDataSource(抽象数据源)
Extending AbstractDataSource
AbstractDataSource 是 Spring 的数据源实现的一个抽象基类。它实现了所有数据源实现所共有的代码。如果你编写自己的数据源实现,你应该扩展 AbstractDataSource类。
使用 SingleConnectionDataSource
Using SingleConnectionDataSource
SingleConnectionDataSource 类是 SmartDataSource 接口的一个实现,它封装了一个单一的连接,每次使用后都不会关闭。这不具备多线程能力。
如果任何客户端代码在池化连接的假设下调用关闭(如使用持久化工具时),你应该将 suppressClose 属性设置为 true。这个设置会返回一个包裹物理连接的抑制关闭的代理。请注意,你不能再把它投给一个本地的 Oracle 连接或类似的对象。
SingleConnectionDataSource 主要是一个测试类。它通常可以在应用服务器之外,结合简单的 JNDI 环境,轻松测试代码。与 DriverManagerDataSource 不同的是,它一直重复使用同一个连接,避免了过度创建物理连接。
使用 DriverManagerDataSource
Using DriverManagerDataSource
DriverManagerDataSource 类是标准 DataSource 接口的一个实现,它通过 bean 属性配置一个普通的 JDBC 驱动,并且每次都返回一个新的 Connection。
这个实现对于Java EE 容器之外的测试和独立环境非常有用,可以作为 Spring IoC 容器中的 DataSource Bean,或者与简单的 JNDI 环境相结合。池假设 Connection.close()
调用关闭连接,所以任何 DataSource 感知的持久化代码都应该工作。然而,使用 JavaBean 风格的连接池(如 commons-dbcp)是非常容易的,即使是在测试环境中,几乎总是倾向于使用这种连接池而不是 DriverManagerDataSource。
使用 TransactionAwareDataSourceProxy
Using TransactionAwareDataSourceProxy
TransactionAwareDataSourceProxy 是一个目标 DataSource 的代理。该代理包装了该目标数据源,以增加对 Spring 管理的事务的认识。在这方面,它类似于由 Java EE 服务器提供的事务性 JNDI 数据源。
:::info 很少有必要使用这个类,除非已经存在的代码必须被调用并传递给一个标准的 JDBC 数据源接口实现。在这种情况下,你仍然可以让这段代码可用,同时,让这段代码参与 Spring 管理的事务。一般来说,最好是通过使用更高级别的资源管理抽象来编写自己的新代码,如 JdbcTemplate 或 DataSourceUtils。 :::
更多细节见 TransactionAwareDataSourceProxy javadoc。
使用 DataSourceTransactionManager
Using DataSourceTransactionManager
DataSourceTransactionManager 类是一个针对单一 JDBC 数据源的 PlatformTransactionManager 实现。它将一个 JDBC 连接从指定的数据源绑定到当前执行的线程,可能允许每个数据源有一个线程连接。
应用程序代码需要通过 DataSourceUtils.getConnection(DataSource)
而不是 Java EE 的标准 DataSource.getConnection
来检索 JDBC 连接。它抛出未检查的 org.springframework.dao
异常,而不是受检查的的 SQLExceptions。所有框架类(如 JdbcTemplate)都隐含地使用这个策略。如果不与该事务管理器一起使用,该查找策略的行为与普通策略完全相同。因此,它可以在任何情况下使用。
DataSourceTransactionManager 类支持自定义隔离级别和超时,这些超时将作为适当的 JDBC 语句查询超时来应用。为了支持后者,应用程序代码必须使用 JdbcTemplate 或为每个创建的语句调用 DataSourceUtils.applyTransactionTimeout(.)
方法。
在单一资源的情况下,你可以使用这个实现而不是 JtaTransactionManager,因为它不需要容器支持 JTA。在两者之间切换只是一个配置问题,只要你坚持使用所需的连接查找模式。JTA 不支持自定义隔离级别。