9

Spring 3.0.2, Hibernate 3.5.0, Hibernate-валидатора 4.0.2.GA-303 JSR инъекции зависимостей и Hibernate

Я пытаюсь внедрить зависимости Spring в ConstraintValidator с помощью:

@PersistenceContext 
private EntityManager entityManager; 

Я настроил контекст приложения с:

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

Который, согласно документации Spring, должны позволить «обычай ConstraintValidators, чтобы извлечь выгоду из инъекции зависимостей, как и любой другой весенний боб "

Внутри отладчика я вижу Spring, вызывающий getBean, для создания ConstraintValidator. Позже, когда flush запускает preInsert, создается и вызывается другой ConstraintValidator. Проблема заключается в том, что EntityManager имеет значение null в этом новом ConstraintValidator. Я попытался ввести другие зависимости в ConstraintValidator, и они всегда равны нулю.

Кто-нибудь знает, можно ли вводить зависимости в ConstraintValidator?

ответ

4

Похоже, что в JPA2, механизм проверки провайдера персистенции в кайф по умолчанию на предварительно сохраняются, предварительно обновлений и т.д.

Он использует свой собственный механизм для построения валидаторов, Spring не увлекаться.

Единственное решение, которое я могу видеть в данный момент отключить проверку JPA2 вне коробки в 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> 

Затем с помощью Spring, LocalValidatiorFactoryBean как обычно. Затем вам нужно будет вызвать валидаторы вручную, как в мире до JPA2.

UPDATE:

Просто, чтобы сделать его полным, другим решением было бы указать constraint-validator-factory в META-INF/validation.xml.

Было бы очень приятно, если бы это могло быть весной SpringConstraintValidatorFactory, но, к сожалению, для его передачи в свой конструктор требуется AutowireCapableBeanFactory, но класс с конструктором no-arg ожидается здесь JPA2.

Что оставляет возможность создания собственной реализации ConstraintValidatorFactory, которая вытаскивает валидаторы из Spring, но я не вижу никакого чистого способа сделать это.

+0

'<проверка режима> NONE' делает трюк. И вам даже не нужно вызывать валидаторы вручную. Spring проверяет валидатор на Hibernate Validator. С Hibernate Validator 4.1 У меня была проблема, что внезапно все сущности были проверены дважды. Один раз через весну (с автоподготовкой) и один раз через HV (неудачный, потому что не автоуведомленный). Он работал с Hibernate Validator 4.0. – Koraktor

+0

, а также установите для параметра "javax.persistence.validation.mode" значение "none". – Matt

1

Существует также возможность пройти ValidatorFactory как собственность к созданию управляющего объектом, используя свойство javax.persistence.validation.factory. Если есть экземпляр объекта ValidatorFactory, который хранится под этим свойством, диспетчер объектов использует эту фабрику валидатора вместо создания новой.

7

Хотя JSR-303 (Hibernate Validator является эталонной реализацией) создает экземпляр ConstraintValidator, он фактически делегирует фактическое создание в ValidatorFactory. Итак, вы правы, думая, что использование SpringValidatorFactoryBean Spring должно включить инъекцию зависимостей для ваших пользовательских валидаторов в вашем приложении.

Тонкость здесь заключается в том, что сам Hibernate использует отдельный файл ValidatorFactory, отличный от настроенного в контексте Spring, при обработке событий жизненного цикла объекта (предварительное обновление, предварительная установка, предварительное удаление). Это по дизайну, я думаю: Hibernate Validator не знает о Spring, поэтому мы должны сказать Hibernate использовать SpringValidatorFactoryBean Spring вместо создания его собственного.

В принципе, что-то вроде этого необходимо в 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 не требует пояснений.

+1

Не работает с Hibernate 4, Spring 3.1. Свойство Event Listeners не может быть установлено в новой версии. Я также пытаюсь использовать подход, основанный на интеграторах. http://stackoverflow.com/a/11672377/161628. Но он сообщает о зарегистрированной ошибке Duplicate Event Listener. –

12

Лучший способ внедрить Spring ValidativeFactory в свой EntityManager с помощью свойства 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"/> 

Наслаждайтесь!

+0

Возможно, этот вопрос интересный. Я не могу решить проблему в аналогичном сценарии. http://stackoverflow.com/questions/13599821/autowired-repository-is-null-in-custom-constraint-validator –

+0

Использование Spring 4 + Hibernate 4.3 + HV 5.1. Принял у меня удивительное количество времени, чтобы найти решение проблемы наличия нескольких ValidationFactories в запущенной системе. Это прекрасно работает. –

+0

@MartinFrey Здесь же! Просто найдите эту страницу после поиска решения, отлаживая спящий код. Это похоже на недокументированное «секретное» свойство ... – Beccari

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>