控制数据库连接

使用 DataSource

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

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

您应该仅将 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://127.0.0.1:");
	dataSource.setUsername("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean
fun dataSource() = DriverManagerDataSource().apply {
	setDriverClassName("org.hsqldb.jdbcDriver")
	url = "jdbc:hsqldb:hsql://127.0.0.1:"
	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://127.0.0.1:");
	dataSource.setUsername("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean(destroyMethod = "close")
fun dataSource() = BasicDataSource().apply {
	driverClassName = "org.hsqldb.jdbcDriver"
	url = "jdbc:hsqldb:hsql://127.0.0.1:"
	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://127.0.0.1:");
	dataSource.setUser("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean(destroyMethod = "close")
fun dataSource() = ComboPooledDataSource().apply {
	driverClass = "org.hsqldb.jdbcDriver"
	jdbcUrl = "jdbc:hsqldb:hsql://127.0.0.1:"
	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"/>

使用 DataSourceUtils

DataSourceUtils 类是一个方便且功能强大的辅助类,它提供 static 方法来从 JNDI 获取连接,并在必要时关闭连接。它支持使用 DataSourceTransactionManager 的线程绑定 JDBC Connection,但也支持 JtaTransactionManagerJpaTransactionManager

请注意,JdbcTemplate 隐含了 DataSourceUtils 连接访问,在每个 JDBC 操作后面使用它,隐式地参与正在进行的事务。

实现 SmartDataSource

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

扩展 AbstractDataSource

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

使用 SingleConnectionDataSource

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

如果任何客户端代码在假设池化连接的情况下调用 close(如使用持久性工具时),则应将 suppressClose 属性设置为 true。此设置返回一个关闭抑制代理,该代理包装物理连接。请注意,您不能再将其转换为本机 Oracle Connection 或类似对象。

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

使用 DriverManagerDataSource

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

此实现对于 Jakarta EE 容器外部的测试和独立环境很有用,既可以作为 Spring IoC 容器中的 DataSource bean,也可以与简单的 JNDI 环境结合使用。假设池的 Connection.close() 调用会关闭连接,因此任何 DataSource 感知的持久性代码都应该可以工作。但是,即使在测试环境中,使用 JavaBean 样式的连接池(例如 commons-dbcp)也非常容易,因此几乎总是优选使用这样的连接池而不是 DriverManagerDataSource

使用 TransactionAwareDataSourceProxy

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

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

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

使用 DataSourceTransactionManager / JdbcTransactionManager

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

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

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

在单资源情况下,您可以使用DataSourceTransactionManager代替JtaTransactionManager,因为它不需要容器支持JTA事务协调器。在这些事务管理器之间切换仅仅是配置问题,前提是您坚持使用所需的连接查找模式。请注意,JTA不支持保存点或自定义隔离级别,并且具有不同的超时机制,但在JDBC资源和JDBC提交/回滚管理方面,其行为大体相似。

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

LazyConnectionDataSourceProxy还为只读事务期间使用的只读连接池提供特殊支持,避免了在每次从主连接池获取JDBC Connection时在事务的开始和结束处切换JDBC Connection的只读标志的开销(这可能会根据JDBC驱动程序而代价高昂)。

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

在异常行为方面,JdbcTransactionManager大致等效于JpaTransactionManager以及R2dbcTransactionManager,可以作为彼此的直接伴侣/替代。另一方面,DataSourceTransactionManager等效于JtaTransactionManager,可以作为直接替代。