Spring boot 2 @Transactional annotation makes Autowired fields null

Viewed 2245

I'm trying to use @Transactional annotation in a method on my service to lazily load a field. However using @Transactional on my Implementation class makes all autowired fields null.

Here is my implementation :

@Service
public class UserServiceImpl implements UserService {

 /**
  * DefaultMapper.
  */
 @Autowired
 private DefaultMapper defaultMapper;

 /**
  * Resource service injection.
  */
 @Autowired
 private ResourceService resourceService;

 /**
  * UserRepository.
  */
 @Autowired
 private UserRepository userRepository;

 /**
  * Jwt Factory.
  */
 @Autowired
 private JwtService jwtService;

 @Override
 @Transactional
 public final UserDto findByLogin(final String login) throws ResourceNotFoundException {
 // user repository is null here when using @Transactional
  User user = this.userRepository.findByLogin(login)
   .orElseThrow(() -> new ResourceNotFoundException(
    resourceService.getMessage(MessageBundle.EXCEPTION, "resource.notfound.user.login")
   ));
  UserDto userDto = defaultMapper.asUserDtoWithRoles(user);
  return userDto;
 }

Thank you in advance.

3 Answers

I faced the same problem working with Kotlin. When I added the @Transactional annotation to a method inside a service, I got a message saying Methods annotated with '@Transactional' must be overridable so I went ahead and marked both, the class and the method as open. Easy, right?! Well, not quite.

Although this compiles, I got the required repository as null at execution time. I was able to solve the problem in two ways:

  1. Mark the class and ALL of its methods as open:
open class FooService(private val barRepository: BarRepository) {
    open fun aMethod(): Bar {
        ...
    }

    @Transactional
    open fun aTransactionalMethod(): Bar {
        ...
    }
}

This works but having all the methods in a class marked with open might look a bit odd, so I tried something else.

  1. Declare an interface:
interface IFooService {
    fun aMethod(): Bar

    fun aTransactionalMethod(): Bar
}

open class FooService(private val barRepository: BarRepository) : IFooService {
    override fun aMethod(): Bar {
        ...
    }

    @Transactional
    override fun aTransactionalMethod(): Bar {
        ...
    }
}

This way you can still use the annotation since all the methods will be overridable and you won't need to use open everywhere.

Hope this helps =)

Notice - final on the method you execute creates the problem, not necessary the method marked with @Transnational. @Transnational annotation lead to dynamic creation of proxy objects. When having final method, it will not run on the proxy objects.

Related