控制資料庫連線

使用 DataSource

Spring 透過 DataSource 獲取資料庫連線。DataSource 是 JDBC 規範的一部分,是一個通用的連線工廠。它允許容器或框架嚮應用程式碼隱藏連線池和事務管理問題。作為開發者,你無需知道如何連線資料庫的詳細資訊。這是設定資料來源的管理員的責任。在你開發和測試程式碼時,你很可能同時扮演這兩種角色,但你不一定非要知道生產環境中的資料來源是如何配置的。

當你使用 Spring 的 JDBC 層時,你可以從 JNDI 獲取資料來源,或者使用第三方提供的連線池實現來配置你自己的資料來源。傳統的選擇是 Apache Commons DBCP 和 C3P0,它們提供 Bean 風格的 DataSource 類;對於現代 JDBC 連線池,可以考慮使用 HikariCP,它提供構建器風格的 API。

你應該僅將 Spring 發行版中包含的 DriverManagerDataSourceSimpleDriverDataSource 類用於測試目的!這些變體不提供連線池,並且在多次請求連線時效能很差。

以下部分使用 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://:");
	dataSource.setUsername("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean
fun dataSource() = DriverManagerDataSource().apply {
	setDriverClassName("org.hsqldb.jdbcDriver")
	url = "jdbc:hsqldb:hsql://:"
	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://:");
	dataSource.setUsername("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean(destroyMethod = "close")
fun dataSource() = BasicDataSource().apply {
	driverClassName = "org.hsqldb.jdbcDriver"
	url = "jdbc:hsqldb:hsql://:"
	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://:");
	dataSource.setUser("sa");
	dataSource.setPassword("");
	return dataSource;
}
@Bean(destroyMethod = "close")
fun dataSource() = ComboPooledDataSource().apply {
	driverClass = "org.hsqldb.jdbcDriver"
	jdbcUrl = "jdbc:hsqldb:hsql://:"
	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 以及 JtaTransactionManagerJpaTransactionManager 配合使用的執行緒繫結 JDBC Connection

請注意,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 管理的事務。通常更傾向於使用更高階的資源管理抽象來編寫新的程式碼,例如 JdbcTemplateDataSourceUtils

有關更多詳細資訊,請參閱 TransactionAwareDataSourceProxy 的 Javadoc。

使用 DataSourceTransactionManager / JdbcTransactionManager

DataSourceTransactionManager 類是用於單個 JDBC DataSourcePlatformTransactionManager 實現。它將指定 DataSource 的 JDBC Connection 繫結到當前執行的執行緒,潛在地允許每個 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 連線時在每個事務開始和結束時切換連線只讀標誌的開銷(這可能取決於 JDBC 驅動程式而成本較高)。

自 5.3 版本起,Spring 提供了一個擴充套件的 JdbcTransactionManager 變體,它在提交/回滾時增加了異常轉換能力(與 JdbcTemplate 對齊)。DataSourceTransactionManager 只會丟擲 TransactionSystemException(類似於 JTA),而 JdbcTransactionManager 會將資料庫鎖定失敗等轉換為相應的 DataAccessException 子類。請注意,應用程式碼需要為這些異常做好準備,而不僅僅是預期 TransactionSystemException。在這種情況下,JdbcTransactionManager 是推薦的選擇。

在異常行為方面,JdbcTransactionManager 大致等同於 JpaTransactionManagerR2dbcTransactionManager,它們可以作為彼此的直接配套或替代品。而 DataSourceTransactionManager 則等同於 JtaTransactionManager,並可以在該處作為直接替代品。