I thought it was best practice to put the @Transactional
annotation on the service layer classes and not on the controllers (see f.e. Why we shouldn't make a Spring MVC controller @Transactional?). But this not working on my Spring Boot application. Why is that?
The registerAction
method in the controller (see code below) performs multiple service calls. When f.e. the mailService.sendActivationMail(...)
fails, I want to rollback the inserted user from the userService.registerUser(...)
call. Do I need to put the @Transactional
annotation on the controller class or not?
My Spring Boot application correctly uses transactions when the @Transactional
annotation is set on the controller class:
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;
}
}
but transactions don't work when the @Transactional
annotation is set on the Service classes instead (and not on the controller):
UserService.java
@Service
@Transactional
public class UserService {
@Autowired
private ApiUserRepository userRepository;
public ApiUser registerUser(Registration registration) {
...
userRepository.save(user);
...
}
}
My configuration classes:
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());
}
}