【问题标题】:How to SendEmail only if transaction is success with spring-transactions仅当事务通过spring-transactions成功时如何发送电子邮件
【发布时间】:2019-06-16 12:50:57
【问题描述】:

我想在数据库中创建一个用户,并使用 AWS SES 向这个案例的用户发送电子邮件。

  1. 如果用户在数据库中提交事务成功 => 发送电子邮件
  2. 如果发送电子邮件 AWS SES 失败(检查异常)=> 在数据库中回滚用户创建
  3. 如果用户在数据库中提交事务失败 => 不要使用 AWS 向用户发送电子邮件

我的代码有问题:如果我的 sendEmail 方法抛出异常,则提交事务。

配置:使用 spring-data-jpa 的 Spring-Boot 项目

class EmailServiceImpl {   

    @Transactional(rollbackFor = Exception.class)
    @Override
    public User createUserAndSendEmail(UserDto userDto) throws UserEmailException {
        try {
            //rollback userCreation if sendEmail throw a checkedException
            User user = userService.create(userDto);
            sendEmail(user);
            return user;

        } catch (Exception exception) {
            throw new UserEmailException(exception.getMessage());
        }
    }

    //don't send email if userCommit in database failed
    private void sendEmail(User user) {
        amazonEmailService.sendMail(user);
    }
}

class UserServiceImpl {    

   @Transactional(propagation = Propagation.REQUIRES_NEW)
   @Override
   public User create(UserDto userDto) throws Exception {
       User user = mapp(userDto);
       return userRepository.save(user);
   }
}

【问题讨论】:

  • 您的用户创建是在一个新事务中完成的,因为它具有传播级别REQUIRES_NEW。所以它会在邮件发送之前提交到数据库中。如果发送邮件失败,它仍然会被提交,因为它是一个单独的事务。要么把所有事情都变成一个单一的交易,要么在发送电子邮件失败时采取补偿措施。

标签: java spring spring-data-jpa spring-transactions


【解决方案1】:

要在 TX 提交后执行某些操作,您可以使用 @TransactionalEventListenerTransactionPhase.AFTER_COMMIT(默认设置)。把你想做的动作放在TransactionalEventListener

使用ApplicationEventPublisher 发布UserCreatedEvent

public class EmailServiceImpl {   

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Transactional(rollbackFor = Exception.class)
    public User createUserAndSendEmail(UserDto userDto) throws UserEmailException {
        try {
            User user = userService.create(userDto);

            //Publish UserCreatedEvent such the UserCreatedEventHandler can handled it after TX commit
            applicationContext.publishEvent(new UserCreatedEvent(user));

            return user;
        } catch (Exception exception) {
            throw new UserEmailException(exception.getMessage());
        }
    }
}

并且UserCreatedEvent 将在 TX 提交后由该处理程序处理:

@Component
public class UserCreatedEventHandler {

    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handle(UserCreatedEvent event) {    
        try{
            amazonEmailService.sendMail(user);
            System.out.println("Good, can send email.");
        }catch(Exception exception){
            System.out.println("Sad, fail to send email , so remove user from DB...");
            userService.remove();
        }
    }
}

Deinum 的好球。如果您使用我的建议,您必须将userService.create() 更改为@Transactional(propagation = Propagation.REQUIRES)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-15
    • 2012-02-04
    • 2018-11-15
    • 2013-10-10
    • 2015-03-21
    • 2014-11-14
    • 2011-06-02
    • 1970-01-01
    相关资源
    最近更新 更多