guy labs guy labs

Autowiring Spring beans in Hibernate/JPA entity listeners

After a long time without a post I will write something about Spring dependency injection in Hibernate/JPA entity listeners.

The problem here is that you are not able to just autowire a Spring bean in a Hibernate/JPA entity listener, because Hibernate for instance instantiates the entity listener before Spring can autowire the beans and therefore all the autowired beans are null.

I found some some other ways to autowire the beans which specify which listeners should be registered in a Hibernate configuration class, but the way I will show you in this post is the simplest one in my opinion. Other opinions are also welcome 😉 I found the idea here at Stackoverflow.

In the following example I want to show you how to add a JPA listener which always sets a creation date and a modification date when a entity is created/modified.

Ok lets start by a simple abstract JPA entity:

Here there is nothing special except the AbstractEntityListener :

And here you already see that we have an autowired Spring bean which returns the current date. This entity listener listens for the pre-update and the pre-persist events and sets the according dates. There is also the static call to the autowire method of the AutowireHelper . That’s the core of the dependency injection in the Hibernate entity listeners:

This AutowireHelper  implements the ApplicationContextAware  interface from Spring with which the helper is able to get a reference of the application context. The autowire method takes the class instance which needs to be autowired and the beans which need to be autowired. (This is not needed but it’s a small performance optimization)

You just need to register this AutowireHelper in your XML or Java Spring configuration and you have fully working Spring dependency injection in Hibernate entity listeners.

Have a nice weekend,

Cheers

Guy

94,396 total views, 15 views today

Share Post :

31 Comments

  • mclane1
    May 13, 2014 6:43 PM at 6:43 PM 

    Hi,
    Thanks very much for your post.
    In the last things you talk about “You JUST need to register this AutowireHelper in your XML”. I’m in spring batch and I don’t know how register an object in my config. Exemple do nothing :

    Regards,

    Thierry

    • guy
      May 13, 2014 7:19 PM at 7:19 PM 

      Hi Thierry,

      I just updated the post and the code to better inject the AutowireHelper bean. If you are using the Java configuration style of Spring Batch then add the following snippet to your configuration class:

      and if you are using the XML style configuration you need to add the following snippet to the XML application context file:

      I hope this helps ;).

      Regards,

      Guy

      • tlmanz
        February 23, 2016 10:25 AM at 10:25 AM 

        Shouldn’t this be updated to?

        @Bean
        public AutowireHelper autowireHelper(){
        return AutowireHelper.getInstance();
        }

        • guy
          February 25, 2016 6:38 AM at 6:38 AM 

          Hi,

          yes you are right. I just changed the previous comment. I think I should deprecate this post as this is really old and not up to date at all.

          Thanks for that.

          Regards,

          Guy

  • mclane1
    May 14, 2014 9:36 AM at 9:36 AM 

    Hi Guy,
    I see another bug :
    Not
    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) {
    AutowireHelper.applicationContext = applicationContext;
    }
    but
    @Override
    public void setApplicationContext(final ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
    }

    • guy
      May 14, 2014 12:36 PM at 12:36 PM 

      Hi Thierry,

      thanks for catching this one. I just edited it here in WP that’s why I missed it. It’s fixed now.

  • mclane1
    May 14, 2014 9:57 AM at 9:57 AM 

    Hi Guy,
    Don’t work.
    this.autowireHelper.autowire(this, this.dateTimeService);
    => NullPointerException.
    Maybe the old version work with the bad static but not the new.

    Regards.

    Thierry

    • guy
      May 14, 2014 12:44 PM at 12:44 PM 

      Hi Thierry,

      well can you revert the changes I’ve done and can you send me you configuration where you add the AutowireHelper bean to your application context?

      And please use the code as it is now in the post. (as it was before)

      Sorry for the circumstances.

      Regards,

      Guy

  • Jonathan
    September 23, 2014 10:48 PM at 10:48 PM 

    Instead of creating the class AutowireHelper, you can call the Spring provided SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this) from within your entity listener. This prevents you from having to specifically list which beans to autowire. Spring will autowire the whole class.

    • guy
      September 24, 2014 7:50 AM at 7:50 AM 

      Hi Jonathan,

      thanks for your comment, I didn’t know this class/method. But in the documentation it says that the “current thread’s context ClassLoader, needs to be the web application’s ClassLoader”. So I think this would not work if you just use plain Spring JPA without any servlet context.

      One could also add an overloaded method which doesn’t take the list of beans and always starts the autowiring process. Then you do not need to specify the beans you want to autowire.

      Regards,

      Guy

  • Joe
    October 26, 2015 6:49 PM at 6:49 PM 

    @Component on AbstractEntityListener is redundant since jpa does not use spring managed instance.

    No need for autowire the instance and hence the instance variable dateTimeService. Simple get the instance required through applicationContext

    • guy
      November 6, 2015 7:50 AM at 7:50 AM 

      Hey Joe,

      thanks for pointing this out. Yes the @Component can be removed (I just updated the post). And yes the AutowireHelper could also be done differently (this code is around two years old ;)) such that it just gets the bean you need from the ApplicationContext.

      Thanks and regards,

      Guy

  • Thibault
    November 17, 2015 6:02 PM at 6:02 PM 

    Any word about thread-safety? Maybe a non issue…

    Also, the loop seems to invoke autowireBean() multiple times when passing multiple beans to inject, which seems unnecessary.

    • guy
      November 17, 2015 7:33 PM at 7:33 PM 

      Hi,

      thanks for your comment and the hint! I just updated the old code to return if the autowireBean() is called once. About the thread safety: I think it could happen that the autowireBean() method is called twice for a bean but that should not be a problem.

      Thanks and regards,

      Guy

  • Sam
    February 1, 2016 11:56 PM at 11:56 PM 

    Hi,
    Thanks for the useful post. I am using spring boot and I am unsure as to where do I register my AutoWireHelper class. Please help.

    • guy
      February 3, 2016 9:14 AM at 9:14 AM 

      Hi Sam,

      thanks for the praise. Please check out this comment.

      Thanks and regards,

      Guy

  • Prasana
    March 28, 2016 5:07 PM at 5:07 PM 

    ApplicationContext is null inside AutowireHelper. How to fix this issue?

    • guy
      March 30, 2016 11:43 AM at 11:43 AM 

      Hi Prasana,

      did you register the AutowireHelper bean in your application context? You can also set a break point in the setApplicationContext method and when this is called then the application context should be set.

      Thanks and regards,

      Guy

  • omblanco
    May 11, 2016 12:50 PM at 12:50 PM 

    Hi, the code works but to run the code I have a infinite loop between AutowireHelper.autowire and access to any repository, for example configurationRepository.findAll(). My code:
    public class AuditListener {
    @Inject
    private UserRepository userRepository;
    @Inject
    private ConfigurationRepository configurationRepository;
    @PreUpdate
    public void preUpdate(Object object) {
    //TODO: pre update
    AutowireHelper.autowire(this, this.userRepository, this.configurationRepository);
    //I need get bbdd entities
    List config = this.configurationRepository.findAll();
    Foo foo = (Foo) object;
    System.out.println(“Foo updated: ” + foo.getId());
    }
    }

    My error:
    Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
    12:43:25,291 INFO [stdout] (http-/0.0.0.0:8080-1) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    12:43:25,291 INFO [stdout] (http-/0.0.0.0:8080-1) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    12:43:25,291 INFO [stdout] (http-/0.0.0.0:8080-1) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    12:43:25,291 INFO [stdout] (http-/0.0.0.0:8080-1) at java.lang.reflect.Method.invoke(Method.java:601)
    12:43:25,291 INFO [stdout] (http-/0.0.0.0:8080-1) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:221)
    12:43:25,291 INFO [stdout] (http-/0.0.0.0:8080-1) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:137)
    12:43:25,291 INFO [stdout] (http-/0.0.0.0:8080-1) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
    12:43:25,291 INFO [stdout] (http-/0.0.0.0:8080-1) at org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver.doResolveHandlerMethodException(ExceptionHandlerExceptionResolver.java:362)|

    • guy
      May 17, 2016 7:21 AM at 7:21 AM 

      Hi,

      I think the loop comes from the this.configurationRepository.findAll() call you make as this again calls your AuditListener, but this is just a guess. Can you maybe remove this and try again? If it works then you need to find another solution without the Hibernate listeners and use a Spring based listener or something similar.

      I hope this helps.

      Thanks and regards,

      Guy

  • Aigloss
    June 16, 2016 5:21 PM at 5:21 PM 

    Hi,

    maybe it’s a little bit late, but why not doing somehing like:

    @Component
    public class AbstractEntityListener {

    private static DateTimeService dateTimeService;

    @PreUpdate
    public void preUpdate(AbstractEntity abstractEntity) {
    abstractEntity.setModificationDate(AbstractEntityListener.dateTimeService.getCurrentDate());
    }

    @PrePersist
    public void prePersist(AbstractEntity abstractEntity) {
    Date currentDate = AbstractEntityListener.dateTimeService.getCurrentDate();
    abstractEntity.setCreationDate(currentDate);
    abstractEntity.setModificationDate(currentDate);
    }

    @Autowired
    public void setDateTimeService(DateTimeService dateTimeService) {
    AbstractEntityListener.dateTimeService = dateTimeService;
    }
    }

    no helpers, no manual autowiring…just the entity and the listener. Of course, you’ll have an instance of AbstractEntityListener in your context which will probably never been used, but it’s stateless, so I think it’s worth it keeping the code simplier.

  • guy
    June 17, 2016 8:11 AM at 8:11 AM 

    Hi Aigloss,

    well the problem is that in the @EntityListener annotation you are just able to reference a class and not a Spring bean, so the class will be instantiated afterwards and then the autowiring does not work out of the box. That is why I created this AutowireHelper class. I know today there are other methods of doing this but at that time this was the only way I knew.

    Thanks for your comment and regards,

    Guy

  • Aigloss
    June 17, 2016 8:49 AM at 8:49 AM 

    Yes, you’re right… and you’re not 😉
    Just think about the timings: when you start you app, spring will create a bean of type AbstractEntityListener autowiring the dateTimeService of the listener by calling the setDateTimeService method. This method is assigning the bean injected by spring to a STATIC variable. So, any further instance of this class will have the bean available.
    This way, whenever JPA creates a new instance of the listener, the injected bean will be available through the static variable dateTimeService.

    • guy
      June 17, 2016 9:23 AM at 9:23 AM 

      I missed that you set the field as a static field. Well then it works as you said ;). Thanks for this new idea!

  • Ariff Md
    July 9, 2016 5:35 AM at 5:35 AM 

    Hi Aigloss,

    Your approach of using a Static variable in the listener and autowiring it with setter method looks good, but here we are writing to a static field with instance method…it will create issues in multi-threading env. Are there any solutions that work perfectly in multi task env? any thoughts?

    Regards
    Ariff Md,

  • Rasha Elsayed
    July 16, 2017 11:05 AM at 11:05 AM 

    Hi Aigloss
    your idea works, but it is still a workaround. Putting @Component on the AbstractEntityListener caused that it is created twice, once through spring and once through hibernate. The spring instance is not used afterwards, only the hibernate instance will be in use. But As the service is injected to a static variable, it will be found with either instances.

    Anyway I find creating unnecessary instance is not nice, although it will be cleaned up later by the GC

  • Leonardo Beal
    December 1, 2017 10:23 AM at 10:23 AM 

    or you can just use SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
    Although, both solutions don’t work when using jUnit as no context is defined.

  • Sandeep
    March 27, 2018 7:34 AM at 7:34 AM 

    This Worked for me.
    Thank you for the solution.

  • Veena
    April 27, 2018 11:19 AM at 11:19 AM 

    A good approach in solving this issue with such a simple idea. I had tried using Configurable and load time weaving with no luck . Thanks .

Leave a Reply

Time limit is exhausted. Please reload CAPTCHA.

This site uses Akismet to reduce spam. Learn how your comment data is processed.