JSR-303依赖项注入和Hibernate

JSR-303依赖项注入和Hibernate,hibernate,spring,dependency-injection,validation,bean-validation,Hibernate,Spring,Dependency Injection,Validation,Bean Validation,Spring 3.0.2, Hibernate 3.5.0, Hibernate验证程序4.0.2.GA 我尝试使用以下方法将Spring依赖项注入ConstraintValidator: @PersistenceContext private EntityManager entityManager; 我已将应用程序上下文配置为: <bean id="validator" class="org.springframework.validation.beanvalidation.Local

Spring 3.0.2, Hibernate 3.5.0, Hibernate验证程序4.0.2.GA

我尝试使用以下方法将Spring依赖项注入ConstraintValidator:

@PersistenceContext
private EntityManager entityManager;
我已将应用程序上下文配置为:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

根据Spring文档,它应该允许“定制ConstraintValidator像其他Springbean一样从依赖注入中受益”

在调试器中,我可以看到Spring调用getBean来创建ConstraintValidator。稍后,当flush触发预插入时,将创建并调用另一个ConstraintValidator。问题是EntityManager在此新的ConstraintValidator中为空。我尝试在ConstraintValidator中注入其他依赖项,这些依赖项总是空的


有人知道是否可以将依赖项注入到ConstraintValidator中吗?

在JPA2中,持久性提供程序的验证机制在预持久化、预更新等默认情况下起作用

它使用自己的机制构造验证器,Spring不参与其中

目前我能看到的唯一解决方案是在persistence.xml中禁用现成的JPA2验证:

<persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
    <properties>
      <property name="hibernate.show_sql" value="true"/>
      <!-- other props -->
    </properties>
  <validation-mode>NONE</validation-mode>
</persistence-unit>

org.hibernate.ejb.HibernatePersistence
没有一个
然后像往常一样使用Spring的LocalValidatiorFactoryBean。然后,您必须像在JPA2之前的世界中那样手动调用验证器

更新:

为了使其完整,另一个解决方案是在META-INF/validation.xml中指定一个
约束验证器工厂

如果这可能是Spring的
SpringConstraintValidatorFactory
那就太好了,但不幸的是,它需要一个
AutowireCapableBeanFactory
来传递到它的构造函数中,但是JPA2在这里需要一个没有arg构造函数的类


这就留下了创建自己的
ConstraintValidatorFactory实现的选项,该实现将验证器从Spring中拉出来,但我看不到任何干净的方法。

还可以使用属性javax.persistence.validation.factory将验证器工厂作为属性传递给实体管理器创建。如果有一个ValidatorFactory实例存储在此属性下,实体管理器将使用此验证器工厂,而不是创建一个新的验证器工厂。

尽管JSR-303(Hibernate validator是参考实现)实例化了我们的自定义ConstraintValidator,但实际上它将实际创建委托给验证器工厂。 因此,您认为使用Spring的LocalValidatoryFactoryBean应该在整个应用程序中为您的自定义验证器启用依赖项注入是正确的

这里的微妙之处在于,Hibernate本身在处理实体生命周期事件(预更新、预插入、预删除)时使用了一个独立的验证器工厂,而不是在Spring上下文中配置的验证器工厂。我认为这是出于设计:Hibernate验证器不知道Spring,所以我们必须告诉Hibernate使用Spring的LocalValidatoryBean,而不是创建自己的

基本上,在Spring应用程序上下文XML中需要这样的内容:

<!-- The LocalValidatorFactoryBean is able to instantiate custom validators and inject autowired dependencies. -->
<bean id="validator"
      class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>

<!--
   This is the key to link Spring's injection to Hibernate event-based validation.
   Notice the first constructor argument, this is our Spring ValidatorFactory instance.
  -->
<bean id="beanValidationEventListener" class="org.hibernate.cfg.beanvalidation.BeanValidationEventListener">
    <constructor-arg ref="validator"/>
    <constructor-arg ref="hibernateProperties"/>
</bean>

<bean id="mySessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
    ...

    <!--
      A reference to our custom BeanValidationEventListener instance is pushed for all events.
      This can of course be customized if you only need a specific event to trigger validations.
    -->
    <property name="eventListeners">
        <map>
            <entry key="pre-update" value-ref="beanValidationEventListener"/>
            <entry key="pre-insert" value-ref="beanValidationEventListener"/>
            <entry key="pre-delete" value-ref="beanValidationEventListener"/>
        </map>
    </property>
</bean>

...

因为我已经花了一段时间来寻找如何实现它,所以我在这里组装了一个演示应用程序,自述文件是不言自明的。

在EntityManager中插入Spring上下文感知验证器工厂的最佳方法是使用javax.persistence.validation.factory属性。配置如下:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
 <property name="dataSource" ref="dataSource" />
 <property name="jpaVendorAdapter">
  <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
   <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" />              
  </bean>
 </property>
 <property name="jpaPropertyMap">
  <map>
   <entry key="javax.persistence.validation.factory" value-ref="validator" />               
  </map>
 </property>
</bean>

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>


享受吧

对于Spring 4.0.5和Hibernate 4.3.5,我能够用EL解决这个问题:

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
...
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
    ...
    <property name="hibernateProperties">
        <props>
            ...
            <prop key="javax.persistence.validation.factory">#{validator}</prop>
        </props>
    </property>
</bean>

...
...
...
#{validator}

如果您使用的是Spring Boot 2.1.0+,您可以这样做:

@Configuration
@Lazy
class SpringValidatorConfiguration {


    @Bean
    @Lazy
    public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(final Validator validator) {
        return new HibernatePropertiesCustomizer() {

            @Override
            public void customize(Map<String, Object> hibernateProperties) {
                hibernateProperties.put("javax.persistence.validation.factory", validator);
            }
        };
    }
}
@配置
@懒惰的
类SpringValidator配置{
@豆子
@懒惰的
公共HibernatePropertiesCustomizer HibernatePropertiesCustomizer(最终验证程序){
返回新的HibernatePropertiesCustomizer(){
@凌驾
公共void自定义(映射hibernateProperties){
hibernateProperties.put(“javax.persistence.validation.factory”,validator);
}
};
}
}
来自


而且

没有一个
能起作用。您甚至不必手动调用验证器。Spring将验证传递给Hibernate验证器。在HibernateValidator4.1中,我遇到了一个问题,突然所有实体都被验证了两次。直流弹簧(带自动接线)和直流高压(故障,因为未自动接线)。不过,它与HibernateValidator4.0配合使用,并将“javax.persistence.validation.mode”设置为“none”。与Hibernate4、Spring3.1不配合使用。无法在新版本中设置事件侦听器属性。我也在尝试一种基于积分器的方法。但它报告了一个重复的事件侦听器注册错误。您可能会发现这个问题很有趣。我无法解决类似情况下的问题。使用Spring4+Hibernate4.3+HV5.1。我花了很长时间才找到在运行系统中有多个ValidationFactory的问题的解决方案。这很好用。@MartinFrey这里也一样!通过调试hibernate代码找到解决方案后,只需访问此页面。这似乎是一个未记录的“秘密”财产。。。