雖然當前主流已轉向 SSM(SpringMVC + Spring + MyBatis),但 SSH 作為經典三層架構代表,理解其整合原理對掌握 IoC、AOP、ORM 協作機制仍有重要價值。本文基於 Struts 2.3.24 + Hibernate 3.6.10 + Spring 4.3.2 + MySQL 5.5 記錄完整搭建步驟與核心配置。
採用標準三層分包:entity、dao、service、action。核心 JAR 分類管理(MyEclipse User Library):
mysql-connector-java-5.0.8-bin.jar

-- 用戶表
CREATE TABLE t_user (
id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
password VARCHAR(50),
deptNo INT,
FOREIGN KEY (deptNo) REFERENCES _dept(deptNo)
);
-- 部門表(下劃線前綴避免關鍵字衝突)
CREATE TABLE _dept (
deptNo INT PRIMARY KEY AUTO_INCREMENT,
deptName VARCHAR(50)
);
// User.java
public class User {
private int id;
private String username, password;
private Dept dept; // 多對一關聯
// Getter/Setter 省略
}
// User.hbm.xml
<class name="com.mz.entity.User" table="t_user">
<id name="id" column="id"><generator class="native"/></id>
<property name="username" column="username"/>
<property name="password" column="password"/>
<many-to-one name="dept" column="deptNo" class="com.mz.entity.Dept"/>
</class>
// Dept.java
public class Dept {
private int deptNo;
private String deptName;
// Getter/Setter 省略
}
// Dept.hbm.xml
<class name="com.mz.entity.Dept" table="_dept">
<id name="deptNo" column="deptNo"><generator class="native"/></id>
<property name="deptName" column="deptName"/>
</class>
public class UserDao {
private SessionFactory sessionFactory;
public boolean save(User user) {
sessionFactory.getCurrentSession().save(user);
return true;
}
public User getUserById(int id) {
return (User) sessionFactory.getCurrentSession().get(User.class, id);
}
public List<User> getUserByDept(int deptNo) {
String hql = "from User u where u.dept.deptNo = ?";
return sessionFactory.getCurrentSession()
.createQuery(hql).setInteger(0, deptNo).list();
}
// Getter/Setter for sessionFactory
}
注意:DAO 中無事務代碼,事務由 Spring 聲明式管理。
public class UserService {
private UserDao userDao;
public User getUser(int id) { return userDao.getUserById(id); }
// Getter/Setter
}
public class UserAction extends ActionSupport implements RequestAware {
private Map<String, Object> request;
private UserService userService;
public String execute() {
User user = userService.getUser(2);
request.put("user", user);
return SUCCESS;
}
// Getter/Setter
}
<package name="user" namespace="/" extends="struts-default">
<action name="user" class="userAction"> <!-- class 對應 Spring Bean ID -->
<result name="success">/index.jsp</result>
</action>
</package>
<beans ... xmlns:aop="..." xmlns:tx="...">
<!-- 1. C3P0 數據源 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="user" value="root"/>
<property name="password" value="111111"/>
<property name="initialPoolSize" value="3"/>
<property name="maxPoolSize" value="10"/>
</bean>
<!-- 2. SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
<props>
<prop key="dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="show_sql">true</prop>
<prop key="hbm2ddl.auto">update</prop>
</props>
</property>
<property name="mappingLocations">
<list><value>classpath:com/mz/entity/*.hbm.xml</value></list>
</property>
</bean>
<!-- 3. 事務管理器 -->
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<!-- 4. 事務通知 (AOP) -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*save*" propagation="REQUIRED"/>
<tx:method name="*get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.mz.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
<!-- 5. Bean 裝配 -->
<bean id="userDao" class="com.mz.dao.UserDao">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
<bean id="userService" class="com.mz.service.UserService">
<property name="userDao" ref="userDao"/>
</bean>
<bean id="userAction" class="com.mz.action.UserAction">
<property name="userService" ref="userService"/>
</bean>
</beans>
<!-- Spring 容器啟動 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:bean.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Struts2 過濾器 -->
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 解決 Hibernate 懶加載異常:OpenSessionInViewFilter -->
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>*.action</url-pattern>
</filter-mapping>
https://localhost:8080/ssh/user${user.username} 成功${user.dept.deptName},若拋出 LazyInitializationException,確認 OpenSessionInViewFilter 已正確配置並映射至 *.action

| 環節 | 核心要點 |
|---|---|
| JAR 衝突 | Struts2 與 Hibernate 共用 antlr,需排除一份 |
| 事務管理 | Spring AOP 織入 Service 層,DAO 無事務代碼 |
| Struts-Spring 整合 | Action 類在 Spring 中定義 Bean,struts.xml 中 class 指向 Bean ID |
| 懶加載 | 配置 OpenSessionInViewFilter 延長 Session 生命周期至視圖渲染結束 |
| 映射文件 | *.hbm.xml 放在 classpath 下,SessionFactory 掃描 mappingLocations |