This Spring service, EmailsOutgoingService, is responsible for processing outgoing emails and it is scheduled to run every 60 seconds using the @Scheduled(fixedRate = 60000) annotation.
Basically it checks the status of the email in the queue and performs operations on the existing record or creates another one.
The problem is that the “repository.save” transaction is not committed to the database.
@Service
public class EmailsOutgoingService {
@Autowired
private JavaMailSender emailSender;
@Autowired
private EmailsOutgoingRepository repository;
@Scheduled(fixedRate = 60000)
@Transactional
public void processEmails() {
System.out.println("Processing emails...");
List<EmailsOutgoing> pendingEmails = repository.findByDeliveryStatus(EmailsOutgoing.STATUS_PENDING);
List<EmailsOutgoing> failedEmails = repository.findByDeliveryStatus(EmailsOutgoing.STATUS_FAILED);
for (EmailsOutgoing pendingEmail : pendingEmails) {
System.out.println("Processing pending email with ID: " + pendingEmail.getId());
sendEmail(pendingEmail);
}
for (EmailsOutgoing failedEmail : failedEmails) {
System.out.println("Processing failed email with ID: " + failedEmail.getId());
retryFailedEmail(failedEmail);
}
}
public void sendEmail(EmailsOutgoing email) {
try {
System.out.println("Sending email...");
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(email.getEmailReceivers().split(","));
message.setSubject(email.getEmailSubject());
message.setText(email.getEmailBody());
emailSender.send(message);
email.setDeliveryStatus(EmailsOutgoing.STATUS_DELIVERED);
repository.save(email);
System.out.println("Email sent successfully, status updated to DELIVERED with ID: " + email.getId());
} catch (Exception e) {
System.out.println("Failed to send email: " + e.getMessage());
email.setDeliveryStatus(EmailsOutgoing.STATUS_FAILED);
email.setEmailMessage(e.getMessage());
repository.save(email);
System.out.println("Email failed, status updated to FAILED with ID: " + email.getId());
EmailsOutgoing newEmail = new EmailsOutgoing();
newEmail.setEmailSubject(email.getEmailSubject());
newEmail.setEmailBody(email.getEmailBody());
newEmail.setDeliveryStatus(EmailsOutgoing.STATUS_PENDING);
newEmail.setEmailEntity(email.getEmailEntity());
newEmail.setClientCode(email.getClientCode());
newEmail.setEmailReceivers(email.getEmailReceivers());
repository.save(newEmail);
System.out.println("Created new pending email with ID: " + newEmail.getId());
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void retryFailedEmail(EmailsOutgoing failedEmail) {
System.out.println("Retrying failed email...");
EmailsOutgoing newEmail = new EmailsOutgoing();
newEmail.setEmailSubject(failedEmail.getEmailSubject());
newEmail.setEmailBody(failedEmail.getEmailBody());
newEmail.setDeliveryStatus(EmailsOutgoing.STATUS_PENDING);
newEmail.setEmailEntity(failedEmail.getEmailEntity());
newEmail.setClientCode(failedEmail.getClientCode());
newEmail.setEmailReceivers(failedEmail.getEmailReceivers());
repository.save(newEmail);
System.out.println("Created new pending email with ID: " + newEmail.getId());
sendEmail(newEmail);
}
}
If I try to call the method from postman everything works normally. I concluded that @Scheduled is not committing the transaction. How can the problem be solved? I also tried creating a separate class with @Scheduled that calls the service class that does the persistence operations but it’s not working.