使用注解实现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);
}
}
这个时候即出现数学异常日志的保存行为会正常的插入,而部门的保存会正常进行。