在開始整合前,需規劃好專案分層結構與依賴管理。建議採用 Maven 管理依賴,核心包含:
專案包結構規劃:
cn.itcast.scm ├── entity # 實體類 ├── dao # DAO 接口 ├── dao.impl # DAO 實現 ├── service # Service 接口 ├── service.impl # Service 實現 ├── action # Controller (Struts2 風格) / controller (SpringMVC 風格) ├── test # 測試類 └── utils # 工具類
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 類型別名,簡化映射文件中的類全路徑 -->
<typeAliases>
<typeAlias type="cn.itcast.scm.entity.Dept" alias="Dept" />
</typeAliases>
<!-- 映射文件註冊,也可在 Spring 中統一配置 -->
<mappers>
<mapper resource="cn/itcast/scm/entity/DeptMapper.xml" />
</mappers>
</configuration>
關鍵點: 整合 Spring 後,資料源、事務管理移交 Spring 容器,此文件僅保留類型別名與映射文件引用。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="https://www.springframework.org/schema/tx"
xmlns:aop="https://www.springframework.org/schema/aop"
xmlns:context="https://www.springframework.org/schema/context"
xsi:schemaLocation="...省略...">
<!-- 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/test?useUnicode=true&characterEncoding=UTF-8" />
<property name="user" value="root" />
<property name="password" value="root" />
</bean>
<!-- 2. SqlSessionFactoryBean:整合 MyBatis 核心 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:myBatis-config.xml" />
<!-- 亦可直接在此配置 typeAliasesPackage、mapperLocations -->
</bean>
<!-- 3. 事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 4. 事務通知 (AOP) -->
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor advice-ref="advice" pointcut="execution(* cn.itcast.scm.service.impl.*.*(..))"/>
</aop:config>
<!-- 5. SqlSessionTemplate:線程安全的 MyBatis 操作模板 -->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
<!-- 6. 元件掃描 -->
<context:component-scan base-package="cn.itcast.scm"/>
</beans>
核心解析:
Dept.java - 標準 POJO,含序列化、getter/setter、toString。
DeptMapper.xml - 定義 SQL 映射:
<mapper namespace="cn.itcast.scm.entity.DeptMapper">
<resultMap type="Dept" id="deptResultMap">
<id property="deptId" column="dept_id" />
<result property="deptName" column="dept_name" />
<result property="deptAddress" column="dept_address" />
</resultMap>
<select id="selectDept" parameterType="Integer" resultMap="deptResultMap">
select dept_id, dept_name, dept_address from dept where dept_id = #{deptID}
</select>
<insert id="insertDept" parameterType="Dept">
insert into dept(dept_name, dept_address) values(#{deptName}, #{deptAddress})
</insert>
</mapper>
@Repository("deptDao")
public class DeptDaoImpl {
@Resource
private SqlSessionTemplate sqlSessionTemplate;
public Dept selectDept(Integer deptId) {
return sqlSessionTemplate.selectOne("cn.itcast.scm.entity.DeptMapper.selectDept", deptId);
}
public int insertDept(Dept dept) {
return sqlSessionTemplate.insert("cn.itcast.scm.entity.DeptMapper.insertDept", dept);
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestDeptDao {
@Resource
private DeptDaoImpl deptDao;
@Test
public void testSelectDept() {
System.out.println(deptDao.selectDept(1));
}
@Test
public void testInsertDept() {
Dept dept = new Dept();
dept.setDeptName("測試部門");
dept.setDeptAddress("北京");
System.out.println("影響行數:" + deptDao.insertDept(dept));
}
}
改進建議: 使用 Spring Test 支援的 `@RunWith`、`@ContextConfiguration` 替代手動獲取 `ApplicationContext`,測試碼更簡潔。
<beans ... xmlns:mvc="https://www.springframework.org/schema/mvc" ...>
<mvc:annotation-driven/>
<context:component-scan base-package="cn.itcast.scm.action"/> <!-- 僅掃描 Controller -->
<!-- 視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
<web-app ...>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>mvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc</servlet-name>
<url-pattern>*.action</url-pattern> <!-- 或 / -->
</servlet-mapping>
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param>
</filter>
<filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>
</web-app>
DeptAction.java - 處理請求、調用 Service、跳轉視圖。
index.jsp - 測試表單提交至 `dept/insert.action`。
main.jsp - 成功頁面。
| 問題 | 原因 | 解決方案 |
|---|---|---|
| 事務不生效 | AOP 切點配置錯誤、未掃描到 Service、方法未拋出異常 | 切點指向 service.impl、確保 `@Service` 被掃描、rollback-for="Exception" |
| 映射文件找不到 | 路徑錯誤、未在 myBatis-config 或 SqlSessionFactoryBean 中註冊 | 使用 `mapperLocations="classpath*:cn/itcast/scm/dao/**/*Mapper.xml"` 統一掃描 |
| 中文亂碼 | 資料庫連結、Tomcat、Filter 未統一 UTF-8 | JDBC URL 加 `characterEncoding=UTF-8`、配置 CharacterEncodingFilter、DB 表建立指定 charset |
| 循環依賴 | Service 注入 DAO、DAO 注入 SqlSessionTemplate、互相引用 | 分層清晰、必要時用 `@Lazy` 或 setter 注入 |
至此,SSM 基礎架構搭建完成。下一階段可引入 MyBatis 逆向工程生成代碼、整合分頁插件、優化事務邊界、引入 Redis 快取等進階主題。