一 介紹
本文并不想介紹Struts,Spring,Hibernate的原理系統架構等,本文地目的是通過一個較復雜地實例介紹如何整合Struts,Spring,Hibernate,網上現有的例子雖然也能達到目的,但功能都比較單一,復雜的例子時會有意想不到的麻煩。本文對讀者假設已經具備了以上框架的基礎知識。以及那些已經了解Struts,Spring,Hibernate的基本概念,但是還沒有親身在較復雜的項目中體驗Struts+Spring+Hibernate的開發人員。
1 Struts
雖然不打算過多介紹Struts的原理,但是大概介紹一下還是有必要的。Struts本身就是 MVC 在這里負責將用戶數據傳人業務層,以及 將業務層處理的結果返回給用戶,此系統屬于較簡單WEB應用,采用了OpenSessionInView模式處理LazyLoad問題,這樣我們可以在用戶視圖中使用 get,set方法來方便地獲取關聯對象。為了處理龐大的Action和ActionForm問題,在此我門準備使用DynaActionForm (DynaValidatorForm)和DispatchAction以及 動態驗證框架 來解決。及使用Tile來解決框架問題 。使用自定義標簽處理分頁和身份驗證問題。
2 Spring
Spring Framework最得以出名的是與Hibernate的無縫鏈接,雖然Spring 對Hibernate提供了90%以上的封裝,使我們不必去關心Session 的建立,關閉,以及事務使我們能夠專心的關注業務邏輯。但是一些特殊情況如 有時需要Query以及Criteria 對象,分頁等,Spring不能給我們提供支持,總不能每次都在你的DAO上寫個HibernateCallBackup()吧?Spring的作用不是把Hibernate再封裝一層,而是讓你接觸不到Hibernate的API,而是幫助你管理好Session和Transaction。
在這里解決方法是:首先 寫一個IBase 的接口,和一個BaseDao的實現。在實現中仿照HibernateTemplate,將其功能一一實現,同時考慮到Spring 未能支持的地方,我們不得已只好自己來管理Session,因此加入public Session openSession(),public Query getQuery(String sql),public Criteria getCriteria(Class clazz),以及分頁的方法。 然后為每一個Entity 都建立繼承于以上類的IEntity,與EntityDao。這里可以根據需求對Entity加入特殊的方法實現,如 在 StudentsDao.java 中加入類似用戶身份驗證等。以上就是數據訪問層。接下來在Service層中通過對dao的引用完成業務邏輯方法。在下面的例子中我們分別為學生模塊,教師模塊,管理員模塊構建Service層,StudentsServiceImpl,TeachersServiceImpl,AdminServiceImpl。
3 Hibernate
有了Spring的封裝,我們要對Hibernate做的就是正確實現對象關系的映射。由于此處處于系統的最底層,準確無誤的實現對象之間的關聯關系映射將起著至關重要的作用。
總之,理解了Struts,Spring,Hibernate地原理以及之間的關系之后,剩下的工作就如同在以Spring為核心的Struts為表現的框架中堆積木。
下圖可以更好的幫助我們理解Struts,Spring,Hibernate之間的關系。
pic1.JPG
二 案例簡述:
設計思路主要源于 大學選修課,該系統可以方便處理學生在課程選報,學分查詢,成績查詢,以及 成績發布等。
系統以班級為核心,一門課程可以對應多個班級,一名教師也可以帶不同的班級,學生可以選報不同課程所對應的班級,班級自身有目前人數,和最大人數,以及上課時間,上課地點的屬性。
學生在選報班級之后,班級的人數會自動加一,直到等于最大人數時,其他學生將會有人數已滿的錯誤提示。同理如果學生選擇了同一課程的不同班級,也將收到錯誤提示。學生有密碼,系別,學分,地址,電話等屬性。
教師在系統中主要負責成績發布,教師可以對其所帶的班級的學生的成績修改,系統會以成績是否大于等于60來判斷學生是否通過考試,如果通過會將該課程的學分累加到學生學分,同樣如果教師二次修改了成績,而且小于60,系統會在學生學分上扣掉該課程的分數。
課程在系統中具體體現為班級,自身帶有學分屬性。
系有編號,名稱的屬性,同時可以作為聯系教師,課程,學生的橋梁。
功能模塊
l 身份驗證模塊: 根據用戶名,密碼,用戶類別 轉發用戶到不同的模塊。
l 學生模塊: 查看課程,查看班級,選報課程,查看己選課程,成績查詢。
l 教師模塊: 錄入成績
l 管理員模塊:對學生,教師,課程,班級,系 增,刪,查,改。
三 具體實踐
代碼下載
http://m.tkk7.com/Files/limq/StudentManger.rar
1 對象關系映射:
首先,將庫表映射為數據模型(SQL在源碼中查看),轉換后的數據模型如下圖:
pic2.jpg
由此我們可以看出一下關聯關系:
1 Students 和 Contact(聯系方式)一對一關系。
2 Students 和 History(選課歷史) 一對多關系
3 Students 和 Classes 多對多關系。
4 Classes 和 Classes_info 一對多關系。
5 Classes 和 Teachers 多對一關系。
6 Classes 和 Courses 多對一關系。
7 Course 和 Department(系) 多對一關系。
8 Teachers 和 Department 多對一關系。
9 Students 和 Department 多對一關系。
在Hibernate中將以上關系一一映射,如Students 和 History 一對多關系
Students.cfg.xm.:
1
6
7
9 set>
10
同樣在History.cfg.xml中加入:
1
4 many-to-one>
5
用過MyEclipse開發Hibernate的就知道,MyEclipse會幫助我們生成持久對象和抽象對象,我們要在 Students.java 中加入對History的引用
private Set history=new HashSet();
public Set getHistory() {
return history;
}
public void setHistory(Set history) {
this.history = history;
}
同時,在AbstractHistory.java 中刪除student_id 以及對應的get,set 方法,History.java 中加入
private Students student;
public Students getStudent() {
return student;
}
public void setStudent(Students student) {
this.student = student;
}
具體內容請查看 源代碼。
2 DAO 數據訪問層
首先,編寫IBaseDao與BaseDao,其中IBaseDao代碼如下:
1 package limq.hibernate.dao;
2
3 import java.util.Collection;
4 import java.util.List;
5 import net.sf.hibernate.Criteria;
6 import net.sf.hibernate.Query;
7 import net.sf.hibernate.Session;
8 import limq.exception.DaoException;
9
10 public interface IBaseDao {
11
12 public Session openSession();
13
14 public int getTotalCount( String hql) throws Exception;
15
16 public Query getQuery(String sql) throws Exception;
17
18 public Criteria getCriteria(Class clazz) throws Exception;
19
20 public int getTotalPage(int totalCount,int pageSize);
21
22 public void create(Object entity);
23
24 public void update(Object entity);
25
26 public void delete(Object entity) throws DaoException;
27
28 public void deleteAll(Class clazz) throws DaoException;
29
30 public void deleteAll(Collection entities) throws DaoException;
31
32 public Object loadByKey(Class clazz, String keyName, Object keyValue);
33
34 public List find(String queryString) throws DaoException;
35
36 public List find(String queryString, Object param) throws DaoException;
37
38 public List find(String queryString, Object[] params) throws DaoException;
39
40 }
41
BaseDao繼承org.springframework.orm.hibernate.support.HibernateDaoSupport
實現以上的 定義的方法
如:
1 public void create(Object entity) {
2 try {
3 getHibernateTemplate().save(entity);
4
5 } catch (Exception e) {
6 log.error("保存 " + entity.getClass().getName() + " 實例到數據庫失敗", e);
7
8 }
9 }
10 /**
11 * 獲得session
12 */
13 public Session openSession() {
14 return SessionFactoryUtils.getSession(getSessionFactory(), false);
15 }
16
17 /**
18 * 獲得Query對象
19 */
20 public Query getQuery(String sql) throws Exception{
21 Session session = this.openSession();
22 Query query = session.createQuery(sql);
23 return query;
24 }
25 /**
26 * 獲得Criteria對象
27 */
28 public Criteria getCriteria(Class clazz) throws Exception{
29
30 Session session=this.openSession();
31 Criteria criteria = session.createCriteria(clazz);
32 return criteria;
33 }
34
可以看到,這里即充分利用了Spring對Hibernate的支持,還彌補了Spring的不足。最后分別為每個持久對象建立Interface,以及DAO,使其分別繼承IBaseDao與BaseDao。
如IDepartment,DepartmentDao
1 public interface IDepartment extends IBaseDao {}
2
3 public class DepartmentDao extends BaseDao implements IBaseDao {}
4
3 Service 層
在這里需要認真思考每個業務邏輯所能用到的持久層對象和DAO,還要完成配置Spring框架, 首先我一起看看applications-service.xml
1 xml version="1.0" encoding="UTF-8"?>
2 DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
3 "http://www.springframework.org/dtd/spring-beans.dtd">
4
5
6
7 com.mysql.jdbc.Drivervalue>
8 property>
9
10 jdbc:mysql://localhost:3306/Studentvalue>
11 property>
12
13 rootvalue>
14 property>
15
16 value>
17 property>
18 bean>
19
20
21
22 property>
23
24
25 limq/hibernate/vo/Admins.hbm.xmlvalue>
26 limq/hibernate/vo/Classes.hbm.xmlvalue>
27 limq/hibernate/vo/Courses.hbm.xmlvalue>
28 limq/hibernate/vo/Students.hbm.xmlvalue>
29 limq/hibernate/vo/ClassesInfo.hbm.xmlvalue>
30 limq/hibernate/vo/Contact.hbm.xmlvalue>
31 limq/hibernate/vo/Department.hbm.xmlvalue>
32 limq/hibernate/vo/History.hbm.xmlvalue>
33 limq/hibernate/vo/Teachers.hbm.xmlvalue>
34 list>
35 property>
36
37
38 net.sf.hibernate.dialect.MySQLDialectprop>
39 trueprop>
40 props>
41 property>
42 bean>
43
44
45
46 property>
47 bean>
48
49
50
51
52 property>
53 bean>
54
55
56
57 property>
58 bean>
59
60
61
62 property>
63 bean>
64
65
66
67 property>
68 bean>
69
70
71
72 property>
73 bean>
74
75
76
77 property>
78 bean>
79
80
81
82 property>
83 bean>
84
85
86 limq.hibernate.dao.IStudentsvalue>
87 property>
88
89
90 hibernateInterceptorvalue>
91 studentDaoTargetvalue>
92 list>
93 property>
94 bean>
95
96
97 limq.hibernate.dao.ITeachersvalue>
98 property>
99
100
101 hibernateInterceptorvalue>
102 teacherDaoTargetvalue>
103 list>
104 property>
105 bean>
106
107
108 limq.hibernate.dao.ICoursesvalue>
109 property>
110
111
112 hibernateInterceptorvalue>
113 courseDaoTargetvalue>
114 list>
115 property>
116 bean>
117
118
119 limq.hibernate.dao.IClassesvalue>
120 property>
121
122
123 hibernateInterceptorvalue>
124 classDaoTargetvalue>
125 list>
126 property>
127 bean>
128
129
130 limq.hibernate.dao.IDepartmentvalue>
131 property>
132
133
134 hibernateInterceptorvalue>
135 departmentDaoTargetvalue>
136 list>
137 property>
138 bean>
139
140
141 limq.hibernate.dao.IAdminvalue>
142 property>
143
144
145 hibernateInterceptorvalue>
146 adminDaoTargetvalue>
147 list>
148 property>
149 bean>
150
151
152
153
154 property>
155
156
157 property>
158
159
160 property>
161
162
163 property>
164 bean>
165