Spring事务
如果你作为一个开发者,不开事务就增删改,那我估计一天都活不下来就改走人了,事务很重要,简单来说事务就是连续一组的sql操作,一组动作都完成才能提交,但凡是一个环节出了异常那就得回滚到最初的状态
而传播方式则主要是指多个事务同时存在时,Spring如何处理事务,它们是否需要在同一个事务中运行;一个有事务的方法被另外有事务的方法调用时,这个事务应该如何运行;例如
methodA() { // 有事务
update;
ServiceB.methodB(); // 又调用了另一个有事务的方法 异常;
}
methodB() { // 事务
update;
}
7种传播方式
开事务
Spring容器中配置用注解的方式开事务,xml写<bean>
我下面会说
<tx:annotation-driven transaction-manager="transactionManager" />
1 REQUIRED
REQUIRED:表示如果当前存在一个事务,则加入该事务,否则将新建一个事务;这个是Spring事务默认传播行为
测试
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT)
public int incr(int balance, int id) {
//传播方式request
int result = studentMapper.updateBalance(balance, id);
//新事务
studentService.decr(-100, 2);
//制造异常引起incr这个事务回滚
int i = 1/0;
return result ;
}
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
rollbackFor = Throwable.class)
public int decr(int balance, int id){
int result = studentMapper.updateBalance(balance, id);
return result ;
}
测试结果是两个方法对应的操作都不起作用,decr加入到了incr这个事务中,incr回滚导致decr也回滚
2 REQUIRES_NEW
表示不管是否存在事务,都创建一个新的事务,原来的事务挂起,新的事务执行完毕,继续执行老的事务;
测试
和刚才代码几乎一样,我只是把decr的传播方式改成了REQUIRES_NEW
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT)
public int incr(int balance, int id) {
//传播方式request
int result = studentMapper.updateBalance(balance, id);
//新事务
studentService.decr(-100, 2);
//制造异常
int i = 1/0;
return result ;
}
/** 转出 */
@Transactional(propagation = Propagation.REQUIRES_NEW,
isolation = Isolation.DEFAULT,
rollbackFor = Throwable.class)
public int decr(int balance, int id) {
int result = studentMapper.updateBalance(balance, id);
return result ;
}
而结果是decr这个事务不会被incr影响,最终导致"增加"这个事务回滚,"减少"这个事务会正常提交
3 SUPPORTS
表示如果当前存在事务,就加入该事务;如果当前没有事务,那就不使用事务;
测试
/** 转入 */
@Override
public int incr(int balance, int id) {//没有事务
//传播方式request
int result = studentMapper.updateBalance(balance, id);
//新事务
studentService.decr(-100, 2);
int i = 1/0;
return result ;
}
/** 转出 */
@Transactional(propagation = Propagation.SUPPORTS,
isolation = Isolation.DEFAULT,
rollbackFor = Throwable.class)
public int decr(int balance, int id) /*throws SQLException */{
int result = studentMapper.updateBalance(balance, id);
//throw new SQLException("SQL异常");
return result ;
}
结果是incr 和 decr都没有事务,即使报了异常,也都操作成功了,反之我们给incr设上事务
测试二
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT
)
public int incr(int balance, int id) {
//传播方式request
int result = studentMapper.updateBalance(balance, id);
//新事务
studentService.decr(-100, 2);
int i = 1/0;
return result ;
}
/** 转出 */
@Override
@Transactional(propagation = Propagation.SUPPORTS,
isolation = Isolation.DEFAULT,
rollbackFor = Throwable.class)
public int decr(int balance, int id) /*throws SQLException */{
int result = studentMapper.updateBalance(balance, id);
//throw new SQLException("SQL异常");
return result ;
}
结果是decr进入了incr这个事务,两者都回滚了,对数据库没有影响
4 NOT_SUPPORTED
表示不使用事务;如果当前存在事务,就把当前事务暂停,以非事务方式执行;
测试
/** 转入 */
public int incr(int balance, int id) {
//传播方式request
int result = studentMapper.updateBalance(balance, id);
//新事务
studentService.decr(-100, 2);
int i = 1/0;
return result ;
}
/** 转出 */
@Transactional(propagation = Propagation.NOT_SUPPORTED,
isolation = Isolation.DEFAULT,
rollbackFor = Throwable.class)
public int decr(int balance, int id) /*throws SQLException */{
int result = studentMapper.updateBalance(balance, id);
//throw new SQLException("SQL异常");
return result ;
}
测试结果是两者都没了事务,抛异常没有影响
5 MANDATORY
表示必须在一个已有的事务中执行,如果当前没有事务,则抛出异常;
测试
假设我不在一个事务中执行,而且我把1/0这个异常排了看看效果
/** 转入 */
public int incr(int balance, int id) {
//传播方式request
int result = studentMapper.updateBalance(balance, id);
//新事务 会出异常
studentService.decr(-100, 2);
return result ;
}
/** 转出 */
@Transactional(propagation = Propagation.MANDATORY,
isolation = Isolation.DEFAULT,
rollbackFor = Throwable.class)
public int decr(int balance, int id) /*throws SQLException */{
int result = studentMapper.updateBalance(balance, id);
//throw new SQLException("SQL异常");
return result ;
}
测试的结果是,因为incr事务被关了,所以不管下面的代码异常与否,incr会影响数据库,因为事务被decr关了,而它自己会抛出异常导致不执行本身的操作
6 NEVER:
表示以非事务方式执行,如果当前存在事务,则抛出异常;
测试
正好和MANDATORY相反,我们这次把它放在一个事务中看效果,而且它既然自己抛异常我们把那个 1/0给删了
/** 转入 */
@Override
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
public int incr(int balance, int id) {
//传播方式request
int result = studentMapper.updateBalance(balance, id);
//新事务
studentService.decr(-100, 2);
return result ;
}
/** 转出 */
@Override
@Transactional(propagation = Propagation.NEVER,
isolation = Isolation.DEFAULT,
rollbackFor = Throwable.class)
public int decr(int balance, int id) /*throws SQLException */{
int result = studentMapper.updateBalance(balance, id);
//throw new SQLException("SQL异常");
return result ;
}
测试结果是 两个方法都不会对数据库造成影响,因为decr由于抛异常导致incr回滚,而他自己也没有顺利执行所以不会影响数据库
7 NESTED:
这个是嵌套事务,如果当前存在事务,则在嵌套事务内执行;如果当前不存在事务,则创建一个新的事务;
嵌套事务使用数据库中的保存点来实现,即嵌套事务回滚不影响外部事务,但外部事务回滚将导致嵌套事务回滚;
-可以理解为:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与REQUIRED类似的操作。
配置文件开事务
写在SpringRoot容器中
<tx:advice id="tx-advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" />
<tx:method name="update*" />
<tx:method name="delete*" />
<tx:method name="transfer" isolation="DEFAULT"
propagation="REQUIRED" rollback-for="Throwable" />
<tx:method name="incr" propagation="REQUIRED" />
<tx:method name="decr" propagation="NESTED" />
<tx:method name="query*" read-only="true" />
</tx:attributes>
</tx:advice>
总结
两种开事务的方式,和七种传播方式你学会了吗