第三部分:建立框架代碼
工程結構

大家可以看到,本例一共分為:dao、entity、service、web四層。另外在這些層次下,還以業務功能再進行分包,這樣做是為了方便在以后的功能擴展中,能更好的管理和維護代碼。如果將所有類都直接集中在這4個包下面,隨著類的增加,會越來越難以維護,而且查找起來也很費勁。
HibernateDao
在本例中,我是通過繼承Spring提供的HibernateDaoSupport來實現持久層的基類。同時引入泛型參數,封裝了一些基本操作方法。

這是HibernateDao的部分代碼,引入的這個泛型參數,其實就是實體類(User、Role)。通過傳遞這個實體類,在構造方法中利用反射特性將它從JVM中取出來。

這里的getClass()方法是獲得繼承HibernateDao的類(UserDao、RoleDao)
getGenericSuperclass()方法就是通過這些繼承了HibernateDao的類,來得到父類(父類就是HibernateDao)的泛型。注意這個方法的返回值為Type,這是一個類型接口。請參考API。
因為在繼承HibernateDao 的時候,會給它加一個泛型參數。比如,User、Role實體類。因此超類是參數化類型,所以返回的 Type 對象包含所使用的實際類型參數。這里返回的Type對象是ParameterizedType接口的實現類ParameterizedTypeImpl,所以要將返回類型轉型為ParameterizedType。
getActualTypeArguments()方法是ParameterizedType接口中的,它的作用就是獲得實際類型參數 Type 對象的數組,因為我們這里只定義了一個泛型參數,數組里面也只有一個值,所以需要在數組下標處填0。然后最后一步轉型千萬別忘記了,因為這個方法返回的可是一個Type數組喔。
如果對于這部分的說明還有點不理解的話,請到時候有了代碼,設個斷點跟蹤一下,就會全部清楚了。關于java反射,它已經超出本文的范圍。大象只對本例中用到的部分進行講解。
使用這種寫法,是方便我們進行類型轉換與類型檢查。另外還可以簡化某些方法的寫法。比如:createCriteria(Criterion... criterions)這個方法。參數是Criterion類型的可變參數,它是用來設置查詢條件。如果要進行對象化查詢,那么最簡單的寫法就可以直接寫成createCriteria()。另外還有重載的方法,可以根據傳入class類型來創建自定義查詢。
dao
持久層的Dao類是根據實體類定義,一般是一個實體類就會有一個對應的Dao類。當然這要跟業務需求來設計,不是絕對的。另外你也可以為了簡便而去掉dao層,將持久化操作與業務邏輯全部寫在service層。

這些定義的方法是供service層調用,在業務層,將不會看到一行與持久層有關的代碼,降低藕合性是這樣做的目的。@Repository注解的作用就是標注這個UserDao是一個持久層組件。還記得前一章講到的掃描器嗎?component-scan 它就是用來將標有@Repository,@Service這樣的注解類掃描到Spring的容器里,并且同時對標有@Autowired注解的Bean啟用了自動注入功能。這就是Spring從2.5開始提供的新特性。我們使用注解的方法,就可以告別那繁瑣的配置文件定義。
entity
關于實體的定義就是使用JPA注解,關于這部分,我以前寫過一篇文章專門講這個,如果有不清楚的朋友可以先參考一下。學習JPA——Hibernate Annotation使用實例
本例中,我有兩點要講下。
第一、管理主鍵的表generator_table去掉原來單獨定義的那個ID主鍵,把g_key設為主鍵,整個表將只有兩個字段,g_key和g_value。
第二、在User實體中,我將角色ID(role_id)與角色實體(Role)做了一個多對一關聯。這一點是原來文章中沒有講過的。

請一定注意role_id是user表的字段。我在本例中設定的是一個角色可以對應多個人員,所以這個role_id存的就是role表id的值。fetch = FetchType.LAZY指定采用延遲檢索,如果當你取得了User對象,但又不想取Role中的信息,這時,User對象中的role屬性是代理狀態。Role對象中的值都是空的。只有當你使用role.id或role.name進行取值的時候,hibernate才會去數據庫中查找對應的記錄,因此在一定程度上降低了資源消耗。不過這里有點要注意,采用延遲檢索的時候,需要加上前一篇講到的OpenSessionInViewFilter過濾器。否則會遇到session關閉的異常。
service

@Service表示這是業務層組件。在業務層需要對UserDao加上@Autowired注解,大象在這里將業務層的方法名與持久層的方法名定義為一樣的,是我的一種習慣,大家可以按自己的想法來做。
測試
既然有了這么多代碼,那就來測試一下吧,看看有沒有問題。

好吧,為了照顧那些堅定的JUnit擁護者,再寫一個JUnit測試。本例使用junit-4.4.jar


@BeforeClass注解的方法表示在類實例化之前,也就是在類的構造方法執行之前就會執行。而且使用這個注解的方法必須是static void
@Test標明這是測試方法,方法名不用像以前那樣必須按照規則進行命名,可以自由定義。
上圖顯示大象使用JUnit方式測試也通過了(如果不會通過我寫它干嘛?嘿嘿)。
假如我將張三改成張四,再來看看測試結果。

這個截圖可以很明顯的說明所有東西。
這一篇是給大家講怎么用代碼來實現除web層之外的全注解步驟。當然,我主要是講思路,其實思路比代碼重要得多。這一個系列的最后,我會放上所有源碼供大家下載。現在這樣慢慢分析,是想給大家講道理。我們應該努力提升自己的境界與層次,而不要只把眼光放在代碼上面。下一章將會著重介紹web層,以及struts2的注解插件struts2-convention。那么,我們下次繼續。
本文為菠蘿大象原創,如要轉載請注明出處。http://www.aygfsteel.com/bolo