【问题标题】:How to inject spring beans into the hibernate envers RevisionListener如何将spring bean注入休眠环境RevisionListener
【发布时间】:2020-01-14 01:49:47
【问题描述】:

我正在使用 Spring 5.1 和 Hibernate 5.3.9,包括 hibernate-envers。 我不知道如何将spring bean注入hibernate envers自定义RevisionListener。

我试过了

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

  @Autowired
  private MyService myService;

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

当然,该类包含在@ComponentScan 包解析中。 一个问题是 myService 没有注入到监听器中。

在 hibernate-envers 文档中:

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

从 Hibernate Envers 5.3 开始,RevisionListener 现在支持依赖注入。 此功能取决于各种依赖框架,例如 CDI 和 Spring,在 Hibernate ORM 引导期间提供必要的实现以支持注入。如果没有提供合格的实现,RevisionListener 将在没有注入的情况下构造。

不幸的是,我还没有找到任何可行的示例。

@纳罗斯 我在我的 spring 持久性 JPA 中设置了 EntityMaganerFactoryBean 配置-> entityManager = 新的 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}

在我的 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;
  }

【问题讨论】:

    标签: java hibernate spring-data-jpa hibernate-envers


    【解决方案1】:

    使用 Spring Framework 5.1 和 Envers 设置 bean 注入实际上并没有太多涉及。侦听器实现不需要由@Service@Component 注释,因为这些类实际上是由Hibernate 构造的,然后ManagedBeanRegistry 负责与用于注入/连接依赖项的任何DI 框架进行协调。

    如果您的MyService 没有被注入到修订监听器中,那么需要检查的东西很少:

    1. 确认您使用的是 Spring Framework 5.1 或更高版本。
      虽然其他 Spring 组件可能使用不同的版本方案,但确保基础 Spring Framework 版本确实是 5.1+ 很重要.
    2. RevisionListener 配置正确吗?
      我通常通过添加带有@Entity 注释的@RevisionEntity 注释类来配置它,然后在@RevisionEntity 注释的值属性中指定侦听器。如果您愿意,您应该可以使用org.hibernate.envers.revision_listener 来指定侦听器类的完全限定类名。
    3. 您的 MyService 是否可以在任何其他 Spring bean 中注入?
      也许这里的问题是 Spring 根本没有构建您的 MyService bean,这将导致依赖注入不提供在修订监听器的 bean 注入期间实现。

    我创建了一个小演示here,您可以参考。我将考虑在本月晚些时候在 Hibernate 博客上发布一篇关于此的博文。


    更新

    简单地看一下 Spring Framework 源代码,我认为问题在于您手动创建了一个 LocalContainerEntityManagerFactoryBean,当注入 bean 工厂时,它实际上并没有为依赖注入创建必要的 Hibernate 配置。

    我认为您可能需要自己手动设置:

    @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
    }
    

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

    在上面,在构造 EntityManagerFactory bean 时,我们在 BeanFactory 实例中进行了配置传递。我们将该工厂传递给additionalProperties,在那里我们手动将其应用于正确的Hibernate 配置,然后传递给Hibernate 引导程序。

    【讨论】:

    • 感谢您创建演示项目。我检查了你的所有三点,一切看起来都很好,但它仍然无法正常工作。我所有的“spring”库都在 5.1.7.RELEASE 版本中,除了 spring-data-jpa 1.11.3 和 spring-data-commons 1.13.3。我的修订实体有一个注释 @RevisionEntity(ExtendedRevisionListener.class) 并且该服务可在其他 spring bean 中注入,我的库中的 Hibernate 库:hibernate-envers-5.3.9.Final,hibernate-core-5.3.9.Final, hibernate-commons-annotations-5.0.4.Final,hibernate-jpa-2.1-api-10.2.Final。我不知道有什么问题:(
    • Spring Data 有一个spring-data-envers 库,你没有使用它吗?你能不能把一个像我这样的简单例子放在一起,它不起作用并与我分享,让我看看?
    • 我不使用 spring-data-envers 库。举个例子可能会有问题,因为它是一个更大的项目,在 wildFly 16 上运行检票口。
    • 您是否在 Spring 配置中设置了 EntityManagerFactoryBeanSessionFactoryBean?我很好奇这是否与在 WF 和非 WF 下运行的 JPA 与 ORM5 有关,以及 Spring 如何与 ORM 5.3 中的 BeanContainer 功能集成。
    • 那么您能否为org.hibernate.internal.SessionFactoryImpl 启用调试日志记录并在引导过程中检查您是否看到一个显示Instantiating session factory with properties 的条目并验证hibernate.resource.beans.container 是否存在并且它指向SpringBeanContainer ?
    【解决方案2】:

    如果你使用的是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
        }
    
    }
    

    这是使用 Spring Boot 2.3.9 测试的。

    【讨论】:

      【解决方案3】:

      还有其他方法……你可以使用spring中的静态类……

      检查....

      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());
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-05-11
        • 2012-01-21
        • 2013-06-25
        • 1970-01-01
        • 2011-05-01
        • 1970-01-01
        相关资源
        最近更新 更多