那麼,您是否仍應使用 Spring 的 HibernateTemplate 和/或 JpaTemplate??

工程 | Alef Arendsen | 2007年6月26日 | ...

前幾天我讀了 Vigil Bose 在 TSS 上關於 使用 Spring 進行動態路由的文章,看到了 HibernateDaoSupport 類的用法。由於這不再是推薦的從 Spring 使用 Hibernate 的方式,我想我不如再寫一篇部落格來談談。

隨著 Spring 2.0 的出現,可以再次開始直接使用 Hibernate Session API 了。問題在於,在使用 Hibernate 或 Spring 提供的其他模板化方法時,是否明智地放棄使用 HibernateTemplate。

使用 Spring XxxTemplates

在 Spring 1.0 中,我們引入了一種處理丟擲檢查性異常的資料訪問 API 的革命性方法。Spring 提供的模板方法與其事務同步管理器以及對執行時異常的廣泛使用,使得 2005 年我們創造的 TCFTC (try/catch-finally-try/catch 的縮寫) 在資料訪問程式碼中變得完全過時。下面您可以看到 (一個簡化且不完全精確的版本) Spring 的模板方法能為您做什麼 (包含您本應自己編寫的程式碼片段)。 template.png

連線的獲取:如果事務同步已啟用(如果您正在使用 Spring 的事務管理基礎設施,那麼它就是啟用的),大多數時候,任何 Spring 模板在整個執行緒中都會使用相同的連線(實際上情況比這更復雜,但這會讓我們陷入太多細節)。

參與事務 同樣,在使用事務管理功能時,Spring 會自動將任何新連線關聯到當前事務。這同樣取決於當前的傳播設定等,但無論您如何看待它,您的核心程式碼都不會受到影響。

SQL 的指定:這(顯然)是您自己必須做的。SQL 最好使用繫結引數,以避免 SQL 注入的發生。引數作為引數傳遞給 JDBC 模板。

建立/執行語句和遍歷結果集:在您指定 SQL 後,Spring 將為您建立語句,設定您可能指定的任何引數,執行它併為您迴圈遍歷結果集。

解析結果集中的結果:如果您願意,您可以選擇自己解析結果集(或者如果您有複雜的解析要求),或者您可以讓 Spring 返回一個原始值列表,或者只返回結果集中的一個值。

異常的處理和轉換:這是 Spring 將可能發生的任何異常轉換為 Spring 自有的 DataAccessException 層級結構的地方,從而自動使呼叫程式碼免受所使用的資料訪問技術的影響。

連線的釋放:這是最後一塊拼圖,Spring 會釋放所有使用的資源。當然,如果事務同步已啟用,資源可能不會立即釋放。

模板可用於多種 API,例如

  • JDBC (JdbcTemplate)
  • Hibernate (HibernateTemplate)
  • iBatis (SqlMapClientTemplate)
  • JDO (JdoTemplate)
  • TopLink (TopLinkTemplate)
  • Messaging (JmsTempate)
  • Transaction management (TransactionTemplate)
  • JNDI (JndiTemplate)

模板真的有必要嗎?

在使用使用已檢查異常(與執行時異常或未檢查異常相對)的 API 時,模板會增加很多價值,但也會為您的程式碼庫增加很多一致性。學會了 Spring 的 JdbcTemplate 的人可以很容易地開始使用 Spring 的 JdoTemplate 或 Spring 的 HibernateTemplate——使用這些模板的方法對每個模板來說都是相似的。

Spring 模板方法最顯著的影響是程式碼的減少,例如對於 JDBC。這主要是因為已檢查異常在模板內部被轉換為執行時異常,從而無需在主線程式碼中捕獲異常。其他原因包括透明的資源管理和與當前正在進行的事務的自動同步。當然,將框架更改為本機使用執行時異常而不是由 Spring 來做是相當容易的,例如 Hibernate 從 3.0 版本開始就已經開始這樣做了。Hibernate 並不是唯一採用這種方式的技術——Java Persistence API 也使用執行時異常。

這些技術使用執行時異常這一事實基本上使得 Spring 模板在這些技術上的等價物變得無用……至少在很大程度上是這樣,如果您從程式碼簡化角度來看。如果您使用 Spring HibernateTemplate 純粹是為了減少執行 Hibernate 資料訪問操作所需的程式碼量,那麼您可能會說您不一定需要使用模板!然而,當我們檢視上表時,我們可以看到 Spring 在幕後做了比您想象的更多的工作。

除了部分簡化了錯誤處理問題(我們仍然需要將特定於資料訪問技術的異常轉換為 Spring 的 DataAccessExceptions)之外,事務管理和資源管理問題也透過底層資料訪問技術的幾項更改得到了解決。讓我們更詳細地看看這些。

資源管理 自 Hibernate 3.0.1(以及 Java Persistence API 從釋出之初)以來,Spring 就能夠管理底層資源,而無需您透過這些技術可用的任何模板。這意味著即使您直接使用 Hibernate API(例如透過 SessionFactory.getCurrentSession()),您仍然會使用 Spring 管理的 Hibernate Session。對於透過 JPA EntityManagerFactory 獲取的 EntityManager 也是如此。這是您不必再使用 Spring 的 HibernateTemplate 來獲得整合體驗的另一個原因。

事務管理 現在 Spring 能夠在您無需透過模板的情況下處理底層資源,Spring 也能夠將資源與在獲取資源時發生的任何事務同步。這意味著事務管理問題也得到了解決,而無需您透過模板。同樣,這意味著我們不一定需要再使用 Spring 的 HibernateTemplate 了。

錯誤處理 當您使用 Hibernate 或 JPA(即 Hibernate Session 或 JPA EntityManager)附帶的普通 API 時,唯一無法直接獲得的是將特定於技術的 JDAO 異常轉換為 Spring DataAccessException 層級結構的功能。不過,正如我們稍後將看到的,我們可以非常輕鬆地解決這個問題。

無模板化

那麼,如果我們不使用 HibernateTemplate,情況會是怎樣的呢?展示工作原理非常簡單。我們首先直接使用 Session API 而不是 HibernateTemplate。為了訪問 Hibernate Session,我們需要 SessionFactory,它將像往常一樣被注入。

public class HibernateAccountRepository implements AccountRepository {

	private SessionFactory factory;
	
	public HibernateAccountRepository(SessionFactory factory) {
		this.factory = factory;
	}
	
	public Account loadAccount(String username) {
		return (Account)factory.getCurrentSession()
		    .createQuery("from Account acc where acc.name = :name")
		    .setParameter("name", "Alef").uniqueResult();
	}
}

以下是我們將用於組裝應用程式的 XML。正如您所見,我們當然仍然使用 Spring 的方式來設定 Hibernate(使用 LocalSessionFactoryBean)。


<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
	<!-- the works -->
</bean>

<bean id="accountRepo" class="com.mycompany.HibernateAccountRepository">
	<constructor-arg ref="sessionFactory"/>
</bean>

現在,正如我之前所說,由於 Hibernate 3.0.1 的一項小改動,Spring 能夠為您管理 Hibernate Session,而無需您透過 Hibernate Session。唯一缺失的是異常轉換。要也實現這一點,您只需要用 @Repository 註釋(由 Spring 提供)來註釋儲存庫,並使用後處理器開啟異常轉換。


@Repository // from org.springframework.stereotype
public class HibernateAccountRepository implements AccountRepository {

	// see above for full impl...
}


<!-- for the other beans in the configuration, see above -->

<bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

後處理器將自動識別 @Repository 註釋,並指示 Spring 為此 bean 開啟異常轉換。該代理功能的工作原理實際上與本次討論無關。

請注意,這同樣適用於使用 Java Persistence API (JPA) 的儲存庫。事實上,您甚至不需要更改後處理器或註釋。

如果您在無法使用註解的環境中使用 Hibernate(Java5 之前),您仍然可以享受自動異常轉換;使用 AOP。首先宣告一個異常轉換器,然後宣告一段 AOP 配置,如下所示。


<bean id=“persistenceExceptionInterceptor
    class=“org.springframework.dao.support.PersistenceExceptionTranslationInterceptor"/>

<aop:config>
    <aop:advisor pointcut=“execution(* *..*Repository+.*(..))" 
                          advice-ref=“persistenceExceptionInterceptor" />
</aop:config>

這裡的切入點匹配任何實現 Repository 介面的類(更準確地說,是名稱以 Repository 結尾的介面)。

真正的問題是:選擇哪種方法??

要用典型的顧問的回答來回答:“這取決於”:)。讓我告訴您,我個人寧願不使用 HibernateTemplate 和 JpaTemplate,僅僅因為我認為它們現在提供的價值不夠。為了保持一致性,您可以爭辯說,選擇一種基於模板的方法可以使所有地方的情況相似;您仍然需要了解 Hibernate 的工作原理,並且對於更復雜的情況,您可能仍然希望直接使用 Session API。請注意,即使您使用 HibernateTemplate(透過 Spring 的 HibernateCallback),這仍然是可能的。

簡而言之(如 HibernateTemplateJpaTemplate 的 JavaDoc 所述),我建議您在新專案開始使用 Hibernate 或 JPA 時直接使用 Session 和/或 EntityManager API——請記住:Spring 試圖做到非侵入性,這是另一個很好的例子!

[更新:小錯別字] [更新:添加了關於無註解異常轉換的資訊]

獲取 Spring 新聞通訊

透過 Spring 新聞通訊保持聯絡

訂閱

領先一步

VMware 提供培訓和認證,助您加速進步。

瞭解更多

獲得支援

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進位制檔案,只需一份簡單的訂閱。

瞭解更多

即將舉行的活動

檢視 Spring 社群所有即將舉行的活動。

檢視所有