简体   繁体   English

如何将 spring bean 注入休眠环境 RevisionListener

[英]How to inject spring beans into the hibernate envers RevisionListener

I am using Spring 5.1 and Hibernate 5.3.9 including hibernate-envers.我正在使用 Spring 5.1 和 Hibernate 5.3.9,包括 hibernate-envers。 I don't know how to inject spring beans into the hibernate envers custom RevisionListener.我不知道如何将 spring beans 注入 hibernate envers 自定义 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.当然,该类包含在@ComponentScan 包解析中。 A problem is that myService is not injected into the listener.一个问题是 myService 没有注入监听器。

In the hibernate-envers documentation:在 hibernate-envers 文档中:

https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#envers-basics 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.从 Hibernate Envers 5.3 开始,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.此功能取决于各种依赖框架,例如 CDI 和 Spring,在 Hibernate ORM 引导期间提供必要的实现以支持注入。 If no qualifying implementation is supplied, the RevisionListener will be constructed without injection.如果未提供符合条件的实现,则将在不注入的情况下构造 RevisionListener。

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() @Naros 我在 spring 持久性 JPA 配置中设置了 EntityMaganerFactoryBean -> 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:\wildfly16\standalone\configuration\logging.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 Files\Java\jdk1.8.0_201\jre\bin, org.jboss.logmanager.nocolor=true, sun.java.command=org.jboss.modules.Main -mp C:\wildfly16\modules org.jboss.as.standalone -b localhost --server-config=standalone.xml -Djboss.server.base.dir=C:\wildfly16\standalone, java.specification.vendor=Oracle Corporation, java.naming.factory.url.pkgs=org.jboss.as.naming.interfaces, java.home=C:\Program Files\Java\jdk1.8.0_201\jre, jboss.server.persist.config=true, file.separator=\, jboss.server.data.dir=C:\wildfly16\standalone\data, line.separator=
, java.vm.specification.vendor=Oracle Corporation, java.specification.name=Java Platform API Specification, jboss.server.base.dir=C:\wildfly16\standalone, 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 Files\Java\jdk1.8.0_201\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\rt.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\sunrsasign.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_201\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_201\jre\classes, 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:\Users\ptaszek\AppData\Local\Temp\, org.jboss.boot.log.file=C:\wildfly16\standalone\log\boot.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:\wildfly16\modules, java.library.path=C:\Program Files\Java\jdk1.8.0_201\bin;C:\WINDOWS\Sun\Java\bin;C:\WINDOWS\system32;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 Files\Oracle\Java\javapath;C:\Program Files\Microsoft MPI\Bin\;C:\Program Files (x86)\Intel\iCLS Client\;C:\Program Files\Intel\iCLS Client\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\Program Files\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\IPT;C:\WINDOWS\System32\OpenSSH\;C:\Program Files (x86)\AOMEI Backupper;C:\Program Files\TortoiseSVN\bin;C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\;C:\Program Files\Microsoft SQL Server\140\Tools\Binn\;C:\Program Files (x86)\Microsoft SQL Server\140\DTS\Binn\;C:\Program Files\Microsoft SQL Server\140\DTS\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\130\Tools\Binn\;C:\Program Files (x86)\Microsoft SQL Server\Client SDK\ODBC\130\Tools\Binn\;C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\;C:\Program Files\MySQL\MySQL Shell 8.0\bin\;C:\eclipse;;., jboss.server.name=1501-10, java.vendor=Oracle Corporation, jboss.modules.dir=C:\wildfly16\modules, sun.io.unicode.encoding=UnicodeLittle, jboss.server.temp.dir=C:\wildfly16\standalone\tmp, 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:\wildfly16\jboss-modules.jar, jboss.server.deploy.dir=C:\wildfly16\standalone\data\content, 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:\wildfly16\standalone\configuration, sun.cpu.endian=little, user.home=C:\Users\ptaszek, 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 Files\Java\jdk1.8.0_201\jre\lib\endorsed, 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:\wildfly16\standalone\log, java.vendor.url.bug=http://bugreport.sun.com/bugreport/, user.dir=C:\wildfly16\bin, 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 Files\Java\jdk1.8.0_201\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext, jboss.node.name=1501-10, java.class.version=52.0}

In my PersistenceJPAConfig:在我的 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;
  }

There really is not much involved in setting up bean injection with Spring Framework 5.1 and Envers.使用 Spring Framework 5.1 和 Envers 设置 bean 注入确实没有太多涉及。 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.侦听器实现不需要由@Service@Component进行注释,因为这些类实际上是由 Hibernate 构造的,然后ManagedBeanRegistry负责与用于注入/连接依赖项的任何 DI 框架进行协调。

If your MyService is not being injected into the revision listener then there are few things to check:如果您的MyService没有被注入修订侦听器,则需要检查的内容很少:

  1. Verify that you're using Spring Framework 5.1 or later.验证您使用的是Spring Framework 5.1或更高版本。
    While other spring components may use differing version schemes, its important to make sure that the underlying Spring Framework version is indeed 5.1+.虽然其他 spring 组件可能使用不同的版本方案,但确保底层 Spring Framework 版本确实是 5.1+ 很重要。
  2. Is the RevisionListener configured correctly? RevisionListener配置是否正确?
    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.我通常通过添加带有@Entity注释的@RevisionEntity注释类来配置它,然后在@RevisionEntity注释的 value 属性中指定侦听器。 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.如果您愿意,您应该能够使用org.hibernate.envers.revision_listener来指定侦听器类的完全限定类名。
  3. Is your MyService injectable in any other spring bean?您的MyService是否可以在任何其他 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.也许这里的问题是 Spring 根本没有首先构建您的MyService bean,这将导致依赖注入在修订侦听器的 bean 注入期间不提供实现。

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.我将考虑在本月晚些时候在 Hibernate 博客上发布一篇关于此的博客文章。


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.简单地看一下 Spring Framework 源代码,我相信问题在于您手动创建了一个LocalContainerEntityManagerFactoryBean ,当注入 bean 工厂时,它实际上并没有创建必要的 Hibernate 配置来进行依赖注入。

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.虽然LocalContainerEntityManagerFactoryBeanBeanFactoryAware ,但它只是设置了一个内部属性,仅此而已。 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.该属性实际上并未设置为 Hibernate 检测它的必要配置属性,因此 Hibernate 最终默认为 CDI 注入,因为您处于 CDI 环境中。

In the above, as the EntityManagerFactory bean is being constructed, we have the configuration pass in the BeanFactory instance.在上面,当 EntityManagerFactory bean 正在构建时,我们在BeanFactory实例中有配置传递。 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.我们将该工厂传递到additionalProperties ,在那里我们手动将其应用到正确的 Hibernate 配置,然后传递给 Hibernate 引导程序。

If you are using Spring Boot, it is best to use ApplicationContextAware:如果使用 Spring Boot,最好使用 ApplicationContextAware:

public class MyRevisionListener implements RevisionListener, ApplicationContextAware {

    private MyService myService;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.myService = applicationContext.getBean(MyService.class);
    }

    @Override
    public void newRevision(Object revisionEntity) {
        MyRevisionEntity revinfo = (MyRevisionEntity) revisionEntity;

        // Use myService here to set something on MyRevisionEntity
    }

}

This was tested using Spring Boot 2.3.9.这是使用 Spring Boot 2.3.9 测试的。

Have other way... you can use the static class from spring....有其他方式...您可以使用 spring 中的静态类....

Check....查看....

import org.hibernate.envers.RevisionListener;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;


public class EnverRevisionListener implements RevisionListener{

    @Override
    public void newRevision(Object revisionEntity) {
        EnverRevisionEntity revEntity = (EnverRevisionEntity) revisionEntity;
        SecurityContext context = SecurityContextHolder.getContext();
        Authentication auth = context.getAuthentication();
        revEntity.setUserName(auth.getName());
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM