注解方式实现声明式事务管理
使用注解实现Spring的声明式事务管理,更加简单!
步骤:
1) 必须引入Aop相关的jar文件
2) bean.xml中指定注解方式实现声明式事务管理以及应用的事务管理器类
3)在需要添加事务控制的地方,写上: @Transactional
@Transactional注解:
1)应用事务的注解
2)定义到方法上: 当前方法应用spring的声明式事务
3)定义到类上: 当前类的所有的方法都应用Spring声明式事务管理;
4)定义到父类上: 当执行父类的方法时候应用事务。
修改bean.xml如下
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 1、数据源对象:C3P0连接池 --> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"></property> <property name="jdbcUrl" value="jdbc:sqlserver://localhost:1433;DataBaseName=Test"></property> <property name="user" value="sa"></property> <property name="password" value="123456"></property> <property name="initialPoolSize" value="3"></property> <property name="maxPoolSize" value="11"></property> <property name="maxStatements" value="50"></property> <property name="acquireIncrement" value="2"></property> </bean> <!-- 2、创建JdbcTemplate对象 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <constructor-arg ref="dataSource"></constructor-arg> <!-- 或者<property name="dataSource" ref="dataSource"></property> --> </bean> <!-- 3、开启注解扫描 --> <context:component-scan base-package="cn.gqx.b_anno"></context:component-scan> <!-- 4、注解方式实现事物 --> <!-- 事物管理器类 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <tx:annotation-driven transaction-manager="txManager"/> </beans>
DeptService如下
@Service public class DeptService { //接受容器注入的dao @Resource private DeptDao deptDao; //事物控制注解 @Transactional( readOnly=false, //读写事物 timeout=-1, //事物的超时时间不限制 //noRollbackFor=ArithmeticException.class //遇到数学异常不回滚 rollbackFor=ArithmeticException.class, //只回滚数学异常,指定更加精确 isolation=Isolation.DEFAULT, //事物的隔离级别,数据库的默认 /* * DEFAULT 使用后端数据库默认的隔离级别(spring中的的选择项) * READ_UNCOMMITED 允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读 * READ_COMMITTED 允许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生 * REPEATABLE_READ 对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生。 * SERIALIZABLE 完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。 * 这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的。 */ propagation=Propagation.REQUIRES_NEW /*Propagation.REQUIRED * 指当前方法必须在事物的环境下执行 * 如果当前运行的方法,已经存在事物,就会加入到当前事物 * Propagation.REQUIRES_NEW类似 * 指当前方法必须在事物的环境下执行; * 如果当前运行的方法,已经存在事物,事物会挂起,始终开始一个新的事物 * 执行完后,刚才挂起的事物才继续运行 * * 举例保存部门前要插入日志 * 不管是否将部门保存成功,都需要写入日志,这个时候要用REQUIRES_NEW */ ) public void save(Dept dept){ //第一次调用 deptDao.save(dept); // 模拟异常,此时整个service.save()执行成功的时候要回滚 int i=1/0; //第二次调用 deptDao.save(dept); } }
对propagation=Propagation.REQUIRES_NEW/Propagation.REQUIRED的测试
不管是否将部门保存成功,都需要写入日志,这个时候要用REQUIRES_NEW
Propagation.REQUIRED
指定当前的方法必须在事务的环境下执行;
如果当前运行的方法,已经存在事务, 就会加入当前的事务;
Propagation.REQUIRED_NEW
指定当前的方法必须在事务的环境下执行;
如果当前运行的方法,已经存在事务: 事务会挂起; 会始终开启一个新的事务,执行完后; 刚才挂起的事务才继续运行。
创建logDao
//测试,日志传播行为 @Repository public class LogDao { @Resource private JdbcTemplate jdbcTemplate; @Transactional(propagation=Propagation.REQUIRES_NEW) public void save() { jdbcTemplate.update("insert into t_log values('保存数据了')"); } }
这个时候的DeptService如下
@Service public class DeptService { //接受容器注入的dao @Resource private DeptDao deptDao; @Resource private LogDao logDao; //事物控制注解 @Transactional( readOnly=false, //读写事物 timeout=-1, //事物的超时时间不限制 //noRollbackFor=ArithmeticException.class //遇到数学异常不回滚 rollbackFor=ArithmeticException.class, //只回滚数学异常,指定更加精确 isolation=Isolation.DEFAULT, //事物的隔离级别,数据库的默认 propagation=Propagation.REQUIRED ) public void save(Dept dept){ //保存日志 logDao.save(); // 模拟异常,此时整个service.save()执行成功的时候要回滚 int i=1/0; //调用 deptDao.save(dept); } }
这个时候即出现数学异常日志的保存行为会正常的插入,而部门的保存会正常进行。