9

Spring 3.0.2、Hibernate 3.5.0、Hibernate-Validator 4.0.2.GA

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

@PersistenceContext
private EntityManager entityManager;

我已经配置了应用程序上下文:

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

根据 Spring 文档,这应该允许“自定义 ConstraintValidators 像任何其他 Spring bean 一样从依赖注入中受益”</p>

在调试器中,我可以看到 Spring 调用 getBean 来创建 ConstraintValidator。稍后当刷新触发 preInsert 时,会创建并调用不同的 ConstraintValidator。问题是 EntityManager 在这个新的 ConstraintValidator 中为空。我尝试在 ConstraintValidator 中注入其他依赖项,这些依赖项始终为空。

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

4

6 回答 6

12

The best way to inject a Spring context aware ValidatorFactory inside your EntityManager is by using the javax.persistence.validation.factory property. Configuration goes as follows:

<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"/>

Enjoy!

于 2011-05-25T14:23:04.110 回答
7

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

这里的微妙之处在于 Hibernate 本身在处理实体生命周期事件(更新前、插入前、删除前)时使用了一个单独的 ValidatorFactory,而不是在 Spring 上下文中配置的那个。我认为这是设计使然:Hibernate Validator 不知道 Spring,因此我们必须告诉 Hibernate 使用 Spring 的 LocalValidatorFactoryBean 而不是创建自己的。

基本上,在您的 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>

由于我一直在努力寻找如何做到这一点,我在 Github 上整理了一个演示应用程序README 文件是不言自明的。

于 2010-11-24T04:28:07.453 回答
4

似乎在 JPA2 中,持久性提供程序的验证机制默认在 pre-persist、pre-update 等时启动。

它使用自己的机制来构造验证器,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>

然后像往常一样使用 Spring 的 LocalValidatiorFactoryBean 。然后,您必须像在 JPA2 之前的世界中那样手动调用验证器。

更新:

为了使它完整,另一种解决方案是constraint-validator-factory在 META-INF/validation.xml 中指定一个。

如果这可能是 Spring 的,那就太好SpringConstraintValidatorFactory了,但不幸的是,它需要将 anAutowireCapableBeanFactory传递给它的构造函数,但是 JPA2 在这里期望一个没有参数构造函数的类。

这留下了创建自己的实现的选项ConstraintValidatorFactory,将验证器从 Spring 中拉出,但我没有看到任何干净的方式来做到这一点。

于 2010-04-28T11:00:45.193 回答
2

对于 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>
于 2014-09-01T13:17:49.803 回答
1

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

于 2010-05-11T15:48:39.500 回答
1

如果您使用的是 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);
            }
        };
    }
}

Spring Boot 2.0.0 M6的想法 - 添加 Hibernate 拦截器

Spring Boot - Hibernate 自定义约束不注入服务

于 2019-02-18T12:43:45.697 回答