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一样受益于依赖注入”
在调试器中我可以看到Spring调用getBean来创建ConstraintValidator。稍后当flush触发preInsert时,会创建并调用另一个ConstraintValidator。问题是EntityManager在这个新的ConstraintValidator中是null。我尝试在ConstraintValidator中注入其他依赖项,这些依赖项始终为null。
有谁知道是否可以将依赖项注入ConstraintValidator?
答案 0 :(得分:12)
在EntityManager中注入Spring上下文感知ValidatorFactory的最佳方法是使用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"/>
享受!
答案 1 :(得分: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>
由于我已经挣扎了一段时间才知道该怎么做,我在这里整理了一个演示应用程序on Github README文件不言自明。
答案 2 :(得分:4)
似乎在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>
然后像往常一样使用Spring的LocalValidatiorFactoryBean。然后,您必须像在JPA2之前的世界那样手动调用验证器。
<强>更新强>
为了使其完整,另一种解决方案是在META-INF / validation.xml中指定constraint-validator-factory
。
如果这可能是Spring的SpringConstraintValidatorFactory
,那将是非常好的,但不幸的是,它需要将AutowireCapableBeanFactory
传递给它的构造函数,但是JPA2在这里需要一个带有no-arg构造函数的类。
这样就可以选择创建自己的ConstraintValidatorFactory
实现,从而将验证器从Spring中拉出来,但我没有看到任何干净的方法。
答案 3 :(得分: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>
答案 4 :(得分:1)
还可以选择使用属性 javax.persistence.validation.factory 将 ValidatorFactory 作为属性传递给实体管理器创建。如果在此属性下存储了 ValidatorFactory 实例,则实体管理器将使用此验证器工厂,而不是创建新工厂。
答案 5 :(得分:0)
如果您使用的是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 - Add Hibernate Interceptor的想法
和Spring Boot - Hibernate custom constraint doesn't inject Service