目录
一.使用总结
1.不要在接口上声明@Transactional ,而要在具体类的方法上使用 @Transactional 注解,否则注解可能无效。
2.不建议将@Transactional放置在类级的声明中,放在类级声明中会使得所有方法都有事务。故@Transactional应该放在方法级别,不需要使用事务的方法就不要放置事务,比如查询方法,否则对性能是有影响的。
3.@Transactional 注解只能应用到 public 可见度的方法上。 如果应用在protected、private或者 package可见度的方法上,也不会报错,不过事务设置不会起作用。
4.一个类中的事务方法只有在被外部类调用时,该类的事务方法才会起作用。原因:Spring在扫描bean的时候会扫描方法上是否含有@Transactional 注解,如果包含,spring会为这个bean动态的生成一个子类(即代理类:proxy)【面向切面编程】,代理类继承原来的bean。当这个有注解的方法被外部类调用时,实际上是由代理类来调用的,代理类在调用之前就会启动transaction。然而,如果这个有注解的方法是被本类中其他方法调用,那么该方法调用并没有通过代理类,而是直接通过原来的bean,所以就不会启动transaction。
5.在同一个类中,一个方法调用另一个有@Transactional注解的方法,注解是不会生效的。
6.默认情况下,Spring会对unchecked异常进行事务回滚;如果是checked异常则不回滚。只有在异常抛出的时候才会回滚,如果是catch捕获了,事务不会回滚。可以在catch里面加上throw new RuntimeException()使事务进行回滚。
7.和锁同时使用需要注意:由于Spring事务是通过AOP实现的,所以在方法执行之前会有开启事务,之后会有提交事务逻辑。而synchronized代码块执行是在事务之内执行的,可以推断在synchronized代码块执行完时,事务还未提交,其他线程进入synchronized代码块后,读取的数据不是最新的。所以必须使synchronized锁的范围大于事务控制的范围,把synchronized加到Controller层或者大于事务边界的调用层!
二.事务回滚情况举例
A类中的methodA为调用者,B类中的methodB为被调用者。methodA和methodB以propagation = Propagation.REQUIRED 为事务传播属性。
1.被调用者方法methodB内未作任何异常处理,但是在执行过程中,methodB抛出了异常。methodA会产生事务回滚。
@Service
public class TestA {
@Autowired
private TestB testB;
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// dboper1 --数据库操作1
testB.methodB();
// dboper2 --数据库操作2
}
}
@Service
public class TestB {
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
//dboper3 --数据库操作3
}
}
2.被调用者方法methodB内进行了try catch处理,在执行过程中,methodB抛出了异常,但是被catch捕获。methodA不会产生事务回滚。
public class TestA {
@Autowired
private TestB testB;
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// dboper1 --数据库操作1
testB.methodB();
// dboper2 --数据库操作2
}
}
@Service
public class TestB {
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
try {
//dboper3 --数据库操作3
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.被调用者方法methodB内进行了try catch处理,但是在catch块中,显式抛出了RuntimeException。在执行过程中,methodB抛出了异常,被catch捕获后,抛给了外层调用者。methodA会产生事务回滚。原理同1。
@Service
public class TestA {
@Autowired
private TestB testB;
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// dboper1 --数据库操作1
testB.methodB();
// dboper2 --数据库操作2
}
}
@Service
public class TestB {
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
try {
//dboper3 --数据库操作3
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException();
}
}
}
4.被调用者方法methodB内未作任何异常处理。在methodA中对被调用代码块methodB进行了try catch处理。如果methodB执行过程中抛出了异常,那么会报Transaction rolled back because it has been marked as rollback-only的异常。原理:因为事务传播属性设置为Propagation.REQUIRED,那么methodB和methodA用的就是同一个事务,在执行methodB方法过程中,由于抛出了异常,那么spring就在执行methodB方法过程中将事务状态标记为rollback-only,虽然在methodA中将methodB异常捕获,但是由于事务状态已经被标记为rollback-only,所以在methodA也不会正常的提交事务,报Transaction rolled back because it has been marked as rollback-only的异常。
@Service
public class TestA {
@Autowired
private TestB testB;
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// dboper1 --数据库操作1
try {
testB.methodB();
} catch (Exception e) {
}
// dboper2 --数据库操作2
}
}
@Service
public class TestB {
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
//dboper3 --数据库操作3
}
}