什麼是休眠? Hibernate Core 實現的基礎
已發表: 2013-04-17Hibernate 是一個開源的 Java 持久性框架項目。 使用 HQL 和 SQL 執行強大的對象關係映射和查詢數據庫。
一般來說,廣泛使用的庫都經過精心設計和實現,向它們學習一些編碼最佳實踐非常有趣。
讓我們看一下 hibernate 核心庫的內部,並發現它的一些設計關鍵。
在這篇文章中, JArchitect
對 Hibernate Core 進行了分析,以深入了解其設計和實現。
按功能打包
Package-by-feature
使用包來反映功能集。 它將與單個功能(並且僅該功能)相關的所有項目放入單個目錄/包中。 這導致包具有高內聚性和高度模塊化,並且包之間的耦合最小。 緊密結合的項目彼此相鄰放置。
Hibernate 核心包含許多包,每個包都與特定功能 hql、sql 等相關。
耦合
低耦合是可取的,因為在應用程序的一個區域中的更改將需要在整個應用程序中進行較少的更改。 從長遠來看,這可以減少與修改和向應用程序添加新功能相關的大量時間、精力和成本。
以下是使用接口帶來的三個主要好處:
- 接口提供了一種定義促進重用的契約的方法。 如果一個對象實現了一個接口,那麼該對象將符合一個標準。 使用另一個對象的對象稱為消費者。 接口是對象與其消費者之間的契約。
- 接口還提供了使程序更易於理解的抽象級別。 接口允許開發人員開始討論代碼行為的一般方式,而不必涉及許多詳細的細節。
- 接口強制組件之間的低耦合,這使得保護接口使用者免受實現接口的類中的任何實現更改變得容易。
讓我們搜索 Hibernate Core 定義的所有接口,為此我們使用CQLinq
來查詢代碼庫。
1 |
from t in Types where t . IsInterface select t |
如果我們的主要目標是強制低耦合,那麼在使用接口時會出現一個常見錯誤,這可能會破壞使用它們的實用性。 這是使用具體類而不是接口,為了更好地解釋這個問題,讓我們舉個例子:
類 A 實現了包含 calculate() 方法的接口 IA,消費者類 C 是這樣實現的
1 2 3 4 5 6 7 8 9 10 11 |
public class C < br > { … . public void calculate ( ) { … . . m_a . calculate ( ) ; … . } A m_a ; } |
類 C 不是引用接口 IA,而是引用類 A,在這種情況下我們失去了低耦合的好處,這種實現有兩個主要缺點:
- 如果我們決定使用 IA 的另一個實現,我們必須更改 C 類的代碼。
- 如果在 A 中添加了一些在 IA 中不存在的方法,而 C 使用它們,我們也失去了使用接口的合同利益。
C# 為語言引入了顯式接口實現能力,以確保永遠不會從對具體類的引用調用來自 IA 的方法,而只會從對接口的引用調用。 這種技術對於保護開發人員免於失去使用接口的好處非常有用。
使用 JArchitect,我們可以使用CQLinq
檢查此類錯誤,其想法是從其他方法直接使用的具體類中搜索所有方法。
1 2 3 4 5 6 7 8 9 |
from m in Methods where m . NbMethodsCallingMe > 0 && m . ParentType . IsClass && ! m . ParentType . IsThirdParty && ! m . ParentType . IsAbstract let interfaces = m . ParentType . InterfacesImplemented from i in interfaces where i . Methods . Where ( a = > a . Name == m . Name && a . ParentType ! = m . ParentType ) . Count ( ) > 0 select new { m , m . ParentType , i } |
例如,實現 SessionFactoryImplementor 接口的 SessionFactoryImpl 中的 getEntityPersister 方法就關注這個問題。
讓我們搜索直接調用 SessionFactoryImpl.getEntityPersister 的方法。
1 2 3 |
from m in Methods where m . IsUsing ( "org.hibernate.internal.SessionFactoryImpl.getEntityPersister(String)" ) select new { m , m . NbBCInstructions } |

像 SessionImpl.instantiate 這樣的方法直接調用 getEntityPersister,而不是通過接口傳遞,這破壞了使用接口的好處。 幸運的是,hibernate core 不包含很多有這個問題的方法。
與外部罐子耦合
當使用外部庫時,最好檢查一下我們是否可以輕鬆地將第三方庫更改為另一個而不影響整個應用程序,有很多原因可以鼓勵我們更改第三方庫。
另一個庫可以:
- 擁有更多功能
- 更加強大
- 更安全
讓我們以antlr lib
為例,它用於解析hql查詢,想像一下另一個比antlr更強大的解析器被創建,我們可以很容易地用新的解析器改變antlr嗎?
要回答這個問題,讓我們搜索一下 hibernate 中哪些方法直接使用它:
1 2 3 |
from m in Methods where m . IsUsing ( "antlr-2.7.7" ) select new { m , m . NbBCInstructions } |
哪些間接使用了它:
1 2 3 4 |
from m in Projects . WithNameNotIn ( "antlr-2.7.7" ) . ChildMethods ( ) let depth0 = m . DepthOfIsUsing ( "antlr-2.7.7" ) where depth0 > 1 orderby depth0 select new { m , depth0 } |
許多方法直接使用antlr,這使得hibernate核心與其高度耦合,而將antlr換成另一個並不是一件容易的事。 這個事實並不意味著我們在休眠設計中存在問題,但是我們在使用第三方庫時必須小心,並仔細檢查第三方庫是否必須與應用程序低耦合。
凝聚
單一責任原則指出,一個類應該有一個並且只有一個改變的理由。 據說這樣的類是有凝聚力的。 高LCOM
值通常表明內聚性較差的類。 有幾個 LCOM 指標。 LCOM 的取值範圍為 [0-1]。 LCOMHS(HS 代表 Henderson-Sellers)取值在 [0-2] 範圍內。 請注意,LCOMHS 度量通常被認為更有效地檢測非內聚類型。
LCOMHS 值高於 1 應被視為警報。
一般來說,更關心內聚的類是具有許多方法和字段的類。
讓我們搜索具有許多方法和字段的類型。
1 2 3 4 |
from t in Types where ( t . Methods . Count ( ) > 40 | | t . Fields . Count ( ) > 40 ) && t . IsClass orderby t . Methods . Count ( ) descending select new { t , t . InstanceMethods , t . Fields , t . LCOMHS } |
此查詢只關注少數類型,並且所有這些類型的 LCOMHS 都小於 1。
使用註解
基於註解的開發讓 Java 開發者從繁瑣的配置中解脫出來。 並為我們提供了一個強大的功能,可以將源代碼從樣板代碼中解放出來。 生成的代碼也不太可能包含錯誤。
讓我們搜索 hibernate core 定義的所有註解。
1 |
from t in Types where t . IsAnnotationClass && ! t . IsThirdParty select t |
定義了很多註解,讓開發者輕鬆使用hibernate,避免了配置文件的煩惱。
結論
Hibernate Core 是值得學習的開源項目的一個很好的例子,不要猶豫,看看裡面。