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:
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; }
Advertisement
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:
- 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+. - 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 useorg.hibernate.envers.revision_listener
to specifying the fully qualified class name of the listener class otherwise. - Is your
MyService
injectable in any other spring bean?
Perhaps the issue here is that Spring simply isn’t constructing yourMyService
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.