Reputation: 210
I want to create a user in database and send email with AWS SES to user with this case.
Problem with my code : Transaction is commited if my sendEmail
method throw a exception.
Configuration: Spring-Boot project with spring-data-jpa
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);
}
}
Upvotes: 7
Views: 3439
Reputation: 90497
To do certain actions after TX commit , you can use @TransactionalEventListener
with TransactionPhase.AFTER_COMMIT
(Default setting). Put the action that you want to do in the TransactionalEventListener
:
Use the ApplicationEventPublisher
to publish 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());
}
}
}
And the UserCreatedEvent
will be handled by this handler after TX commit:
@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();
}
}
}
Good catch by Deinum. If you use my suggestion , you have to change userService.create()
to @Transactional(propagation = Propagation.REQUIRES)
Upvotes: 9