Controlling Database Connections

Using DataSource

Spring 通过 DataSource 获取与数据库的连接。DataSource 是 JDBC 规范的一部分,是一个通用的连接工厂。它允许容器或框架对应用程序代码隐藏连接池和事务管理问题。作为开发人员,您不需要了解如何连接到数据库的详细信息。这是设置数据源的管理员的职责。您很可能在开发和测试代码时扮演这两个角色,但您不必一定要知道生产数据源是如何配置的。

当您使用 Spring 的 JDBC 层时,可以从 JNDI 获取数据源,也可以使用第三方提供的连接池实现配置您自己的数据源。传统的选项是用 bean 样式的 DataSource 类的 Apache Commons DBCP 和 C3P0;对于现代 JDBC 连接池,请考虑使用构建器样式 API 的 HikariCP。

你应该只将 DriverManagerDataSourceSimpleDriverDataSource 类(Spring 分发中包含)用于测试目的!这些变体不提供池化,且在对连接进行多次请求时执行效果不佳。

以下部分使用 Spring 的 DriverManagerDataSource 实现。稍后将涵盖其他几个 DataSource 变体。

要配置 DriverManagerDataSource

  1. 使用 DriverManagerDataSource 获取连接,如通常获取 JDBC 连接一样。

  2. 指定 JDBC 驱动程序的完全限定类名,以便 DriverManager 能够加载驱动程序类。

  3. 提供在不同 JDBC 驱动程序之间不同的 URL。(有关正确值,请参见驱动程序的文档。)

  4. 提供用户名和密码,以连接到数据库。

以下示例展示了如何配置 DriverManagerDataSource

  • Java

  • Kotlin

  • Xml

@Bean
DriverManagerDataSource dataSource() {
	DriverManagerDataSource dataSource = new DriverManagerDataSource();
	dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
	dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
	dataSource.setUsername("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean
fun dataSource() = DriverManagerDataSource().apply {
	setDriverClassName("org.hsqldb.jdbcDriver")
	url = "jdbc:hsqldb:hsql://localhost:"
	username = "sa"
	password = ""
}
<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 配置:

  • Java

  • Kotlin

  • Xml

@Bean(destroyMethod = "close")
BasicDataSource dataSource() {
	BasicDataSource dataSource = new BasicDataSource();
	dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
	dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
	dataSource.setUsername("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean(destroyMethod = "close")
fun dataSource() = BasicDataSource().apply {
	driverClassName = "org.hsqldb.jdbcDriver"
	url = "jdbc:hsqldb:hsql://localhost:"
	username = "sa"
	password = ""
}
<bean id="dataSource" class="org.apache.commons.dbcp2.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 配置:

  • Java

  • Kotlin

  • Xml

@Bean(destroyMethod = "close")
ComboPooledDataSource dataSource() throws PropertyVetoException {
	ComboPooledDataSource dataSource = new ComboPooledDataSource();
	dataSource.setDriverClass("org.hsqldb.jdbcDriver");
	dataSource.setJdbcUrl("jdbc:hsqldb:hsql://localhost:");
	dataSource.setUser("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean(destroyMethod = "close")
fun dataSource() = ComboPooledDataSource().apply {
	driverClass = "org.hsqldb.jdbcDriver"
	jdbcUrl = "jdbc:hsqldb:hsql://localhost:"
	user = "sa"
	password = ""
}
<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"/>

Using DataSourceUtils

DataSourceUtils 类是一个方便且强大的帮助程序类,它提供“静态”方法来从 JNDI 获取连接并在必要时关闭连接。它支持使用 DataSourceTransactionManagerJtaTransactionManagerJpaTransactionManager 的线程绑定 JDBC Connection

请注意,JdbcTemplate 意味着 DataSourceUtils 连接访问,在每次 JDBC 操作后使用它,隐式参与正在进行的事务。

Implementing SmartDataSource

SmartDataSource 接口应该由可以提供与关系数据库的连接的类实现。它扩展了 DataSource 接口,以允许使用它的类查询是否应该在给定操作后关闭连接。当您知道需要重用连接时,此用法很有效。

Extending AbstractDataSource

AbstractDataSource 是 Spring DataSource 实现的 abstract 基类。它实现了所有 DataSource 实现通用的代码。如果您编写自己的 DataSource 实现,则应扩展 AbstractDataSource 类。

Using SingleConnectionDataSource

SingleConnectionDataSource 类是对 SmartDataSource 接口的一个实现,它封装了一个在每次使用后不会关闭的单个 Connection。这不支持多线程。

如果任何客户端代码假设连接池中的连接(如使用持久性工具时)而调用 close,您应将 suppressClose 属性设置为 true。此设置会返回一个抑制关闭的代理,它封装物理连接。请注意,您不能再将它强制转换原生 Oracle Connection 或类似的对象。

SingleConnectionDataSource 主要是一个测试类。通常,它能够轻松地测试应用程序服务器外的代码,并与简单的 JNDI 环境结合使用。与 DriverManagerDataSource 相反,它始终重复使用相同连接,避免了物理连接的过度创建。

Using DriverManagerDataSource

DriverManagerDataSource 类是对标准 DataSource 接口的一个实现,它通过 bean 属性配置一个普通的 JDBC 驱动,并每次返回一个新的 Connection

此实现对于 Jakarta EE 容器外的测试和单机环境很有用,它既可用作 Spring IoC 容器中的 DataSource bean,也能与简单的 JNDI 环境结合使用。假设池中的 Connection.close() 会关闭连接,因此,任何支持 DataSource 的持久性代码都能运行。但是,使用 JavaBean 样式连接池(如 commons-dbcp)非常容易,即使是在测试环境中,使用此类连接池通常比使用 DriverManagerDataSource 更可取。

Using TransactionAwareDataSourceProxy

TransactionAwareDataSourceProxy 是一个目标 DataSource 的代理。该代理封装目标 DataSource 以添加对 Spring 管理的事务的感知。在这方面,它类似于事务性 JNDI DataSource(由 Jakarta EE 服务器提供)。

除了必须调用现有的代码并传递标准 JDBC DataSource 接口实现时,很少需要使用此类。在这种情况下,你仍然可以使用此代码,同时让此代码参与 Spring 管理的事务中。通常最好使用更高级的资源管理抽象编写自己的新代码,例如 JdbcTemplateDataSourceUtils

有关更多详细信息,请参阅 TransactionAwareDataSourceProxy javadoc。

Using DataSourceTransactionManager / JdbcTransactionManager

DataSourceTransactionManager 类是一个面向单个 JDBC DataSourcePlatformTransactionManager 实现。它将 JDBC Connection 从指定的 DataSource 绑定到当前正在执行的线程,这可能允许每个 DataSource 有一个线程绑定的 Connection

应用程序代码需要通过 DataSourceUtils.getConnection(DataSource) 检索 JDBC Connection,而不是 Java EE 的标准 DataSource.getConnection。它抛出未签名的 org.springframework.dao 异常,而不是已签名的 SQLException。所有框架类(如 JdbcTemplate)都隐式使用此策略。如果不与事务管理器配合使用,则查找策略的行为与 DataSource.getConnection 完全相同,因此可以在任何情况下使用。

DataSourceTransactionManager 类支持保存点(PROPAGATION_NESTED)、自定义隔离级别和超时,这些超时将适当地用作 JDBC 语句查询超时。为了支持后者,应用程序代码必须在为每个创建的语句使用 JdbcTemplate 或调用 DataSourceUtils.applyTransactionTimeout(..) 方法。

您可以使用 DataSourceTransactionManager(而不是 JtaTransactionManager)来处理单一资源情形,因为它不要求容器支持 JTA 事务协调器。在这些事务管理器之间切换仅是一个配置问题,前提是坚持必需的连接查找模式。请注意,JTA 不支持保存点或自定义隔离级别,并且具有不同的超时机制,但在 JDBC 资源和 JDBC 提交/回滚管理方面表现出类似的行为。

对于 JTA 风格的实际资源连接的延迟检索,Spring 为目标连接池提供了一个对应的 DataSource 代理类:请参阅 LazyConnectionDataSourceProxy。这对于可能没有实际语句执行的潜在空事务特别有用(在这种情况下从不获取实际资源),并且也适用于希望考虑事务同步只读标志和/或隔离级别的路由 DataSource 前面(例如 IsolationLevelDataSourceRouter)。

LazyConnectionDataSourceProxy 还为只读连接池提供特殊支持,以便在只读事务期间使用,从而避免在从主连接池获取 JDBC 连接时在每个事务的开头和结尾处切换 JDBC 连接的只读标记(这可能因 JDBC 驱动而有所不同)。

从 5.3 开始,Spring 提供了一个扩展的 JdbcTransactionManager 变体,它在提交/回滚时增加了异常转换功能(与 JdbcTemplate 一致)。DataSourceTransactionManager 将只会引发 TransactionSystemException(类似于 JTA),而 JdbcTransactionManager 会将数据库锁定失败等转换为相应的 DataAccessException 子类。请注意,应用程序代码需要为这些异常做好准备,而不仅仅是期望 TransactionSystemException。在这样的情况下,JdbcTransactionManager 是推荐的选择。

在异常行为方面,JdbcTransactionManager 大致等同于 JpaTransactionManagerR2dbcTransactionManager,是彼此的直接伴侣/替换。另一方面,DataSourceTransactionManager 等同于 JtaTransactionManager,并可以在其中作为直接替换。