2016-08-22 4 views
5

Ich verwende spring Data. Ich habe ein Problem mit der Feder Daten gleichzeitig ablaufenden Transaktionen wie folgt:Behandlung gleichzeitige Transaktionen im Frühjahr Daten

Das Unternehmen und die Repositories sind wie folgt:

@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 ... 
} 

Die repository ist wie folgt

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); 

} 

ich, damit ich bin ein Methodenaufruf an zwei der Methoden gleichzeitig, wie unten gezeigt:

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

Und die beiden Methoden wie unten

@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; 
} 

beschrieben Das Problem ist, dass ich nicht aktualisierte Werte der gleichen Einheit in verschiedenen Methodenaufrufe bekommen.

Wenn zum Beispiel der Wert der Entity mit der ID 1 den Wert 1000 hat, sollte der Wert nach dem Aufruf der Methode getWallet1() auf 1100 aktualisiert werden, aber nicht in der zweiten Methode get getWallet2() und wieder bekomme ich 1000 in der zweiten Methode wie in den Kommentaren des obigen Codes erklärt.

Ich habe mit propagation, Isolation, Lock versucht, aber immer noch bekomme ich nicht die erforderlichen Ergebnisse.

Gibt es eine Lösung, um solch eine Szenerie zu behandeln, kann ich keine Lösung für eine solche Situation finden. Dies ist eine vereinfachte Version eines Szenerio, das ich in einem riesigen monetären Transaktionssystem bekomme, wo die Trefferquote liegt etwa 4 bis 5 Transaktionen pro Sekunde.

Das obige ist nur ein Beispiel, in dem ich gerade versucht habe, das Szenerio zu reproduzieren, Unten ist der eigentliche Code für das gleiche.

@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; 
    } 

} 

Und eine andere Methode, die auf dem gleichen Unternehmen tätig ist, unter

@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); 

    } 

wie diese gibt es sieben Methoden in den verschiedenen Diensten, die die Brieftasche in der gleichen Zeit kann es Zugriff angezeigt Anzahl der Benutzer, die gleichzeitig angemeldet sind, und Wahrscheinlichkeiten sind, dass der Benutzeradministrator auch eingeloggt ist und Geldtransaktionen durchführt, das ist die wirkliche Situation, in der wir dieses Problem bekommen.

Vielen Dank im Voraus

+2

Zuerst gibt es nichts, was gleichzeitig mit Ihrem Test oder Transaktionsdaten übereinstimmt. Ihr Verständnis, wie Spring AOP funktioniert, fehlt und damit auch, wie Transaktionen angewendet werden. Kurz gesagt, es werden Proxies verwendet und nur Methodenaufrufe in ein Objekt werden proxisiert. Also ist Ihr '@ Transactional' für die Methode, die Sie aus Ihrem Testfall aufrufen, grundsätzlich nutzlos (selbst wenn die Methode" public "wäre). Testen Sie die echte Methode, nicht in Ihrem Testfall. Daneben sollte Ihre Serviceebene die Transaktionsschicht und nicht das Repository sein. –

+0

Danke Deinum, um auf diese Dinge hinzuweisen. Diese beiden Methoden befinden sich tatsächlich in der Service-Schicht. Aber ich wollte nur so ein Szenario für das Verständnis reproduzieren. Was ich vermitteln wollte, ist, dass es zwei Dienste gibt, die gleichzeitig aus der Datenbank über das Repository auf dieselbe Entität zugreifen. Also der Wert, der in der Datenbank aktualisiert wird, wenn der letzte festgeschrieben wird. Ich wäre daran interessiert zu verstehen, wie dies implementiert werden kann, so dass ich gleichzeitige Transaktionen behandeln kann. –

+0

Bitte stellen Sie einige Links oder Beispiele zur Verfügung, ich wäre dankbar –

Antwort

4

Hallo Jungs Ich werde meine eigene Frage zu beantworten, dies jemand in Zukunft helfen könnte, habe ich die Lösung für mein Problem gefunden. Danke, Denium, dass du auf das Problem aufmerksam gemacht hast. Es ist wirklich ein großartiges Konzept.

Der Fehler, den ich machte, war interne Aufrufe an Methoden und schreiben @Transactional auf die private Methoden.

@Transactional wird mit spring AOP implementiert, so dass die internen Methodenaufrufe nie tatsächlich den Proxy erreichen und das Verhalten der Funktionalität von @Transactional ist seltsam.

Also die Lösung war, die Methoden in ein Objekt zu wickeln und die @Transactional über die Methoden des Objekts zu definieren, und nur die externen Aufrufe an das Objekt.

Andere Lösungen könnten unsere eigenen point cuts und advice

Weitere Referenz werden Definieren Sie bitte die folgenden Links:

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

Bitte fühlen Sie sich frei, alle Vorschläge und Änderungen hinzuzufügen,

Danke

Verwandte Themen