2

I am trying to create an API for transferring funds i.e withdrawal and deposit. I performed the transaction using @Transactional Annotation. But there are certain criteria i.e if bank account number doesn't exist that should through an Runtime exception. I will attach the code within. Now, when transferBalanceMethod is called and if depositor bank Account doesn't exist than amount that is withdrawn should also be rolled back. But that isn't happening. Means when fund transfer occurs from account A to Account B of 1000 rupees then if an exception occurs in B's deposition then the withdraw in A Account should also be withdrawn. I tried @Transactional annotation and also rollbackFor property of the Exception class too I tried to add @Transaction annotation for deposit and withdraw method too but that we use the same transaction because we use propogation Required**

Model Class//This is the Model Class
//All Imports

@Entity
public class BankAccount {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Integer id;

    @Column(name = "bankAccountNumber", nullable = false,unique = true)
    @NotNull
    @Size(min = 5, message = "Bank account number should be greater than 5 characters")
    private String bankAccountNumber;

    @NotNull
    @Column(name = "balance", nullable = false)
    @Min(1000)
    private Long balance;

//Getter Setter and Constructor

**Controller File**//This is the Controller Class

//All imports and other stuff such as @RestController, @Autowired   

@GetMapping("/bankaccount/transfer")
public void transferBalance(@RequestParam("bankAccountNo1") String bankAccountNo1, @RequestParam("bankAccountNo2") String bankAccountNo2,
            @RequestParam("balance") Long balance) throws RuntimeException
    {
        bankService.transferBalance(bankAccountNo1,bankAccountNo2, balance);
    }

}

**Service File:-**//This is Service Layer
//All imports

@Service
public class BankService {

    @Autowired
    private BankRepository bankRepository;

    @Autowired
    private ModelMapper modelMapper;

    public List<BankAccountDTO> getAllBankAccount() {
        List<BankAccountDTO> bankAccountDTO = new ArrayList<BankAccountDTO>();
        List<BankAccount> bankAccount = bankRepository.findAll();
        for (BankAccount b : bankAccount) {
            bankAccountDTO.add(modelMapper.map(b, BankAccountDTO.class));
        }
        return bankAccountDTO;
    }

    public ResponseEntity<?> getIndividualBankAccount(String bankAccountNumber) {
        BankAccount bankAccount = bankRepository.findByBankAccountNumber(bankAccountNumber);
        if (bankAccount == null) {
            return new ResponseEntity<>("Account not found", HttpStatus.BAD_REQUEST);
        } else {
            return new ResponseEntity<>(
                    modelMapper.map(bankRepository.findByBankAccountNumber(bankAccountNumber), BankAccountDTO.class),
                    HttpStatus.OK);
        }
    }

    public Object addBankAccount(BankAccountDTO bankAccountDTO) {
        return bankRepository.save(modelMapper.map(bankAccountDTO, BankAccount.class));
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void depositBalance(String bankAccountNumber, Long balance) throws RuntimeException {
        BankAccount bankAccNo = bankRepository.findByBankAccountNumber(bankAccountNumber);

        if (bankAccNo == null) {
            throw new RuntimeException("Bank Accout Number is not found : " + bankAccountNumber);
        } else {
            if (balance <= 0) {
                throw new RuntimeException("Please deposit appropriate balance");
            } else {
                Long amount = bankAccNo.getBalance() + balance;
                bankAccNo.setBalance(amount);
                bankRepository.save(bankAccNo);
            }
        }

    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void withdrawBalance(String bankAccountNumber, Long balance) throws RuntimeException {
        BankAccount bankAccNo = bankRepository.findByBankAccountNumber(bankAccountNumber);
        if (bankAccNo == null) {
            throw new RuntimeException("Bank Account not found :" + bankAccountNumber);
        } else {
            if (balance <= 0) {
                throw new RuntimeException("Please withdraw appropriate balance");
            } else {
                Long amount = bankAccNo.getBalance() - balance;
                if (amount < 1000) {
                    throw new RuntimeException("Sorry Cannot withdraw.Your minimum balance should be thousand rupees!");
                } else {
                    bankAccNo.setBalance(amount);
                    bankRepository.save(bankAccNo);
                }
            }
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = RuntimeException.class)
    public void transferBalance(String bankAccountNo1, String bankAccountNo2, Long balance) throws RuntimeException {
        try {
            withdrawBalance(bankAccountNo1, balance);
            depositBalance(bankAccountNo2, balance);

        } catch (RuntimeException e) {
            throw e;
        }

    }

}
Vandit Shah
  • 105
  • 7

1 Answers1

0

Only runtime exceptions will trigger a rollback operation within a spring transaction annotation, if you are using custom annotations you need to make sure you either made then extend from RuntimeException or that you add an specific rollback clause to your transaction so that it rollback in that specific exception.

maybe this answer will be useful for you: Spring transaction: rollback on Exception or Throwable

also to go to the official transactional documentation of spring here: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html

Folger Fonseca
  • 183
  • 1
  • 1
  • 11