Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/spring/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 带有@Async的嵌套@Transactional方法_Java_Spring_Jpa_Spring Transactions - Fatal编程技术网

Java 带有@Async的嵌套@Transactional方法

Java 带有@Async的嵌套@Transactional方法,java,spring,jpa,spring-transactions,Java,Spring,Jpa,Spring Transactions,我在JPA中使用Spring。我已启用了@EnableAsync和@EnableTransactionManagement。在我的用户注册服务方法中,我调用了一些其他的服务方法,它们被注释为@Async。这些方法可以做很多事情,比如发送欢迎电子邮件,在我们的第三方支付系统中注册新用户 在我想验证第三方支付系统是否成功创建了用户之前,一切正常。此时,@Async方法尝试创建一个UserAccount(它引用新创建的用户)并出错为javax.persistence.EntityNotFoundExc

我在JPA中使用Spring。我已启用了
@EnableAsync
@EnableTransactionManagement
。在我的用户注册服务方法中,我调用了一些其他的服务方法,它们被注释为
@Async
。这些方法可以做很多事情,比如发送欢迎电子邮件,在我们的第三方支付系统中注册新用户

在我想验证第三方支付系统是否成功创建了用户之前,一切正常。此时,
@Async
方法尝试创建一个
UserAccount
(它引用新创建的
用户
)并出错为
javax.persistence.EntityNotFoundException:找不到id为2017的com.dk.st.model.User

register调用如下所示:

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);
}
@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;
}
private User registerUser(User newUser,Boolean waitForAccount){
字符串username=newUser.getUsername();
字符串email=newUser.getEmail();
//…验证用户是否不存在
//我在这里尝试了各种各样的洗脸和承诺,没有任何效果
newUser=userDAO.merge(newUser);
//这里是我们向支付系统注册新用户的地方。
//我们刚刚合并的用户不在数据库中
Future newCustomer=paymentService.initializeForNewUser(新用户);
//这里是我偶尔(在测试方法中)暂停此线程以等待的地方
//为了成功创建帐户。
如果(waitForAccount){
试一试{
newCustomer.get();
}捕获(例外e){
logger.error(“创建用户帐户时出现异常!”,e);
}
}
//做一些其他事情,可能是,也可能不是@Aysnc
返回新用户;
}
支付服务调用来完成注册用户的工作,如下所示:

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);
}
@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;
}
@Async
@凌驾
@事务性(传播=传播。需要\u新建)
公共未来初始值FornewUser(用户newUser){
//…设置customerParams
客户newCustomer=null;
试一试{
newCustomer=Customer.create(customerParams);
UserAccount newAccount=newuseraccount();
newAccount.setUser(newUser);
newAccount.setCustomerId(newCustomer.getId());
newAccount.setStatus(AccountStatus.PRE_-TRIAL);
//合并时,JPA在DB中找不到newUser对象并发出投诉
userAccountDAO.merge(newAccount);
}捕获(例外e){
logger.error(“创建用户帐户时出错!”,e);
投掷e;
}
返回新结果(newCustomer);
}
列出的StackOverflow答案表明我设置了一个
需要新的
传播,我已经这样做了,但没有这样的运气

谁能给我指出正确的方向吗?我真的不想直接从控制器方法调用paymentService。我觉得这应该是一个服务级别的电话


谢谢你的帮助

尝试创建一个新的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);


带有@Transactional REQUIRES的新用户服务\u new应该强制提交并解决问题。

在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
定义如下所示:

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);
}
@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;
}
@覆盖
@事务性(传播=传播。需要\u新建)
公共用户createUser(用户newUser){
字符串username=newUser.getUsername();
字符串email=newUser.getEmail();
if((username!=null)&&(userDAO.getUserByUsername(username)!=null)){
抛出新EntityAlreadyExistsException(“用户已注册:+用户名”);
}
if(userDAO.getUserByUsername(newUser.getEmail())!=null){
抛出新EntityAlreadyExistsException(“用户已注册:+电子邮件”);
}
返回userDAO.merge(newUser);
}
@凌驾
@事务性(传播=传播。需要\u新建)
公共用户进程新注册(
用户newUser,
布尔值waitForAccount)
{
未来客户=paymentService.initializeForNewUser(新用户);
如果(waitForAccount){
试一试{
customer.get();
}捕获(例外e){
logger.error(“创建客户对象时出错!”,e);
}
}
//做一些其他维护类型的事情。。。
返回新用户;
}
Vyncent指出交易管理是一个问题。创建其他服务使我能够更好地控制这些事务何时提交。虽然我最初对采用这种方法犹豫不决,但这是与Spring管理的事务和代理的权衡


我希望这有助于其他人以后节省一些时间。

首先阅读时,我会说新创建的用户在执行支付服务之前没有刷新。我也这么认为。我在userDAO.merge()调用中添加了一个flush()调用,这样用户就可以立即使用了,但没有这样的运气。似乎
@Async
@Transactional
方法正在完全不同的DB会话中运行。仅供参考:这正是发生的情况。@Async methodcall是在不同的线程上处理的(这就是Async的意思),根据定义,这意味着一个不同的事务。这实际上让我走上了一条道路(以及更深层次的理解——谢谢!),我认为这意味着这永远不会像预期的那样工作。由于事务需要_NEW,因此会创建一个与第一个事务完全隔离的完全独立的事务。对t中的
User
对象调用的任何后续
merge