簡體   English   中英

使用@Async嵌套@Transactional方法

[英]Nested @Transactional methods with @Async

我正在使用Spring和JPA。 我打開了@EnableAsync@EnableTransactionManagement 在我的用戶注冊服務方法中,我有一些我稱之為@Async其他服務方法。 這些方法可以執行各種操作,例如發送歡迎電子郵件以及使用我們的第三方支付系統注冊新創建的用戶。

一切正常,直到我想驗證第三方支付系統是否成功創建了用戶。 此時, @Async方法嘗試創建UserAccount (引用新創建的User )並使用javax.persistence.EntityNotFoundException: Unable to find com.dk.st.model.User with id 2017輸出錯誤javax.persistence.EntityNotFoundException: Unable to find com.dk.st.model.User with id 2017

注冊調用如下所示:

private User registerUser(User newUser, Boolean waitForAccount) {
    String username = newUser.getUsername();
    String email = newUser.getEmail();

    // ... Verify the user doesn't already exist

    // I have tried all manner of flushing and committing right here, nothing works
    newUser = userDAO.merge(newUser);

    // Here is where we register the new user with the payment system.
    // The User we just merged is not /actually/ in the DB
    Future<Customer> newCustomer = paymentService.initializeForNewUser(newUser);
    // Here is where I occasionally (in test methods) pause this thread to wait
    // for the successful account creation.
    if (waitForAccount) {
        try {
            newCustomer.get();
        } catch (Exception e) {
            logger.error("Exception while creating user account!", e);
        }
    }

    // Do some other things that may or may not be @Aysnc

    return newUser;
}

支付服務呼叫完成注冊用戶的工作,如下所示:

@Async
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Future<Customer> initializeForNewUser(User newUser) {
    // ... Set up customerParams

    Customer newCustomer = null;
    try {
        newCustomer = Customer.create(customerParams);

        UserAccount newAccount = new UserAccount();
        newAccount.setUser(newUser);
        newAccount.setCustomerId(newCustomer.getId());
        newAccount.setStatus(AccountStatus.PRE_TRIAL);

        // When merging, JPA cannot find the newUser object in the DB and complains
        userAccountDAO.merge(newAccount);
    } catch (Exception e) {
        logger.error("Error while creating UserAccount!", e);
        throw e;
    }

    return new AsyncResult<Customer>(newCustomer);
}

這里列出的StackOverflow答案表明我設置了一個REQUIRES_NEW傳播,我已經完成了,但沒有這樣的運氣。

誰能指出我正確的方向? 我真的不想直接從我的控制器方法調用paymentService。 我覺得這應該是一個服務水平的電話肯定。

謝謝你的幫助!

在Vyncent的幫助下,這是我到達的解決方案。 我創建了一個名為UserCreationService的新類,並將處理User創建的所有方法放在該類中。 這是一個例子:

@Override
public User registerUserWithProfileData(User newUser, String password, Boolean waitForAccount) {
    newUser.setPassword(password);
    newUser.encodePassword();
    newUser.setJoinDate(Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTime());

    User registered = userService.createUser(newUser);
    registered = userService.processNewRegistration(registered, waitForAccount);

    return userService.setProfileInformation(registered);
}

你會注意到這個方法沒有 @Transactional注釋。 這是故意的。 相應的createUserprocessNewRegistration定義如下所示:

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public User createUser(User newUser) {
    String username = newUser.getUsername();
    String email = newUser.getEmail();

    if ((username != null) && (userDAO.getUserByUsername(username) != null)) {
        throw new EntityAlreadyExistsException("User already registered: " + username);
    }

    if (userDAO.getUserByUsername(newUser.getEmail()) != null) {
        throw new EntityAlreadyExistsException("User already registered: " + email);
    }

    return userDAO.merge(newUser);
}

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public User processNewRegistration(
        User newUser,
        Boolean waitForAccount) 
{
    Future<UserAccount> customer = paymentService.initializeForNewUser(newUser);
    if (waitForAccount) {
        try {
            customer.get();
        } catch (Exception e) {
            logger.error("Error while creating Customer object!", e);
        }
    }

    // Do some other maintenance type things...

    return newUser;
}

Vyncent認為交易管理是個問題。 創建其他服務使我能夠更好地控制何時提交這些事務。 雖然我最初猶豫不決采取這種方法,但這是與Spring托管交易和代理的權衡。

我希望這可以幫助別人以后節省一些時間。

嘗試創建一個新的UserService類來管理用戶檢查,就像這樣

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public User createOrUpdateUser(User newUser) {
    String username = newUser.getUsername();
    String email = newUser.getEmail();

    // ... Verify the user doesn't already exist

    // I have tried all manner of flushing and committing right here, nothing works
    newUser = userDAO.merge(newUser);
    return newUser;
}

然后在實際的課堂上,改變

private User registerUser(User newUser, Boolean waitForAccount) {
    String username = newUser.getUsername();
    String email = newUser.getEmail();

    // ... Verify the user doesn't already exist

    // I have tried all manner of flushing and committing right here, nothing works
    newUser = userDAO.merge(newUser);

通過

private User registerUser(User newUser, Boolean waitForAccount) {
    newUser = userService.createOrUpdateUser(newUser);

帶有@Transactional REQUIRES_NEW的新userService應該強制提交並解決問題。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM