Инъекция зависимостей JSR-303 и Hibernate - PullRequest
9 голосов
/ 26 апреля 2010

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

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

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

Ответы [ 6 ]

12 голосов
/ 25 мая 2011

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

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

7 голосов
/ 24 ноября 2010

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

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

По сути, что-то подобное требуется в контексте XML-приложения Spring:

<!-- 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 не требует пояснений.

4 голосов
/ 28 апреля 2010

Похоже, что в 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>

Затем используйте SpringValidatiorFactoryBean, как обычно. Затем вам придется вызывать валидаторы вручную, как в мире до JPA2.

UPDATE:

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

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

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

2 голосов
/ 01 сентября 2014

Для 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>
1 голос
/ 11 мая 2010

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

0 голосов
/ 18 февраля 2019

Вы можете сделать это, если используете 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 не внедряет Service

...