控制数据库连接
本节内容包括:
使用 DataSource
Spring 通过 DataSource
获取与数据库的连接。DataSource
是 JDBC 规范的一部分,是一个通用的连接工厂。它允许容器或框架将连接池和事务管理问题隐藏在应用程序代码之外。作为开发人员,你不需要了解如何连接到数据库的详细信息。这是设置数据源的管理员的责任。在开发和测试代码时,你很可能同时扮演这两个角色,但不一定需要知道生产环境中的数据源是如何配置的。
当你使用 Spring 的 JDBC 层时,你可以从 JNDI 获取数据源,或者使用第三方提供的连接池实现来配置自己的数据源。传统的选择是使用 Apache Commons DBCP 和 C3P0,它们提供了 bean 风格的 DataSource
类;对于现代的 JDBC 连接池,可以考虑使用 HikariCP,它提供了构建器风格的 API。
你应该仅将 DriverManagerDataSource
和 SimpleDriverDataSource
类(包含在 Spring 发行版中)用于测试目的!这些变体不提供连接池,并且在多次请求连接时性能较差。
以下部分使用 Spring 的 DriverManagerDataSource
实现。稍后将介绍其他几种 DataSource
变体。
配置一个 DriverManagerDataSource
:
-
使用
DriverManagerDataSource
获取连接,就像通常获取 JDBC 连接一样。 -
指定 JDBC 驱动程序的完全限定类名,以便
DriverManager
可以加载驱动程序类。 -
提供一个在 JDBC 驱动程序之间有所不同的 URL。(请参阅驱动程序的文档以获取正确的值。)
-
提供用户名和密码以连接到数据库。
以下示例显示了如何配置一个 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"/>
使用 DataSourceUtils
DataSourceUtils
类是一个方便且强大的辅助类,提供了用于从 JNDI 获取连接和在必要时关闭连接的 static
方法。它支持与 DataSourceTransactionManager
绑定线程的 JDBC Connection
,同时也支持 JtaTransactionManager
和 JpaTransactionManager
。
请注意,JdbcTemplate
意味着使用 DataSourceUtils
进行连接访问,它在每个 JDBC 操作背后使用,隐式参与正在进行的事务。
实现 SmartDataSource
SmartDataSource
接口应由能够提供与关系数据库连接的类实现。它扩展了 DataSource
接口,以便使用它的类可以查询在给定操作后是否应关闭连接。当您知道需要重用连接时,这种用法是高效的。
扩展 AbstractDataSource
AbstractDataSource
是 Spring 的 DataSource
实现的一个 abstract
基类。它实现了所有 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 管理的事务中。通常更倾向于使用更高级别的资源管理抽象来编写自己的新代码,例如 JdbcTemplate
或 DataSourceUtils
。
请参阅 TransactionAwareDataSourceProxy javadoc 以获取更多详细信息。
使用 DataSourceTransactionManager
/ JdbcTransactionManager
DataSourceTransactionManager
类是一个用于单个 JDBC DataSource
的 PlatformTransactionManager
实现。它将指定 DataSource
的 JDBC Connection
绑定到当前执行的线程,可能允许每个 DataSource
有一个线程绑定的 Connection
。
应用程序代码需要通过 DataSourceUtils.getConnection(DataSource)
来检索 JDBC Connection
,而不是使用 Java EE 的标准 DataSource.getConnection
。它抛出未检查的 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 连接的只读标志所带来的开销(这可能会根据 JDBC 驱动程序的不同而代价高昂)。
从 5.3 版本开始,Spring 提供了一个扩展的 JdbcTransactionManager
变体,它在提交/回滚时增加了异常翻译功能(与 JdbcTemplate
对齐)。DataSourceTransactionManager
只会抛出 TransactionSystemException
(类似于 JTA),而 JdbcTransactionManager
会将数据库锁定失败等情况翻译为相应的 DataAccessException
子类。需要注意的是,应用程序代码需要为此类异常做好准备,而不仅仅是期望 TransactionSystemException
。在这种情况下,JdbcTransactionManager
是推荐的选择。
在异常行为方面,JdbcTransactionManager
大致等同于 JpaTransactionManager
和 R2dbcTransactionManager
,可以作为彼此的直接伙伴/替代品。而 DataSourceTransactionManager
则等同于 JtaTransactionManager
,可以直接替换它。