在@Service类(而不是控制器)上Spring Boot @Transactional不工作

时间:2016-01-05 15:06:17

标签: spring transactions spring-boot

我认为最佳做法是将@Transactional注释放在服务层类而不是控制器上(参见f.e. Why we shouldn't make a Spring MVC controller @Transactional?)。但这不适用于我的Spring Boot应用程序。那是为什么?

控制器中的registerAction方法(请参阅下面的代码)执行多个服务调用。当f.e。 mailService.sendActivationMail(...)失败,我想从userService.registerUser(...)调用回滚插入的用户。我是否需要将@Transactional注释放在控制器类上?

我的Spring Boot应用程序在控制器类上设置@Transactional注释时正确使用事务:

AuthController.java

@RestController
@RequestMapping("/api/auth")
@Transactional
public class AuthController {

    @Autowired
    private UserService userService;

    @Autowired
    private ProfileService profileService;

    @Autowired
    private MailService mailService;

    @RequestMapping(path = "register", method = RequestMethod.POST)
    public Profile registerAction(@Valid @RequestBody Registration registration) {
        ApiUser user = userService.registerUser(registration);
        Profile profile = profileService.createProfile(user, registration);

        mailService.sendActivationMail(user);

        return profile;
    }

}

但是,当在服务类上设置@Transactional注释时(而不是在控制器上),事务不起作用:

UserService.java

@Service
@Transactional
public class UserService {

    @Autowired
    private ApiUserRepository userRepository;

    public ApiUser registerUser(Registration registration) {
        ...
        userRepository.save(user);
        ...
    }

}

我的配置类:

SpringApiApplication.java

@SpringBootApplication
public class SpringApiApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringApiCommonApplication.class, args);
    }
}

ApiConfiguration.java

@Configuration
@EnableJpaAuditing
@EnableTransactionManagement
public class ApiConfiguration {
    @Autowired
    private ApiProperties properties;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public UsernameCanonicalizer usernameCanonicalizer() {
        return new UsernameCanonicalizer();
    }

    @Bean
    public EmailCanonicalizer emailCanonicalizer() {
        return new EmailCanonicalizer();
    }

    @Bean
    public ApiTokenHandler activationTokenHandler() {
        return new StandardApiTokenHandler(properties.getActivationTokenDuration());
    }
}

2 个答案:

答案 0 :(得分:2)

@ M.Deinum 让我走上正轨。 Spring(引导)不会像其他框架那样自动将控制器调用包装在事务中。因此,您必须向控制器添加@Transactional注释,或者将代码从控制器类移动到服务类。

将代码从控制器类移动到服务类是更好的事情,因为(除其他外)使代码更易于测试。这就是我所做的。

<强> AuthenticationService.java

@Service
public class AuthenticationService {
    @Autowired
    private UserManager userManager;

    @Autowired
    private ProfileManager profileManager;

    @Autowired
    private MailManager mailManager;

    @Transactional
    public Profile registerUser(Registration registration) {
        ApiUser user = userManager.registerUser(registration);
        Profile profile = profileManager.createProfile(user, registration);

        mailManager.sendActivationMail(user);

        return profile;
    }

    ...
}

<强> AuthController.java

@RestController
@RequestMapping("/api/auth")
public class AuthController {
    @Autowired
    private AuthenticationService authenticationService;

    @RequestMapping(path = "register", method = RequestMethod.POST)
    @ApiOperation(value = "Registers a user")
    public Profile register(@Valid @RequestBody Registration registration) {
        return authenticationService.registerUser(registration);
    }

    ...
}

答案 1 :(得分:-3)

您可以将@Transactional注释放在接口定义,接口上的方法,类定义或类的公共方法之前。但是,仅仅存在@Transactional注释不足以激活事务行为。

@Transactional注释只是某些运行时基础结构可以使用的元数据,@Transactional-aware可以使用元数据来配置具有事务行为的相应Bean。

在前面的示例中,<tx:annotation-driven/>元素会切换事务行为。

    // Below is the service class that we want to make transactional
    @Transactional
    public class DefaultEmployeeService implements EmployeeService {

        void insertEmployee(Employee Employee);

        void updateEmployee(Employee Employee);
    }

    XML Configuration:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" ...>

        <!-- this is the service object that we want to make transactional -->
        <bean id="employeeService" class="service.DefaultEmployeeService"/>

        <!-- enable the configuration of transactional behavior based on annotations -->
        <tx:annotation-driven transaction-manager="txManager"/>

        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- (this dependency is defined somewhere else) -->
            <property name="dataSource" ref="dataSource"/>
        </bean>
    </beans>

您可以尝试一些观点:

  1. 执行上述所有配置。
  2. 从Controller中删除@Transactional配置。
  3. 引用Service对象和事务管理器bean。
  4. 更多详情:http://docs.spring.io/spring/docs/current/spring-framework-reference/html/transaction.html