所以你還應該使用 Spring 的 HibernateTemplate 和/或 JpaTemplate 嗎?

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

前幾天,我在 TSS 上閱讀了 Vigil Bose 的文章,看到了 HibernateDaoSupport 類別的用法。 由於這不再是 Spring 中使用 Hibernate 的建議方式,我想我最好再寫一篇部落格文章來討論它。

隨著 Spring 2.0 的出現,可以再次直接使用 Hibernate Session API。 問題是,在使用 Hibernate 時,放棄使用 HibernateTemplate 是否明智,或者放棄 Spring 提供的任何其他基於範本的方法。

使用 Spring XxxTemplates

在 Spring 1.0 中,我們引入了一種革命性的方式來使用拋出 checked exceptions 的資料存取 API。 Spring 提供的範本方法與其事務同步管理器以及 runtime exceptions 的廣泛使用,使得通常在資料存取程式碼中發現的任何 TCFTC(這是我們在 2005 年創造的 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)
  • 訊息傳遞 (JmsTempate)
  • 事務管理 (TransactionTemplate)
  • JNDI (JndiTemplate)

範本真的有必要嗎?

當使用使用 checked exceptions 的 API(而不是 runtime exceptions 或 unchecked exceptions)時,範本會增加很多價值,而且還會為您的程式碼庫增加很多一致性。 已經學習了 Spring 的 JdbcTemplate 的人可以很容易地開始使用 Spring 的 JdoTemplate 或 Spring 的 HibernateTemplate——使用這些範本的方法對於每個範本都是相似的。

Spring 範本方法最明顯的影響是例如 JDBC 的程式碼減少。 這主要是因為 checked exceptions 在範本中被翻譯為 runtime exceptions,從而無需在您的主線程式碼中捕獲異常。 其他原因包括透明的資源管理和與目前執行的事務的自動同步。 當然,很容易更改框架以在本機使用 runtime exceptions,而不是讓 Spring 執行此操作,這就是例如 Hibernate 從 3.0 版本開始所做的事情。 Hibernate 並不是唯一這樣做的技術——Java Persistence API 也在使用 runtime exceptions。

事實上,這些技術正在使用 runtime exceptions,這基本上使得這些技術的 Spring 範本等效項變得無用……至少在很大程度上是這樣,如果您從程式碼簡化的角度來看。 如果您僅僅為了減少執行 Hibernate 資料存取操作所需的程式碼量而使用 Spring HibernateTemplate,您會說您不一定必須使用範本! 但是,當查看上表時,我們可以發現 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 隨附的普通 API(即 Hibernate Session 或 JPA EntityManager)時,唯一不可直接使用的是將特定於技術的資料存取異常轉換為 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) 的儲存庫也是如此。 事實上,您甚至不需要更改後處理器或註釋。

如果您在無法使用註釋的環境(Java5 之前)中使用 Hibernate,您仍然可以享受自動異常翻譯; 使用 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 社群中所有即將到來的活動。

查看全部