java – 处理spring数据中的并发事务

我正在使用spring Data.
我有弹簧数据并发事务的问题如下:

实体和存储库如下:

    @Entity
    public class Wallet {

        @Version
        private int version;
        @Id
        @GeneratedValue
        @OrderColumn
        private Long Id;
        @OneToOne()
        @OrderColumn
        private User user;
        @OrderColumn
        private Double virtualBalance;
        @Column(name = "created_by")
        @OrderColumn
        private String createdBy;
        @Column(name = "created_date")
        @OrderColumn
        private Date createdDate;
        @Column(name = "updated_by")
        @OrderColumn
        private String updatedBy;
        @Column(name = "updated_date")
        @OrderColumn
        private Date updatedDate;
... Setters and getters ...
}

存储库如下

public interface WalletJpaRepository extends JpaRepository<Wallet, Long>{

    @Lock(LockModeType.OPTIMISTIC) // I have also tried PESSIMISTIC, READ, WRITE, PESSIMISTIC_READ, PESSIMISTIC_WRITE, etc.but they don't seem to work
    Wallet findOne(Long id);

}

我正在同时调用两个方法,如下所示:

@Test
    public void testConcurrentTransactions() {
        System.out.println("Wallet 1 : ->" + getWallet1());
        System.out.println("Wallet 2 : ->" + getWallet2());
    }

这两种方法如下所述

@Transactional(isolation = Isolation.SERIALIZABLE)
private Wallet getWallet1() {
    Wallet wallet1 = walletJpaRepository.findOne(new Long(1)); // suppose the value of wallet1.getVirtualBalance() is 1000
    wallet1.setVirtualBalance(wallet1.getVirtualBalance().doubleValue() + 100); // After evaluating this line it becomes 1100
    System.out.println(Thread.currentThread().getId());
    return wallet1;
}

@Transactional(isolation = Isolation.SERIALIZABLE)
private Wallet getWallet2() {
    Wallet wallet2 = walletJpaRepository.findOne(new Long(1)); // Here again the value of wallet2.getVirtualBalance() fetched is 1000 but I need 1100 to be the value read
    System.out.println(Thread.currentThread().getId());
    return wallet2;
}

问题是我没有在不同的方法调用中获得相同实体的更新值.

例如,如果在调用方法getWallet1()之后,id为1的实体的值最初为1000,则该值应更新为1100,但它不会反映在第二个方法中,即getWallet2(),并且我再次得到1000在上面代码的注释中解释的第二种方法中.

我尝试过传播,隔离,锁定,但仍然没有得到所需的结果.

有没有一个解决方案来处理这样的场景,我无法找到这种情况的解决方案,这是一个简化版本的scenerio,我在一个巨大的货币交易系统,其命中率大约是4到5每秒交易量.

以上只是我试图重现场景的一个例子,下面是相同的实际代码.

@Override
@Transactional
public InterWalletRequestFrontendWrapper approveOrDeclineRequest(User requestingUser, String operation,
        String requestId) {

    InterWalletRequest walletRequest = interWalletRequestJpaRepository.findOne(Long.parseLong(requestId));
    if (walletRequest.getStatus().equalsIgnoreCase(Utility.statusInitiated)
            || walletRequest.getStatus().equalsIgnoreCase(Utility.statusPending)) {
        if (operation.equalsIgnoreCase(Utility.operationDecline)) {
            walletRequest.setStatus(Utility.statusDeclined);
            interWalletRequestJpaRepository.save(walletRequest);
            InterWalletRequestFrontendWrapper response = fetchRaisedRequests(requestingUser);
            response.setStatus(0);
            response.setStatusDesc(Utility.statusDeclined);
            return response;
        } else {

            User admin = walletRequest.getRequestTo();
            Wallet adminWallet = admin.getWallet();

            if (adminWallet.getVirtualBalance() >= walletRequest.getAmount()) {
                try {

                    User user = walletRequest.getRequestFrom();

                    UserWalletTransaction txn1 = new UserWalletTransaction();
                    UserWalletTransaction txn2 = new UserWalletTransaction();
                    /**
                     * New transaction initiated for admin
                     */
                    txn1.setAmountTransacted(walletRequest.getAmount());
                    txn1.setDebitUser(admin);
                    txn1.setCreditUser(user);
                    txn1.setOperationPerformed(Utility.operationPerformedInterWallet);
                    txn1.setPreviousAmount(admin.getWallet().getVirtualBalance());
                    txn1.setStatus(Utility.statusNew);
                    txn1.setUser(admin);
                    txn1.setTransactionType(Utility.transactionTypeDebit);
                    txn1.setCreatedBy(admin.getUserName());
                    txn1.setUpdatedBy(admin.getUserName());
                    txn1.setCreatedDate(new Date());
                    txn1.setUpdatedDate(new Date());
                    txn1.setWallet(admin.getWallet());

                    /**
                     * New txn initiated for the user who walletRequested
                     * the txn.
                     */
                    txn2.setAmountTransacted(walletRequest.getAmount());
                    txn2.setDebitUser(admin);
                    txn2.setCreditUser(user);
                    txn2.setOperationPerformed(Utility.operationPerformedInterWallet);
                    txn2.setPreviousAmount(user.getWallet().getVirtualBalance());
                    txn2.setStatus(Utility.statusNew);
                    txn2.setTransactionType(Utility.transactionTypeCredit);
                    txn2.setCreatedBy(admin.getUserName());
                    txn2.setUpdatedBy(admin.getUserName());
                    txn2.setCreatedDate(new Date());
                    txn2.setUpdatedDate(new Date());
                    txn2.setUser(user);
                    txn2.setWallet(user.getWallet());

                    txn2 = walletTransactionJpaRepository.save(txn2);

                    Wallet wallet1 = admin.getWallet();
                    wallet1.setVirtualBalance(admin.getWallet().getVirtualBalance() - walletRequest.getAmount());
                    wallet1 = walletJpaRepository.save(wallet1);

                    /**
                     * After debit set the reference of other user.
                     */

                    txn1.setRelationalTransaction(txn2);
                    /**
                     * After debit from admin set balance amount
                     * 
                     */
                    txn1.setBalanceAmount(wallet1.getVirtualBalance());

                    /**
                     * Money deducted from admin wallet but not credited to
                     * the user wallet. so status is pending.
                     */
                    txn1.setStatus(Utility.statusPending);
                    txn1 = walletTransactionJpaRepository.save(txn1);

                    Wallet wallet2 = user.getWallet();
                    wallet2.setVirtualBalance(user.getWallet().getVirtualBalance() + walletRequest.getAmount());
                    wallet2 = walletJpaRepository.save(wallet2);

                    /**
                     * After credit to User wallet add balance amount.
                     */
                    txn2.setBalanceAmount(wallet2.getVirtualBalance());

                    txn1.setStatus(Utility.statusSuccess);
                    txn2.setStatus(Utility.statusSuccess);
                    txn2.setRelationalTransaction(txn1);

                    List<UserWalletTransaction> transactions = new ArrayList<>();
                    transactions.add(txn1);
                    transactions.add(txn2);

                    walletTransactionJpaRepository.save(transactions);

                    walletRequest.setStatus(Utility.statusApproved);
                    interWalletRequestJpaRepository.save(walletRequest);

                    InterWalletRequestFrontendWrapper response = fetchRaisedRequests(requestingUser);
                    response.setStatus(0);
                    response.setBalance(wallet1.getVirtualBalance());
                    response.setStatusDesc(Utility.statusApproved);
                    return response;

                } catch (Exception e) {
                    System.out.println(".......... Exception Caught ..........");
                    walletRequest.setStatus(Utility.statusPending);
                    interWalletRequestJpaRepository.save(walletRequest);
                    InterWalletRequestFrontendWrapper response = fetchRaisedRequests(requestingUser);
                    response.setStatus(0);
                    response.setStatusDesc(Utility.statusDeclined);
                    return response;
                }
            } else {
                /**
                 * if the admin wallet desn't have enough balance then the
                 * status is set to pending.
                 */
                walletRequest.setStatus(Utility.statusPending);
                interWalletRequestJpaRepository.save(walletRequest);
                InterWalletRequestFrontendWrapper response = fetchRaisedRequests(requestingUser);
                response.setStatus(0);
                response.setStatusDesc(Utility.statusDeclined);
                return response;
            }
        }
    } else {
        InterWalletRequestFrontendWrapper response = fetchRaisedRequests(requestingUser);
        response.setStatus(0);
        response.setStatusDesc(Utility.statusDeclined);
        return response;
    }

}

另一种在同一实体上运行的方法如下所示

@Override
@Transactional
private UserWalletTransaction initiateVerifyTransaction(AccountsDetails transfer, User user) {

        Double amountTransacted = 2.00;
        Wallet wallet = user.getWallet();
        UserWalletTransaction transaction = new UserWalletTransaction();
        transaction.setAmountTransacted(amountTransacted);

        transaction.setPreviousAmount(wallet.getVirtualBalance());
        transaction.setOperationPerformed(Utility.operationPerformedDVBeneFundTransfer);
        transaction.setTransactionType(Utility.transactionTypeDebit);

        /**
         * Debit from wallet.
         */
        wallet.setVirtualBalance(wallet.getVirtualBalance() - amountTransacted);
        wallet.setUpdatedDate(new Date());
        wallet.setUpdatedBy(user.getUserName());
        wallet = walletJpaRepository.save(wallet);
        logger.info(wallet);

        transaction.setBalanceAmount(wallet.getVirtualBalance());
        transaction.setUser(user);
        transaction.setWallet(wallet);
        transaction.setStatus(Utility.statusNew);
        transaction.setCreatedBy(user.getUserName());
        transaction.setUpdatedBy(user.getUserName());
        transaction.setCreatedDate(new Date());
         transaction.setToAccount(transfer.getAccount());
         transaction.setBankName(transfer.getBankName());
         transaction.setBeniMobile(transfer.getRecipientMobileNo());
         transaction.setTransactionMode(transfer.getChannel().equalsIgnoreCase("2")
         ? "IMPS" : "NEFT");
        return walletTransactionJpaRepository.save(transaction);

    }

像这样,在不同服务中有七种方法可以同时访问钱包,因为可以同时登录多个用户,并且概率是用户管理员也登录并执行货币交易,即我们遇到这个问题的真实场景.

提前致谢

最佳答案
大家好,我将回答我自己的问题,这可能会对将来有所帮助,我找到了解决问题的方法.感谢Denium指出了问题所在.这真是一个伟大的概念.

我正在做的错误是对方法进行内部调用并在私有方法上编写@Transactional.

@Transactional是使用spring AOP实现的,内部方法调用永远不会实际到达代理,并且@Transactional的功能行为很奇怪.

因此,解决方案是将方法包装在对象中,并在对象的方法上定义@Transactional,并仅对对象进行外部调用.

其他解决方案可能是定义我们自己的削减点和建议

有关更多参考,请访问以下链接:

http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html
https://www.mkyong.com/spring/spring-aop-example-pointcut-advisor/

请随意添加任何建议和编辑,

谢谢

转载注明原文:java – 处理spring数据中的并发事务 - 代码日志