p0tta
p0tta

Reputation: 1651

Using Spring @Transactional in a method with multiple operations

I have a service which runs every few seconds and processes some records from a queue which is stored in the database. I noticed that a @Transactional attribute added and was trying to figure out what it's usage is and wanted to make sure it's the right usage.

@Scheduled(initialDelay = 10000, fixedDelay = 10000)
@Transactional 
public void notificationTask() {
    
    Integer txId = myDao.getMinTxIdByStatusCode("Pending");
    myDao.updateStatusByTxId(txId, "Processing");
    boolean status = true; 

    Transaction tx = myDao.getTxById(txId);
    if (txId != null) {
     status = myService.processTx(txId, myTx.getX(), myTx.getY());
     if (status) {
        myDao.updateStatusByTxId(txId, "Complete");
     } else {
        myDao.updateStatusByTxId(txId, "Failed");
     }
        
    }
}

My main question here is, if for some reason either the getTxById or the processTx methods fail, does that mean with the @Transational attribute, it will roll back the updateStatusByTxId? What is a use case where the Transactional attribute would be used here?

Upvotes: 1

Views: 1780

Answers (1)

Indivon
Indivon

Reputation: 1874

When you use @Transactional on a method, Spring wraps the entire method in a transaction. This means that a new transaction is started before the method is entered and committed after the method is exited. If there is an exception, the whole transaction will be rolled back. That's what is called "atomicity": transactions are "all or nothing".

Thus, @Transactional is a "shortcut" for something like this when using Hibernate:

@Scheduled(initialDelay = 10000, fixedDelay = 10000)
public void notificationTask() {
    Transaction transaction = null;  
    try {  
        Session session = sessionFactory.openSession();  //Hibernte session
        transaction = session.beginTransaction();  
        //... your stuff
        Integer txId = myDao.getMinTxIdByStatusCode("Pending");
        myDao.updateStatusByTxId(txId, "Processing");
        boolean status = true; 

        Transaction tx = myDao.getTxById(txId);
        if (txId != null) {
          status = myService.processTx(txId, myTx.getX(), myTx.getY());
          if (status) {
             myDao.updateStatusByTxId(txId, "Complete");
          } else {
             myDao.updateStatusByTxId(txId, "Failed");
          }
        
        }
        //... your stuff done
        transaction.commit();  
    }catch (Exception ex) {  
        ex.printStackTrace();  
        transaction.rollback();  
    }
}

So, if your method updateStatusByTxId is just a database call (is not flushing, nor committing or has is own nested transaction): yes, it is rolled back.

And since the transaction is also active, when entering processTx, this service call will also be in the same transaction, even if you add an additional @Transactional to processTx. Except you explicitly start a new transaction by e.g. @Transactional(Transactional.TxType.REQUIRES_NEW).

Additionally, Spring has a really detailed doc: https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction

Upvotes: 1

Related Questions