声明式事务是指在不修改源代码的情况下通过配置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)。
通过声明式事务,开发者可以专注于业务逻辑,无需手动管理事务边界,代码更简洁且易于维护。