Nested @Transactional methods with @Async

后端 未结 2 1155
一个人的身影
一个人的身影 2021-02-07 13:44

I\'m using Spring with JPA. I have @EnableAsync and @EnableTransactionManagement turned on. In my user registration service method, I have a few other

2条回答
  •  半阙折子戏
    2021-02-07 14:01

    With Vyncent's help, here is the solution that I arrived at. I created a new class called UserCreationService and put all of the method that handled User creation in that class. Here is an example:

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

    You'll notice that there is NO @Transactional annotation on this method. This is on purpose. The corresponding createUser and processNewRegistration definitions look like this:

    @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 was spot on that transaction management was the issue. Creating the other service allowed me to have better control over when those transactions committed. While I was hesitant to take this approach initially, that's the tradeoff with Spring managed transactions and proxies.

    I hope this helps someone else save some time later.

提交回复
热议问题