控制数据库连接
使用 DataSource
Infra 通过 DataSource 获取与数据库的连接。DataSource 是 JDBC 规范的一部分,是一个通用的连接工厂。它允许容器或框架向应用程序代码隐藏连接池和事务管理问题。作为开发人员,您无需了解有关如何连接到数据库的详细信息。这是设置数据源的管理员的责任。在开发和测试代码时,您很可能同时担任这两个角色,但您不一定非要了解生产数据源是如何配置的。
当您使用 Infra JDBC 层时,您可以从 JNDI 获取数据源,或者您可以使用第三方提供的连接池实现来配置自己的数据源。传统的选择是 Apache Commons DBCP 和 C3P0,带有 bean 风格的 DataSource 类;对于现代 JDBC 连接池,请考虑使用带有构建器风格 API 的 HikariCP。
您应该仅出于测试目的使用 DriverManagerDataSource 和 SimpleDriverDataSource 类(包含在 Infra 发行版中)!这些变体不提供池化,并且在发出多个连接请求时性能不佳。
|
以下部分使用 Infra DriverManagerDataSource 实现。后面将介绍其他几个 DataSource 变体。
要配置 DriverManagerDataSource:
-
像通常获取 JDBC 连接一样,使用
DriverManagerDataSource获取连接。 -
指定 JDBC 驱动程序的完全限定类名,以便
DriverManager可以加载驱动程序类。 -
提供因 JDBC 驱动程序而异的 URL。(有关正确值,请参阅驱动程序的文档。)
-
提供用户名和密码以连接到数据库。
以下示例显示了如何配置 DriverManagerDataSource:
@Configuration
class DriverManagerDataSourceConfiguration {
@Component
static DriverManagerDataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
}
接下来的两个示例显示了 DBCP 和 C3P0 的基本连接和配置。要了解有助于控制池化功能的更多选项,请参阅相应连接池实现的产品文档。
以下示例显示了 DBCP 配置:
@Configuration
class BasicDataSourceConfiguration {
@Bean(destroyMethod = "close")
static BasicDataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbcDriver");
dataSource.setUrl("jdbc:hsqldb:hsql://localhost:");
dataSource.setUsername("sa");
dataSource.setPassword("");
return dataSource;
}
}
以下示例显示了 C3P0 配置:
@Configuration
class ComboPooledDataSourceConfiguration {
@Bean(destroyMethod = "close")
static 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;
}
}
使用 DataSourceUtils
DataSourceUtils 类是一个方便且功能强大的辅助类,它提供 static 方法以从 JNDI 获取连接并在必要时关闭连接。它支持带有 DataSourceTransactionManager 的线程绑定 JDBC Connection,但也支持 JtaTransactionManager 和 JpaTransactionManager。
请注意,JdbcTemplate 意味着 DataSourceUtils 连接访问,在每个 JDBC 操作后面使用它,隐式参与正在进行的事务。
实现 SmartDataSource
SmartDataSource 接口应由能够提供与关系数据库连接的类来实现。它扩展了 DataSource 接口,让使用它的类查询在给定操作后是否应关闭连接。当您知道需要重用连接时,这种用法非常有效。
扩展 AbstractDataSource
AbstractDataSource 是 Infra 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 容器之外的测试和独立环境非常有用,既可以作为 Infra IoC 容器中的 DataSource bean,也可以结合简单的 JNDI 环境。假设池化的 Connection.close() 调用会关闭连接,因此任何具有 DataSource 感知的持久性代码都应该可以工作。但是,使用 JavaBean 风格的连接池(如 commons-dbcp)非常容易,即使在测试环境中也是如此,因此几乎总是首选使用此类连接池而不是 DriverManagerDataSource。
使用 TransactionAwareDataSourceProxy
TransactionAwareDataSourceProxy 是目标 DataSource 的代理。代理包装该目标 DataSource 以增加对 Infra 管理事务的感知。在这方面,它类似于 Jakarta EE 服务器提供的事务性 JNDI DataSource。
除非必须调用现有代码并传递标准 JDBC DataSource 接口实现,否则很少希望使用此类。在这种情况下,您仍然可以让此代码可用,同时让此代码参与 Infra 管理的事务。通常最好使用更高级别的资源管理抽象(如 JdbcTemplate 或 DataSourceUtils)编写自己的新代码。
|
有关更多详细信息,请参阅 TransactionAwareDataSourceProxy javadoc。
使用 DataSourceTransactionManager / JdbcTransactionManager
DataSourceTransactionManager 类是单个 JDBC DataSource 的 PlatformTransactionManager 实现。
它将指定 DataSource 中的 JDBC Connection 绑定到当前执行线程,这可能允许每个 DataSource 有一个线程绑定的 Connection。
应用程序代码需要通过 DataSourceUtils.getConnection(DataSource)
而不是 Java EE 的标准 DataSource.getConnection 来获取 JDBC Connection。
它抛出非受检的 infra.dao 异常而不是受检的 SQLExceptions。
所有框架类(如 JdbcTemplate)都隐式使用此策略。如果未与事务管理器一起使用,
则查找策略的行为与 DataSource.getConnection 完全相同,因此可以在任何情况下使用。
DataSourceTransactionManager 类支持保存点(PROPAGATION_NESTED)、自定义隔离级别和超时,
这些超时作为适当的 JDBC 语句查询超时应用。为了支持后者,应用程序代码必须使用 JdbcTemplate
或为每个创建的语句调用 DataSourceUtils.applyTransactionTimeout(..) 方法。
在单一资源的情况下,您可以使用 DataSourceTransactionManager 代替 JtaTransactionManager,
因为它不需要容器支持 JTA 事务协调器。在这些事务管理器之间切换只是配置问题,前提是您坚持使用所需的连接查找模式。
请注意,JTA 不支持保存点或自定义隔离级别,并且具有不同的超时机制,但在 JDBC 资源和 JDBC 提交/回滚管理方面表现出类似的行为。
对于 JTA 风格的实际资源连接延迟检索,Infra 为目标连接池提供了相应的 DataSource 代理类:
请参阅 LazyConnectionDataSourceProxy。
这对于可能没有实际语句执行的空事务(在这种情况下从不获取实际资源)特别有用,并且在路由 DataSource 之前也很有用,这意味着要考虑事务同步的只读标志和/或隔离级别(例如 IsolationLevelDataSourceRouter)。
LazyConnectionDataSourceProxy 还为在只读事务期间使用的只读连接池提供了特殊支持,
避免了在从主连接池获取 JDBC 连接时在每个事务的开始和结束时切换其只读标志的开销(这可能很昂贵,具体取决于 JDBC 驱动程序)。
从 4.0 开始,Infra 提供了一个扩展的 JdbcTransactionManager 变体,
它增加了在提交/回滚时的异常转换功能(与 JdbcTemplate 对齐)。DataSourceTransactionManager
只会抛出 TransactionSystemException(类似于 JTA),而 JdbcTransactionManager 会将数据库锁定失败等转换为相应的 DataAccessException 子类。
请注意,应用程序代码需要准备好应对此类异常,而不是仅仅期望 TransactionSystemException。在这种情况下,JdbcTransactionManager 是推荐的选择。
|
在异常行为方面,JdbcTransactionManager 大致相当于 JpaTransactionManager,也相当于 R2dbcTransactionManager,
可以作为彼此的直接伴侣/替代品。另一方面,DataSourceTransactionManager 相当于 JtaTransactionManager,可以在那里作为直接替代品。