在代码的世界里,通常我们离不开事务。
什么是事务呢?简单来说就是一件事情的开始到结束。
事务存在的意义就是回滚,即事情发生的意外就会返回到事情的开始,一切重来。
在java的世界里注解Transactional就是事务的发起者。
我们可以进去看Transactional的本质是一个接口,且它的默认事务传播Propagation是REQUIRED。
那么事务的传播有哪些呢?
由此我们可以看出事务的传播有七种。
分别是:
REQUIRED(0),
SUPPORTS(1),
MANDATORY(2),
REQUIRES_NEW(3),
NOT_SUPPORTED(4),
NEVER(5),
NESTED(6);
我们通过一些例子来解析一下:
主方法:
@Transactional(propagation = Propagation.REQUIRED)
@Override
public void testPropagationTrans() {
stuService.saveParent();
stuService.saveChildren();
int a = 1 / 0;
}
parent:
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public void saveParent() {
Stu stu = new Stu();
stu.setName("parent");
stu.setAge(19);
stuMapper.insert(stu);
}
children:
@Override
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void saveChildren() {
saveChild1();
int a = 1 / 0;
saveChild2();
}
public void saveChild1() {
Stu stu1 = new Stu();
stu1.setName("child-1");
stu1.setAge(11);
stuMapper.insert(stu1);
}
public void saveChild2() {
Stu stu2 = new Stu();
stu2.setName("child-2");
stu2.setAge(22);
stuMapper.insert(stu2);
}
1.REQUIRED :
我们给予主方法一个REQUIRED,parent没有事务,children也没有事务。然后在children里添加报错。
即:主--REQUIRED
parent--无
~children--无
结果是
既没有数据数据写入,不管是parent和children都进行了回滚。
那我们换一种方式:
主--无
parent--无
~children--REQUIRED
结果是
可以发现parent没有回滚。
那我们再换一种方式:
主--REQUIRED
parent--无
~children--REQUIRED
结果是
我们再结合文档看看
/**
* Support a current transaction, create a new one if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>This is the default setting of a transaction annotation.
*/
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
由此我们可以看出REQUIRED是当前方法使用事务,并且会影响当前方法下的子方法。如果当前方法和子方法都存有REQUIRED事务,则合并成一个REQUIRED事务,即当前方法事务。(常用于增删改)。
2.SUPPORTS :
我们先看代码
/**
* Support a current transaction, execute non-transactionally if none exists.
* Analogous to EJB transaction attribute of the same name.
* <p>Note: For transaction managers with transaction synchronization,
* {@code SUPPORTS} is slightly different from no transaction at all,
* as it defines a transaction scope that synchronization will apply for.
* As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
* will be shared for the entire specified scope. Note that this depends on
* the actual synchronization configuration of the transaction manager.
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
*/
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
可以发现SUPPORTS传播犹如其名是需要事务支持。
例子:
主--无
parent--无
~children--SUPPORTS
结果:
可以发现事务并没有生效。
例子:
主--REQUIRED
parent--无
~children--SUPPORTS
结果:
可以发现事务生效。
由此我们可以看出SUPPORTS必须需要有事务支持才能生效,否则没有事务,它自己也将无效。(用于查询)
3.MANDATORY :
先看代码:
/**
* Support a current transaction, throw an exception if none exists.
* Analogous to EJB transaction attribute of the same name.
*/
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
可以得出MANDATORY必须要有事务支持,不和SUPPORTS不同,MANDATORY的事务支持是强制性的,不然会抛出异常。
例子:
主--无
parent--无
~children--MANDATORY
结果:
由此我们可以看出MANDATORY必须需要有事务支持,否则没有事务,它就抛出异常。(可检查事务存在)
4.REQUIRES_NEW
看代码:
/**
* Create a new transaction, and suspend the current transaction if one exists.
* Analogous to the EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code javax.transaction.TransactionManager} to be
* made available to it (which is server-specific in standard Java EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
可以看出REQUIRES_NEW和REQUIRED的不同就在于,如果主方法存在事务,那么子方法就会单独创建一个事务,不会合并。
例子:
主--REQUIRED
parent--无
~children--REQUIRES_NEW
结果:
可以发现REQUIRES_NEW和REQUIRED一样,那么我们错误给主方法。
例子:
~主--REQUIRED
parent--无
children--REQUIRES_NEW
结果:
可以发现children并没有回滚。
由此我们可以看出REQUIRES_NEW在当前没有事务的时候和REQUIRED一样。如果主方法有事务,子方法的REQUIRES_NEW事务不会受到主方法的影响。(可用于日志收集)
5.NOT_SUPPORTED :
看代码:
/**
* Execute non-transactionally, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code javax.transaction.TransactionManager} to be
* made available to it (which is server-specific in standard Java EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
例子:
主--无
parent--无
~children--NOT_SUPPORTED
结果:
可以发现没有事务回滚。
例子:
主--REQUIRED
parent--无
~children--NOT_SUPPORTED
结果:
可以发现只有children没有进行事务回滚。
由此我们可以看出NOT_SUPPORTED的传播就是另当前方法不受事务影响。
6.NEVER :
看代码:
/**
* Execute non-transactionally, throw an exception if a transaction exists.
* Analogous to EJB transaction attribute of the same name.
*/
NEVER(TransactionDefinition.PROPAGATION_NEVER),
如果当前有事务存在,就抛出异常。
例子:
主--REQUIRED
parent--无
~children--NEVER
结果:
例子:
主--无
parent--无
~children--NEVER
结果:
可以看出事务不生效。
例子:
主--NEVER
parent--无
~children--NEVER
结果:
可以看出事务不生效。子方法也没有吧主方法的NEVER当做事务存在。
由此我们可以看出NEVER的传播就是检测该方法是否存在事务,自己本身不具有事务性质。
7.NESTED :
看代码:
/**
* Execute within a nested transaction if a current transaction exists,
* behave like {@code REQUIRED} otherwise. There is no analogous feature in EJB.
* <p>Note: Actual creation of a nested transaction will only work on specific
* transaction managers. Out of the box, this only applies to the JDBC
* DataSourceTransactionManager. Some JTA providers might support nested
* transactions as well.
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
NESTED(TransactionDefinition.PROPAGATION_NESTED);
可以发现NESTED和REQUIRES_NEW一样也会单独创建事务。
例子:
~主--REQUIRED
parent--无
children--NESTED
结果:
由此可以例子:
主--REQUIRED
parent--无
~children--NESTED (追加try catch)
结果:
可以发现主方法是成功执行,且children的异常也被抛出,并且回滚。parent并没有收到children异常的影响。
由此我们可以看出NESTED 的传播在主方法事务下给子方法开启受主方法事务影响的嵌套事务,但是子事务的异常不会影响到主事务。
总结:
事务传播 - Propagation
REQUIRED: 使用当前的事务,如果当前没有事务,则自己新建一个事务,子方法是必须运行在一
如果当前存在事务,则加入这个事务,成为一个整体。
举例:没有领导,部门就是领导。部门犯错,部门受罚,领导或者部门犯错,所有部门一起受罚。
SUPPORTS: 如果当前有事务,则使用事务;如果当前没有事务,则不使用事务。
举例:没有领导,部门就不工作。领导或者部门犯错,所有部门一起受罚。
MANDATORY: 该传播属性强制必须存在一个事务,如果不存在,则抛出异常
举例:没有领导,部门就不工作而且还报警。领导或者部门犯错错,所有部门一起受罚。
REQUIRES_NEW: 如果当前有事务,则挂起该事务,并且自己创建一个新的事务给自己使用;
如果当前没有事务,则同 REQUIRED
举例:没有领导,部门就是领导。部门犯错,部门受罚,领导或者部门犯错,和部门无关就不用受罚。
NOT_SUPPORTED: 如果当前有事务,则把事务挂起,自己不适用事务去运行数据库操作
举例:没有领导,部门就不工作。有领导,该部门也不工作。所以领导犯错,不管部门有没有错,部门都不受罚。
NEVER: 如果当前有事务存在,则抛出异常
举例:没有领导,部门不当领导也不工作。有领导,部门不工作而且还报警。
NESTED: 如果当前有事务,则开启子事务(嵌套事务),嵌套事务是独立提交或者回滚;
如果当前没有事务,则同 REQUIRED。
但是如果主事务提交,则会携带子事务一起提交。
如果主事务回滚,则子事务会一起回滚。相反,子事务异常,则父事务可以回滚或不回滚。
举例:没有领导,部门就是领导。领导犯错,所有部门一起受罚。部门犯错,部门受罚且不会影响其他部门(前提部门要try-catch)。