gpt4 book ai didi

java - 使用@Async 嵌套@Transactional 方法

转载 作者:IT老高 更新时间:2023-10-28 13:49:56 25 4
gpt4 key购买 nike

我将 Spring 与 JPA 一起使用。我打开了 @EnableAsync@EnableTransactionManagement。在我的用户注册服务方法中,我调用了一些其他的服务方法,它们被注释为 @Async。这些方法可以做各种各样的事情,比如发送欢迎电子邮件和向我们的第三方支付系统注册新用户。

在我想验证第三方支付系统是否成功创建用户之前,一切正常。此时,@Async 方法会尝试创建一个 UserAccount(它引用新生成的 User)并使用 出错javax.persistence.EntityNotFoundException:无法找到 com.dk.st.model.User 的 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 答案 here建议我设置一个 REQUIRES_NEW 传播,我已经这样做了,但没有这样的运气。

谁能指出我正确的方向?我真的不想直接从我的 Controller 方法调用 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);
}

您会注意到此方法上有 NO @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 托管事务和代理的权衡。

我希望这可以帮助其他人在以后节省一些时间。

关于java - 使用@Async 嵌套@Transactional 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29258436/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com