目录
一、isolation
- 类型:Isolation
- 事务的隔离级别:读未提交、读已提交、可重复读、串行化。
- 有事务的业务逻辑,ioc容器中保存的是这个业务逻辑的代理对象。
- 使用:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
- 根据业务的特性,进行调整。
二、propagation
- 类型:Propagation
- 事务的传播行为(事务的传播和行为):设置这个事务是不是和之前的大事务共享同一个事务(使用同一个连接);
如果有多个事务进行嵌套运行,那么子事务是否要和大事务共用一个事务。
AService{
//A的一些方法
tx_Mul(){
tx_A(){
}
tx_B(){
}
}
}
- 例:对于REQUIRED
REQUIRED:required 当前事务和之前的大事务共用一个事务。 如果其中任何一个地方挂了,整个车就爆掉了,就都回滚。 REQUIRES_NEW:当前事务总是使用一个新的事务,如果已经有事务,会将之前的事务挂起。 假设事务A,B都是Requires_New 开启大事务 -> 挂起大事务 -> 开启事务A ->事务A提交 ->开启事务B ->事务B提交 -开启大事务 ->大事务提交 这时小车之间互相不影响,A爆了回滚A, B和大事务照样运行。总结:
1、任何处崩,已经执行的requires_new都会成功,不会受到影响。
2、对于REQUIRED的事务,其属性来源于大事务。
意思是说,当一个事务的属性设置为required时,其本身的属性就失效了,比如timeout等,这时属性由其共用事务的大事务的属性决定
Tx_A(){ TX_B(propagation=Propagation.REQUIRED,timeout=5){ } }
这时B共用A的事务,B的timeout并不会生效,因为它用的属性继承自A,A并没有设置timeout属性。
3、本类事务方法之间的调用就只是一个事务。
对于类外调用XX类的事务方法
MulServiceProxy.mulTx(){ XXXServiceProxy.tx_A(); XXXServiceProxy.tx_A(); }
同一个类事务方法互相调用
XXXServiceProxy.mulTx(){ tx_A(); tx_B(); } A和B就没有代理的事务控制了,就只剩mulTx一个事务
BookService
@Transactional(propagation = Propagation.REQUIRED)
public void checkout(String userName, String ISBN, Integer number){
//减库存
bookDao.updateStock(ISBN, number);
//计算价格
float price = bookDao.getPrice(ISBN);
//减余额
bookDao.updateBalance(userName, price * number);
}
//改价格
@Transactional(propagation = Propagation.REQUIRED)
public void updatePrice(String isbn,int price){
bookDao.updatePrice(isbn,price);
int i = 10/0;
}
MulService
@Service
public class MulService {
@Autowired
private BookService bookService;
@Transactional()
public void mulTx(){
bookService.checkout("Rocke","ISBN-001",1);
bookService.updatePrice("ISBN-003",998);
}
}
在MulService的mulTx这个事务中,调用BookService的两个事务方法,并且其中一个事务存在异常。两个子事务设置的required,即,mulTx存在事务,所以两个字事务方法会共用大事务mulTx的事务。
结果运行失败,全体回滚。
如果把第一个小事务check调整为required_new,这个时候,updatePrice和大事务mulTx共用一个事务,check单独使用一个事务。updatePrice挂了,它自己回滚,check不会受到影响(不回滚)。
REQUIRED:是将之前事务的connection传递过来给这个方法使用。
REQIURES_NEW:是这个方法直接使用新的connection。
三、noRollbackFor
- 对于不同的异常,有不同的处理方式:
运行时异常(非检查异常),可以不处理,默认都回滚;
编译时异常(检查异常),要么try/catch,要么throws,默认不回滚。
- 事务的回滚:默认发生运行时异常都回滚,发生编译时异常不会回滚
- 类型:Class<? extends Throwable>[]
- 指定哪些异常事务可以不回滚,可以让原来默认回滚的异常不回滚。
- 例:对于运行时异常java.lang.ArithmeticException,默认是回滚的,当我们使用noRollbackFor指定不回滚时:
当异常在事务最后时
/*结账:*/ @Transactional(noRollbackFor = {ArithmeticException.class,NullPointerException.class}) public void checkout(String userName, String ISBN, Integer number) { //减库存 bookDao.updateStock(ISBN, number); //计算价格 float price = bookDao.getPrice(ISBN); //减余额 bookDao.updateBalance(userName, price * number); int i = 10/0; }
当异常在事务中间时: 减库存而不减余额。也就是异常前的操作执行,异常后的操作不执行。
/*结账:*/ @Transactional(noRollbackForClassName = {"java.lang.ArithmeticException"}) public void checkout(String userName, String ISBN, Integer number) { //减库存 bookDao.updateStock(ISBN, number); //计算价格 int i = 10/0; float price = bookDao.getPrice(ISBN); //减余额 bookDao.updateBalance(userName, price * number); }
- 结论:异常照样抛,事务不回滚
四、noRollbackForClassName
- 类型:String[],String全类名
- 同上
- 例:
@Transactional(noRollbackForClassName = {"java.lang.ArithmeticException"})
五、rollbackFor
- 类型:Class<? extends Throwable>[]
- 指定哪些异常事务需要回滚,
- 让原本不回滚的异常(编译时异常)事务回滚。
- 例:对于FileNotFoundException这个编译时异常,我们选择抛出
@Transactional(rollbackFor = {FileNotFoundException.class})
public void checkout(String userName, String ISBN, Integer number) throws FileNotFoundException {
//减库存
bookDao.updateStock(ISBN, number);
//计算价格
float price = bookDao.getPrice(ISBN);
//减余额
bookDao.updateBalance(userName, price * number);
new FileInputStream("E://Editor");
}
使用throws抛出异常,事务可以使用rollbackFor指定回滚,而使用try/catch,事务无法指定回滚。
六、rollbackForClassName
- 类型:String[]
- 同上
七、readOnly
- 类型:boolean,默认false
- 修改事务的只读属性,设置事务为只读事务
- 可以进行事务优化:当事务的所有操作都是**只读**时,设置readOnly = true,可以加快查询速度,不用管事务的操作了(加锁等等)
- 但事务有增删改操作时,设置readOnly会抛出异常:
java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed
八、timeout
- 类型:int
- 事务超出指定执行时长后自动终止并回滚
- 以秒为单位
- 超时抛出异常并回滚:
TransactionTimedOutException: Transaction timed out