聲明式事務是指在不修改源代碼的情況下通過配置applicationContext.xml自動實現事務控制,其本質是AOP環繞通知。它的觸發時機為:1、當目標方法執行成功時自動提交事務,2、當目標方法拋出運行時異常
時,自動事務回滾
核心步驟示例(基于 XML 配置)
1. 添加依賴
因為是基于AOP,所以必須引入aop和aspectjweaver:
<!-- Spring 上下文支持 --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.30</version></dependency><!-- Spring JDBC 核心依賴 --><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.30</version></dependency><!-- 數據庫驅動(以 MySQL 為例) --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency><!-- JUnit 4 --><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13.2</version><scope>test</scope></dependency><!-- Spring Test --><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.3.30</version><scope>test</scope></dependency><!-- Spring AOP --><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.3.30</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.7</version></dependency><!--logback日志組件,spring框架默認集成--><dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.3.12</version></dependency>
2.applicationContext.xml配置
主要步驟如下:
1、配置TransactionManager事務管理器
2、配置事務通知與事務屬性
3、為事務通知綁定ponitCut切點
例如:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:contex="http://www.springframework.org/schema/context"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd
"><!-- 加載屬性文件 --><contex:property-placeholder location="application.properties"/><!-- 配置數據源(使用DriverManagerDataSource,適用于簡單場景) --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="${jdbc.driverClassName}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><!--jdbcTemplate配置--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource"/></bean> <!--1、配置事務管理器,用于事務創建、提交、回滾--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource"/></bean><!--2、事務通知配置,決定哪些方法使用事務,哪些方法不使用事務--><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><!--當目標方法符合正則表達式“batch*”時,啟用聲明式事務--><tx:method name="batch*" propagation="REQUIRED"/><!--當目標方法符合正則表達式“find*”時,不需要啟用聲明式事務--><tx:method name="find*" propagation="NOT_SUPPORTED" read-only="true" /><!--當目標方法符合正則表達式“get*”時,不需要啟用聲明式事務--><tx:method name="get*" propagation="NOT_SUPPORTED" read-only="true" /><!--其他目標方法,根據項目需要設置啟用聲明式事務或不啟用事務--><tx:method name="*" propagation="NOT_SUPPORTED" read-only="true" /></tx:attributes></tx:advice><!--3、定義聲明式事務的作用范圍--><aop:config><aop:pointcut id="transactionPointcut" expression="execution(* com.hirain.service.*Service.*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointcut"/></aop:config><bean id="employeeDao" class="com.hirain.dao.EmployeeDao"><property name="jdbcTemplate" ref="jdbcTemplate"/></bean><bean id="employeeService" class="com.hirain.service.EmployeeService"><property name="employeeDao" ref="employeeDao"/><property name="transactionManager" ref="transactionManager"/></bean>
</beans>
3. Service層代碼
package com.hirain.service;import com.hirain.dao.EmployeeDao;
import com.hirain.entity.Employee;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;public class EmployeeService {private EmployeeDao employeeDao;private DataSourceTransactionManager transactionManager;public void batchInsert() {for (int i = 0; i < 10; i++) {if (i==3){throw new RuntimeException("生成員工數據異常");}Employee employee = new Employee();employee.setEmployeeId(80+i);employee.setName("新員工"+i);employee.setDepartmentId(2l);employee.setTitle("客服");employee.setLevel(1);employeeDao.insert(employee);}}
//...getter and setter
}
4. DAO 層代碼
package com.hirain.dao;import com.hirain.entity.Employee;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;public class EmployeeDao {private JdbcTemplate jdbcTemplate;public Employee findById(long id){String sql = "select * from adm_employee where employee_id=?";Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{id}, new BeanPropertyRowMapper<Employee>(Employee.class));return employee;}public int insert(Employee employee){String sql="insert into adm_employee (employee_id,name,department_id,title,level) values(?,?,?,?,?)";int rows = jdbcTemplate.update(sql,new Object[]{employee.getEmployeeId(),employee.getName(),employee.getDepartmentId(),employee.getTitle(),employee.getLevel()});return rows;}public int update(Employee employee){String sql="UPDATE adm_employee SET name=?,department_id=?,title=?,level=? WHERE employee_id=?";int rows = jdbcTemplate.update(sql,new Object[]{employee.getName(),employee.getDepartmentId(),employee.getTitle(),employee.getLevel(),employee.getEmployeeId()});return rows;}public int delete(long id){String sql="delete from adm_employee where employee_id=?";int rows = jdbcTemplate.update(sql,new Object[]{id});return rows;}public EmployeeDao() {}//...getter and setter
}
5.測試代碼
import com.hirain.dao.EmployeeDao;
import com.hirain.entity.Employee;
import com.hirain.service.EmployeeService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;@RunWith(SpringJUnit4ClassRunner.class) // 使用 Spring 的測試運行器
@ContextConfiguration(locations = {"classpath:applicationContext.xml"}) // 加載 Spring 配置文件
public class JDBCTemplateTest { @Autowiredprivate EmployeeService employeeService; @Testpublic void testBantchInsert() {employeeService.batchInsert();}}
關鍵配置說明
** @Transactional
注解屬性**
- 傳播行為:
propagation
(默認REQUIRED
)。 - 隔離級別:
isolation
(默認數據庫級別)。 - 超時時間:
timeout
(秒)。 - 只讀事務:
readOnly
(優化查詢)。 - 回滾規則:
rollbackFor
(指定觸發回滾的異常類型)。
聲明式事務 vs 編程式事務
特性 | 聲明式事務(@Transactional) | 編程式事務(TransactionTemplate) |
---|---|---|
代碼侵入性 | 低(注解聲明) | 高(手動控制事務邊界) |
靈活性 | 低(固定事務屬性) | 高(動態調整事務屬性) |
適用場景 | 大多數簡單到中等復雜度場景 | 需要動態事務控制的復雜場景 |
注意事項
- 異常類型:默認僅
RuntimeException
和Error
觸發回滾,需通過rollbackFor
指定其他異常。 - 自調用失效:在同一個類中通過
this.xxxMethod()
調用事務方法,事務可能失效(需通過代理對象調用)。 - 數據庫支持:事務需要數據庫引擎支持(如 MySQL 的 InnoDB)。
通過聲明式事務,開發者可以專注于業務邏輯,無需手動管理事務邊界,代碼更簡潔且易于維護。