使用@Async嵌套@Transactional方法
我正在使用Spring和JPA。 我打开了@EnableAsync
和@EnableTransactionManagement
。 在我的用户注册服务方法中,我有一些我称之为@Async
其他服务方法。 这些方法可以执行各种操作,例如发送欢迎电子邮件以及使用我们的第三方支付系统注册新创建的用户。
一切正常,直到我想validation第三方支付系统是否成功创建了用户。 此时, @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 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 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(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
注释。 这是故意的。 相应的createUser
和processNewRegistration
定义如下所示:
@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 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应该强制提交并解决问题。