9

primavera 3.0.2, 3.5.0 Hibernate, Hibernate Validator-4.0.2.GAJSR-303 de inyección de dependencias e Hibernate

Estoy tratando de inyectar dependencias de la primavera en un ConstraintValidator usando:

@PersistenceContext 
private EntityManager entityManager; 

he configurado el contexto de aplicación con:

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

que, según la documentación de la primavera, debería permitir “cu Los Validadores de constricción de stom se benefician de la inyección de dependencia como cualquier otro Spring Bean "

Dentro del depurador puedo ver a Spring llamando a getBean para crear el ConstraintValidator. Más tarde, cuando el enjuague desencadena el preInsert, se crea y llama un ConstraintValidator diferente. El problema es que EntityManager es nulo dentro de este nuevo ConstraintValidator. Intenté inyectar otras dependencias dentro de ConstraintValidator y estas siempre son nulas.

¿Alguien sabe si es posible inyectar dependencias en un ConstraintValidator?

Respuesta

4

Parece que en JPA2, mecanismo de validación del proveedor de persistencia se activa de forma predeterminada en pre-persistir, antes de la actualización, etc.

Utiliza su propio mecanismo para la construcción de validadores, la primavera no se involucra.

La única solución que puedo ver en este momento es deshabilitar la validación JPA2 fuera-de-la-caja en persistence.xml:

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

A continuación, utilice LocalValidatiorFactoryBean de primavera como de costumbre. Luego tendrá que llamar a los validadores manualmente como en el mundo anterior a JPA2.

ACTUALIZACIÓN:

sólo para que sea completa, otra solución sería especificar un constraint-validator-factory en META-INF/validation.xml.

Sería muy bueno si este pudiera ser el SpringConstraintValidatorFactory de Spring, pero desafortunadamente requiere un AutowireCapableBeanFactory para pasar a su constructor, pero aquí se espera una clase con el constructor no-arg por JPA2.

Lo cual deja la opción de crear una implementación propia de ConstraintValidatorFactory que saca los validadores de Spring, pero no veo ninguna manera limpia de hacerlo.

+0

' Ninguno' hace el truco. Y ni siquiera tiene que llamar validadores manualmente. Spring pasa la validación a Hibernate Validator. Con Hibernate Validator 4.1 tuve el problema de que de repente todas las entidades se validaron dos veces. Una vez en primavera (con autoencendido) y una vez en HV (falla, porque no está autoconectada). Sin embargo, funcionó con Hibernate Validator 4.0. – Koraktor

+0

, así como establecer "javax.persistence.validation.mode" en "none". – Matt

1

También existe la opción de pasar el ValidatorFactory como propiedad a la creación gestor de la entidad mediante la propiedad javax.persistence.validation.factory. Si hay una instancia ValidatorFactory almacenada en esta propiedad, el administrador de entidades usa esta fábrica de validadores en lugar de crear una nueva.

7

Aunque JSR-303 (el Validador de Hibernate es la implementación de referencia) ejemplifica nuestro ConstraintValidator personalizado, de hecho delega la creación real en un ValidatorFactory. Así que tienen razón para pensar que el uso de LocalValidatorFactoryBean de Primavera debería permitir la inyección de dependencias para sus validadores personalizados a lo largo de su aplicación.

La sutileza aquí radica en el hecho de que Hibernate usa una ValidatorFactory distinta a la configurada en su contexto Spring al manejar los eventos del ciclo de vida de la entidad (pre-actualización, pre-inserción, pre-eliminación). Esto es por diseño que pienso: Hibernate Validator no tiene conocimiento de la primavera, por lo que debe decirle a Hibernate a utilizar LocalValidatorFactoryBean de primavera en lugar de crear su propio.

Básicamente, se requiere algo como esto en el contexto de aplicación de Primavera 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> 

Desde que he luchado por un tiempo para averiguar cómo hacerlo, he elaborado una aplicación de demostración aquí on Github El archivo README es autoexplicativo.

+1

no funciona con Hibernate 4, Spring 3.1. La propiedad Event Listeners no se puede configurar en la nueva versión. Estoy probando un enfoque basado en Integradores también. http://stackoverflow.com/a/11672377/161628. Pero informa un error registrado de Escucha de evento duplicado. –

12

La mejor manera de inyectar un contexto primavera conscientes ValidatorFactory dentro de su EntityManager es mediante el uso de la propiedad javax.persistence.validation.factory. La configuración es la siguiente:

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

¡Disfrútalo!

+0

Puede encontrar esta pregunta interesante. No puedo resolver el problema en un escenario similar. http://stackoverflow.com/questions/13599821/autowired-repository-is-null-in-custom-constraint-validator –

+0

Usando Spring 4 + Hibernate 4.3 + HV 5,1. Me llevó una cantidad increíble de tiempo encontrar la solución al problema de tener múltiples ValidationFactories en el sistema en ejecución. Esto funciona bien –

+0

@MartinFrey ¡Lo mismo aquí! Simplemente acceda a esta página después de encontrar la solución depurando el código de hibernación. Esto parece una propiedad "secreta" no documentada ... – Beccari

2

para la primavera 4.0.5 y 4.3.5 Hibernate que era capaz de resolver este problema con 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> 
Cuestiones relacionadas