缺省情况下,Spring事务基于Spring AOP技术,此时使用事务注解@Transactional需要留意以下问题
1.不要在protected,private或者包内可见方法上使用该注解
2.@Transactional注解的方法在子调场景中无效,例如:
@Service
class FooService{
// 该方法有事务注解
@Transactional
public void methodB(){
//...
}
// 注意:该方法没有事务注解
public void methodA(){
// 这里调用了同一个对象的另一个方法 methodB(), 虽然 methodB 上使用了
// 注解 @Transactional, 但事实上却无效
this.methodB();
}
}
3.不要从@Transactional方法内向外传递任何对当前事务做结论的信息
@Service
class OrderService {
@Transactional
public void completeOrder(int orderId){
// 1. 数据库操作 : 操作订单表,将订单状态修改为完成
final OrderStatus targetStatus=OrderStatus.COMPLETED;
updateOrderStatus(orderId,targetStatus);
// 2. 数据库操作 : 往订单变更记录表中插入一条记录,记录相应的订单变化
addOrderChangeRecord(orderId,targetStatus);
// 3. 发送消息操作 : 向外部通知订单完成的消息
notifyOnOrderComplete(orderId);
}
错误很明确:整个完成订单数据库操作被回滚,像是没事发生一样,但是这条消息已经发出声明订单已经完成了。
4.尽量不要从@Transactional方法内捕获数据操作异常
有的时候,你想捕获数据库操作异常,然后根据异常进行相应的处理。比如你可能想在下面方法中遇到主键重复时捕获异常然后进行处理:
@Transactional
public long addUser(String username,String password)
{
try {
// 添加用户的数据库操作
}
catch (DataIntegrityViolationException e)
{
// 输出日志,重新组织异常以便给用户提供一个友好易于理解的错误消息
}
}
基于这段代码,是希望在遇到DataIntegrityViolationException 时进行捕获异常处理,但实际上catch中的语句并不会被执行,原因和上面的一样,真正的异常抛出会发生在方法体代理方法中,此时已经超出了上面catch所关注的范围,所以上面的catch逻辑并不会如期执行。
5.默认情况下@Transactional所管理的方法中,如果方法抛出运行时异常或error,那么会进行事务回滚;如果抛出的异常是非运行时异常那么则不会回滚。
6.Configuring Spring To Use AspectJ
@Configuration @ComponentScan @EnableTransactionManagement(mode = AdviceMode.ASPECTJ) class ApplicationConfiguration { // ... }