Skip to content
Advertisement

How to inject spring beans into the hibernate envers RevisionListener

I am using Spring 5.1 and Hibernate 5.3.9 including hibernate-envers. I don’t know how to inject spring beans into the hibernate envers custom RevisionListener.

I have tried

  (@Service or @Component)
  public class ExtendedRevisionListener implements RevisionListener {

  @Autowired
  private MyService myService;

  void newRevision(Object revisionEntity){
    myService.doSomething(...)
  }
}

Of course the class is included in the @ComponentScan packages resolution. A problem is that myService is not injected into the listener.

In the hibernate-envers documentation:

https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#envers-basics

As of Hibernate Envers 5.3, dependency injection is now supported for a RevisionListener. This feature is up to the various dependency frameworks, such as CDI and Spring, to supply the necessary implementation during Hibernate ORM bootstrap to support injection. If no qualifying implementation is supplied, the RevisionListener will be constructed without injection.

Unfortunatelly i haven’t found any working example.

@Naros I do a setup of the EntityMaganerFactoryBean in my spring persistence JPA configuration -> entityManager = new org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean()

10:49| DEBUG | SessionFactoryImpl.java 252 | Session factory constructed with filter configurations : {}
10:49| DEBUG | SessionFactoryImpl.java 253 | Instantiating session factory with properties: {hibernate.format_sql=true, awt.toolkit=sun.awt.windows.WToolkit, hibernate.id.new_generator_mappings=false, java.specification.version=1.8, logging.configuration=file:C:wildfly16standaloneconfigurationlogging.properties, sun.cpu.isalist=amd64, sun.jnu.encoding=Cp1250, sun.arch.data.model=64, org.jboss.resolver.warning=true, java.vendor.url=http://java.oracle.com/, javax.persistence.validation.mode=AUTO, sun.boot.library.path=C:Program FilesJavajdk1.8.0_201jrebin, org.jboss.logmanager.nocolor=true, sun.java.command=org.jboss.modules.Main -mp C:wildfly16modules org.jboss.as.standalone -b localhost --server-config=standalone.xml -Djboss.server.base.dir=C:wildfly16standalone, java.specification.vendor=Oracle Corporation, java.naming.factory.url.pkgs=org.jboss.as.naming.interfaces, java.home=C:Program FilesJavajdk1.8.0_201jre, jboss.server.persist.config=true, file.separator=, jboss.server.data.dir=C:wildfly16standalonedata, line.separator=
, java.vm.specification.vendor=Oracle Corporation, java.specification.name=Java Platform API Specification, jboss.server.base.dir=C:wildfly16standalone, hibernate.transaction.coordinator_class=class org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl, wicket.configuration=development, jboss.bind.address.management=localhost, sun.boot.class.path=C:Program FilesJavajdk1.8.0_201jrelibresources.jar;C:Program FilesJavajdk1.8.0_201jrelibrt.jar;C:Program FilesJavajdk1.8.0_201jrelibsunrsasign.jar;C:Program FilesJavajdk1.8.0_201jrelibjsse.jar;C:Program FilesJavajdk1.8.0_201jrelibjce.jar;C:Program FilesJavajdk1.8.0_201jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_201jrelibjfr.jar;C:Program FilesJavajdk1.8.0_201jreclasses, hibernate.hbm2ddl.auto=update, user.script=, java.protocol.handler.pkgs=org.jboss.net.protocol|org.jboss.vfs.protocol, sun.management.compiler=HotSpot 64-Bit Tiered Compilers, java.runtime.version=1.8.0_201-b09, user.name=ptaszek, hibernate.enable_lazy_load_no_trans=true, file.encoding=Cp1250, sun.rmi.dgc.client.gcInterval=3600000, java.io.tmpdir=C:UsersptaszekAppDataLocalTemp, org.jboss.boot.log.file=C:wildfly16standalonelogboot.log, jboss.modules.system.pkgs=org.jboss.byteman, java.version=1.8.0_201, java.vm.specification.name=Java Virtual Machine Specification, jboss.bind.address=localhost, java.awt.printerjob=sun.awt.windows.WPrinterJob, jboss.host.name=1501-10, org.jboss.security.context.ThreadLocal=true, sun.os.patch.level=, module.path=C:wildfly16modules, java.library.path=C:Program FilesJavajdk1.8.0_201bin;C:WINDOWSSunJavabin;C:WINDOWSsystem32;C:WINDOWS;native;C:/Program Files/Java/jre1.8.0_211/bin/server;C:/Program Files/Java/jre1.8.0_211/bin;C:/Program Files/Java/jre1.8.0_211/lib/amd64;C:Program Files (x86)Common FilesOracleJavajavapath;C:Program FilesMicrosoft MPIBin;C:Program Files (x86)InteliCLS Client;C:Program FilesInteliCLS Client;C:WINDOWSsystem32;C:WINDOWS;C:WINDOWSSystem32Wbem;C:WINDOWSSystem32WindowsPowerShellv1.0;C:Program FilesIntelIntel(R) Management Engine ComponentsDAL;C:Program FilesIntelIntel(R) Management Engine ComponentsIPT;C:Program Files (x86)IntelIntel(R) Management Engine ComponentsDAL;C:Program Files (x86)IntelIntel(R) Management Engine ComponentsIPT;C:WINDOWSSystem32OpenSSH;C:Program Files (x86)AOMEI Backupper;C:Program FilesTortoiseSVNbin;C:Program Files (x86)Microsoft SQL Server140ToolsBinn;C:Program FilesMicrosoft SQL Server140ToolsBinn;C:Program Files (x86)Microsoft SQL Server140DTSBinn;C:Program FilesMicrosoft SQL Server140DTSBinn;C:Program FilesMicrosoft SQL ServerClient SDKODBC130ToolsBinn;C:Program Files (x86)Microsoft SQL ServerClient SDKODBC130ToolsBinn;C:Program Files (x86)Microsoft SQL Server140ToolsBinnManagementStudio;C:Program FilesMySQLMySQL Shell 8.0bin;C:eclipse;;., jboss.server.name=1501-10, java.vendor=Oracle Corporation, jboss.modules.dir=C:wildfly16modules, sun.io.unicode.encoding=UnicodeLittle, jboss.server.temp.dir=C:wildfly16standalonetmp, sun.desktop=windows, file.encoding.pkg=sun.io, hibernate.connection.handling_mode=DELAYED_ACQUISITION_AND_HOLD, hibernate.dialect=org.hibernate.dialect.MySQL8Dialect, java.class.path=C:wildfly16jboss-modules.jar, jboss.server.deploy.dir=C:wildfly16standalonedatacontent, java.vm.vendor=Oracle Corporation, user.variant=, user.timezone=Europe/Belgrade, os.name=Windows 10, java.vm.specification.version=1.8, program.name=JBossTools: WildFly 16 at localhost, hibernate.generate_statistics=false, sun.java.launcher=SUN_STANDARD, user.country=PL, hibernate.use_sql_comments=false, javax.persistence.sharedCache.mode=UNSPECIFIED, jboss.server.config.dir=C:wildfly16standaloneconfiguration, sun.cpu.endian=little, user.home=C:Usersptaszek, user.language=pl, jboss.qualified.host.name=1501-10, java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment, java.awt.headless=true, org.apache.xml.security.ignoreLineBreaks=true, sun.rmi.dgc.server.gcInterval=3600000, java.net.preferIPv4Stack=true, jboss.home.dir=C:wildfly16, path.separator=;, os.version=10.0, java.endorsed.dirs=C:Program FilesJavajdk1.8.0_201jrelibendorsed, java.runtime.name=Java(TM) SE Runtime Environment, hibernate.ejb.persistenceUnitName=default, sun.nio.ch.bugLevel=, java.vm.name=Java HotSpot(TM) 64-Bit Server VM, hibernate.show_sql=false, java.security.auth.login.config=jar:file:/C:/wildfly16/modules/system/layers/base/org/picketbox/main/picketbox-5.0.3.Final.jar!/auth.conf, jboss.server.log.dir=C:wildfly16standalonelog, java.vendor.url.bug=http://bugreport.sun.com/bugreport/, user.dir=C:wildfly16bin, os.arch=amd64, org.hibernate.envers.audit_strategy=org.hibernate.envers.strategy.ValidityAuditStrategy, javax.management.builder.initial=org.jboss.as.jmx.PluggableMBeanServerBuilder, hibernate.boot.CfgXmlAccessService.key=org.hibernate.boot.cfgxml.spi.LoadedConfig@35a3f1ae, java.util.logging.manager=org.jboss.logmanager.LogManager, java.vm.info=mixed mode, java.vm.version=25.201-b09, hibernate.bytecode.use_reflection_optimizer=false, hibernate.connection.datasource=HikariDataSource (HikariCpConnectionPool), java.ext.dirs=C:Program FilesJavajdk1.8.0_201jrelibext;C:WINDOWSSunJavalibext, jboss.node.name=1501-10, java.class.version=52.0}

In my PersistenceJPAConfig:

@EnableJpaRepositories(basePackages = "pl.atmoterm", entityManagerFactoryRef = "localContainerEntityManagerFactoryBean")

  @Bean
  @DependsOn({"dataSource"})
  public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean() {
     LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
     em.setDataSource(dataSource());
     em.setPackagesToScan(new String[]{
        "pl.atmoterm.**.*"
     });

     em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
     em.setJpaProperties(additionalProperties());
     return em;
  }

Answer

There really is not much involved in setting up bean injection with Spring Framework 5.1 and Envers. The listener implementation does not require being annotated by @Service nor @Component because these classes are actually constructed by Hibernate and then the ManagedBeanRegistry is responsible for coordinating with whatever DI framework is being used to inject/wire dependencies.

If your MyService is not being injected into the revision listener then there are few things to check:

  1. Verify that you’re using Spring Framework 5.1 or later.
    While other spring components may use differing version schemes, its important to make sure that the underlying Spring Framework version is indeed 5.1+.
  2. Is the RevisionListener configured correctly?
    I typically configure this by adding a @RevisionEntity annotated class with the @Entity annotation and then specify the listener in the @RevisionEntity annotation’s value attribute. If you’d rather, you should be able to use org.hibernate.envers.revision_listener to specifying the fully qualified class name of the listener class otherwise.
  3. Is your MyService injectable in any other spring bean?
    Perhaps the issue here is that Spring simply isn’t constructing your MyService bean in the first place, which would lead to the dependency injection not supplying an implementation during bean injection of the revision listener.

I have created a small demo here that you can reference. I will look into publishing a blog post about this on the Hibernate blog later in the month.


UPDATE

Just looking at the Spring Framework source briefly, I believe the issue is that you’re manually creating a LocalContainerEntityManagerFactoryBean, which when the bean factory gets injected, does not actually create the necessary Hibernate configuration for dependency injection to happen.

What I think you may need to do is manually set this yourself:

@Bean
@DependsOn({"dataSource"})
public LocalContainerEntityManagerFactoryBean localContainerEntityManagerFactoryBean(ConfigurableListableBeanFactory beanFactory) {
   // Create the LocalContainerEntityManagerBean like you were

   // Pass beanFactory here so right configuration gets applied
   em.setJpaProperties(additionalProperties(beanFactory));
   return em;
}

private Properties additionalProperties(ConfigurableListableBeanFactory beanFactory) {
  // add your properties here like you were before
  Properties properties = new Properties();

  // THIS HERE IS THE CRITICAL SETTING
  properties.put(
    "hibernate.resource.beans.container", 
     new SpringBeanContainer(beanFactory));

  return properties
}

While the LocalContainerEntityManagerFactoryBean is BeanFactoryAware, it simply sets an internal property and that’s it. That property does not actually get set as the necessary configuration property for Hibernate to detect it, therefore Hibernate ends up defaulting to CDI injection since you’re in a CDI environment.

In the above, as the EntityManagerFactory bean is being constructed, we have the configuration pass in the BeanFactory instance. We pass that factory down into additionalProperties where we manually apply it to the right Hibernate configuration and that gets passed to the Hibernate bootstrap.

Advertisement