I have created a simple spring boot application which will demonstrate how the bank transactions happens.I have created one 'Account' entity and created one 'debit' rest endpoint.
Here I am calling 'debit' api two times concurrently but only one time amount is debited.I want to know how can I lock the account entity so that another thread will read updated balance and will debit second time too.
I tried to lock 'account' entity with lock mode type as PESSIMISTIC_WRITE but its not working.
Account.java
package hello;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import javax.persistence.*;
@Table(name = "account")
@Entity // This tells Hibernate to make a table out of this class
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Account {
    //@Version
    @Column(name="version")
    private Integer version;
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer userId;
    @Column(name = "name")
    private String name;
    @Column(name="balance")
    private int balance;
    public Integer getUserId() {
        return userId;
    }
    public void setUserId(Integer userId) {
        this.userId = userId;
    }
    public int getBalance() {
        return balance;
    }
    public void setBalance(int balance) {
        this.balance = balance;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return "Account{" +
                "userId=" + userId +
                ", name='" + name + '\'' +
                ", balance=" + balance +
                '}';
    }
}
Rest end point is
    @GetMapping(path = "/debit")
    public ResponseEntity<String>  debit() {
        Integer withdrawAmount = 100;
        Integer userId = 1;
        log.debug("debit  {} from account id {} ",withdrawAmount,userId);
        accountService.debit(userId,withdrawAmount);
        return ResponseEntity.badRequest().body("debited");
    }
AccountService.java
package hello.service;
import hello.Account;
import hello.AccountRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import java.util.Optional;
@Service
public class AccountService {
    final private Logger log = LoggerFactory.getLogger(AccountService.class);
    @Autowired
    private AccountRepository accountRepository;
    @Autowired
    private EntityManager entityManager;
    @Transactional
    public void debit(Integer id,int balance){
        Optional<Account> accountOptional = accountRepository.findById(id);
        Account account = accountOptional.get();
        entityManager.refresh(account, LockModeType.PESSIMISTIC_WRITE);
        final int oldBalance = account.getBalance();
        log.debug("current balance {}",oldBalance);
        account.setBalance(oldBalance-balance);
        accountRepository.save(account);
        log.debug("debited");
    }
}
AccountRepository.java
package hello;
import org.springframework.data.repository.CrudRepository;
// This will be AUTO IMPLEMENTED by Spring into a Bean called userRepository
// CRUD refers Create, Read, Update, Delete
public interface AccountRepository extends CrudRepository<Account, Integer> {
        Account findOneByUserId(Integer userId);
}
my database record is - click here to see the image
To test this scenario I have written on bash script debit.sh
curl -I 'http://localhost:8080/demo/debit' &
curl -I 'http://localhost:8080/demo/debit' &
run this with bash debit.sh So it can call same rest endpoint twice.
The output I am getting is
2019-03-27 14:17:36.375 DEBUG 11191 --- [nio-8080-exec-3] hello.MainController                     : debit  100 from account id 1 
2019-03-27 14:17:36.376 DEBUG 11191 --- [nio-8080-exec-4] hello.MainController                     : debit  100 from account id 1 
2019-03-27 14:17:36.394 DEBUG 11191 --- [nio-8080-exec-4] hello.service.AccountService             : current balance 100
2019-03-27 14:17:36.394 DEBUG 11191 --- [nio-8080-exec-3] hello.service.AccountService             : current balance 100
2019-03-27 14:17:36.395 DEBUG 11191 --- [nio-8080-exec-4] hello.service.AccountService             : debited
2019-03-27 14:17:36.396 DEBUG 11191 --- [nio-8080-exec-3] hello.service.AccountService             : debited
In both the transactions reading current balance as 100 and debiting same. What I want here is It should update balance as -100.
 
    