Transactions in Spring boot

 Cloud Technology Service - Free image on Pixabay

1. Overview

In this tutorial, we will learn about the concept of transactions in Spring.
We will discuss the two types of transactions programmatic and declarative transactions.
 

2. The code used in the tutorial 

Steve sends money to Alex; In this example, the money has been debited from Steve's account but not added to Alex's account.


public boolean sendMoney(){

    Account steve = this.accountRepository.findById(1L).get();
    Account alex = this.accountRepository.findById(2L).get();
    // steve is sending 100$ to alex

    steve.setBalance( steve.getBalance() - 100 );
    this.accountRepository.save(steve);

     // Oops something went wrong while sending money to alex
     if (true)
         throw new RuntimeException("something went wrong");

     alex.setBalance(alex.getBalance() + 100 );
     this.accountRepository.save(alex);

     return true;
}

3. Programmatic transaction

The programmatic transaction is achieved by implementing transaction management code in your service methods to control commit and rollback.
The transaction is committed if the code does NOT throw a runtime exception, otherwise it rolls back.
 
To implement programmatic transactions we need to:
1 - Inject PlatformTransactionManager in your service
2 - Create TransactionTemplate instance
3 - Call TransactionTemplate execute method
 
The following is an example of a programmatic transaction.


// In this example we used programatic transaction to solve the problem
// 1) Inject PlatformTransactionManager
@Autowired
PlatformTransactionManager platformTransactionManager;
public boolean sendMoney(){
     // 2) Create TransactionTemplate instance
     TransactionTemplate transactionTemplate = new TransactionTemplate();
     // 3) Call TransactionTemplate execute method
       transactionTemplate.execute(new TransactionCallback <Object>() {
        @Override
        public Object doInTransaction(TransactionStatus transactionStatus) {
             Account steve = accountRepository.findById(1L).get();
             Account alex = accountRepository.findById(2L).get();

             steve.setBalance( steve.getBalance() - 100 );
             accountRepository.save(steve);

              // RuntimeException => Rollback previous query(ies)

              if (true)
                 throw new RuntimeException("something went wrong");

              alex.setBalance(alex.getBalance() + 100 );
              accountRepository.save(alex);
              return true;
            }
        });
   return true;
}

Because we have a runtime exception the money is not debited from Steve's account.

Important: Transaction is rolled-back only if we get an unchecked exception ( Runtime exception or DataAccessException ) or if you call setRollBackOnly method from TransactionStatus.

3. Declarative transaction management

Declarative transaction is straightforward; to use a declarative transaction you simply need to add to your transactional methods @Transactional annotation.


@Transactional
public boolean sendMoney(){

     Account steve = this.accountRepository.findById(1L).get();
     Account alex = this.accountRepository.findById(2L).get();

     steve.setBalance( steve.getBalance() - 100 );
     this.accountRepository.save(steve);

     // **  Rolls-back because we have @Transactional annotation **
     if (true)
        throw new RuntimeException("something went wrong");

     alex.setBalance(alex.getBalance() + 100 );
     this.accountRepository.save(alex);

     return true;
}

You can also appy the @Transactional annotation in the class level, this way all public methods in the class will be defined as transactional. 

4. Conclusion

In this tutorial, we learned about programmatic and declarative transaction management in the next tutorial we will discuss about the propagation transaction attribute.

Post a Comment

0 Comments