Spring事务注解的注意事项

112 阅读2分钟

缺省情况下,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 { // ... }