Spring Boot cookbook · Intermediate

Send emails after a transaction commits in Spring

Send a confirmation email only if the order was actually saved, without slowing down the request.

Approach

Publish a domain event inside the transactional service, handle it with @TransactionalEventListener(phase = AFTER_COMMIT) and @Async so it runs after a successful commit on another thread. For guaranteed delivery across crashes, use an outbox table instead.

Solution

public record OrderPlaced(long orderId, String email) {}

@Service
class OrderService {
    private final OrderRepository orders;
    private final ApplicationEventPublisher events;
    OrderService(OrderRepository orders, ApplicationEventPublisher events) { this.orders = orders; this.events = events; }

    @Transactional
    public Order place(PlaceOrder cmd) {
        Order order = orders.save(Order.from(cmd));
        events.publishEvent(new OrderPlaced(order.getId(), cmd.email()));
        return order;
    }
}

@Component
class OrderConfirmationMailer {
    private final JavaMailSender mail;
    OrderConfirmationMailer(JavaMailSender mail) { this.mail = mail; }

    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void on(OrderPlaced event) {
        var msg = new SimpleMailMessage();
        msg.setTo(event.email());
        msg.setSubject("Order #" + event.orderId() + " confirmed");
        msg.setText("Thanks for your order!");
        mail.send(msg);
    }
}

@Configuration
@EnableAsync
class AsyncConfig {}

Pitfalls

  • Plain @EventListener runs before commit (emails for rolled-back orders).
  • Lost emails if the app crashes after commit — use an outbox for guarantees.
  • Unbounded async executors (configure spring.task.execution.pool.*).

Variations

  • Spring Modulith's event publication registry persists events for at-least-once delivery.
  • Send via a queue (RabbitMQ/SQS) consumed by a mailer service.

Further reading: Spring: Transaction-bound events

Reference: https://docs.spring.io/spring-framework/reference/data-access/transaction/event.html

More Spring Boot recipes