Search code examples
javaspring-bootspring-data-jpatransactions

Start a transaction for saving only


I'm wondering what is the recomended way to proceed in the following scenario:

  • Let's say I have a ReportService responsible for generating a report, uploading it, sending an email and saving it on database.
  • While saving in database, it would update a lot of entities and I would like to rollback the whole thing in case something fails.

So it would be something like:

@Service
public class ReportService {

    public void generateReport() {

        var entities = entityRepository.findEntities();

        var file = getReportData(entities);

        var url = uploadFile(file);
        
        sendEmail(url);

        // If something goes wrong while save on entity, roll back everything
        for (var e : entities) {
           entityRepository.save(e);
        }
    }
}

So the options I know I have are:

  • Annotate the method with @Transactional: I don't think this is a good idea because we would be mixing HTTP requests and workload with database transaction
  • Creating a whole new service, something like ReportSavingService, and having a method to only save those entities: This would work I think but it seems strange to me having an specific service dealing with the same business logic I already have a service for, just to save it...

What I am wondering is if those are the solutions we have dealing with Spring Data or if there is an easy way to do something like:

        // ...
        sendEmail(url);

        // If something goes wrong while save on entity, roll back everything
        database.beginTransaction();
        for (var e : entities) {
           entityRepository.save(e);
        }
        database.commit();

I know we have that kind of control going down a bit, using EntityManager directly, but then I cannot mix it with Spring Data's repositories methods... Can I?


Solution

  • What you are looking for is the TransactionTemplate

    @Autowired
    TransactionTemplate tx;
    
    // ...
    
        tx.execute(__ -> {
            for (var e : entities) {
               entityRepository.save(e);
            }
        }
    

    I do agree with @crizzis comment that the design seems somewhat wrong, but I consider that somewhat besides the question.