9

Frühling 3.0.2, Hibernate 3.5.0, Hibernate-Validator 4.0.2.GAJSR-303 Dependency Injection und Hibernate

Ich versuche Frühling Abhängigkeiten in eine ConstraintValidator zu injizieren mit:

@PersistenceContext 
private EntityManager entityManager; 

ich habe den Anwendungskontext konfiguriert mit:

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

, die nach der Frühlings-Dokumentation ermöglichen sollte „cu stom ConstraintValidators, um von der Abhängigkeitsinjektion wie jede andere Spring-Bean zu profitieren "

Im Debugger kann ich Spring aufrufen, der getBean aufruft, um den ConstraintValidator zu erstellen. Später, wenn flush den PreInsert auslöst, wird ein anderer ConstraintValidator erstellt und aufgerufen. Das Problem ist, dass der EntityManager in diesem neuen ConstraintValidator null ist. Ich habe versucht, andere Abhängigkeiten innerhalb des ConstraintValidator zu injizieren, und diese sind immer Null.

Weiß jemand, ob es möglich ist, Abhängigkeiten in einen ConstraintValidator zu injizieren?

Antwort

4

Es scheint, dass in JPA2, die Validierungsmechanismus der Persistenz-Provider schlägt standardmäßig vorge bestehen bleiben, vor Aktualisierung in etc.

Es verwendet einen eigenen Mechanismus Validatoren für die Konstruktion, Frühling nicht beteiligt bekommt.

Die einzige Lösung, die ich im Moment sehen kann, ist das Deaktivieren der out-of-the-box JPA2 Validierung in 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> 

Dann Spring LocalValidatiorFactoryBean wie gewohnt verwenden. Sie müssen dann die Validatoren wie in der Pre-JPA2-Welt manuell aufrufen.

UPDATE:

einfach es komplett zu machen, wäre eine andere Lösung, die eine constraint-validator-factory in META-INF/validation.xml angeben sein.

Es wäre sehr schön, wenn dies könnte Spring's SpringConstraintValidatorFactory sein, aber leider erfordert eine AutowireCapableBeanFactory, um in den Konstruktor übergeben werden, aber eine Klasse mit Nein-Arg-Konstruktor wird hier von JPA2 erwartet.

Das lässt die Option der Erstellung einer eigenen Implementierung von ConstraintValidatorFactory, die Validatoren aus Spring zieht, aber ich sehe keinen sauberen Weg, dies zu tun.

+0

' NONE' funktioniert der Trick. Und Sie müssen die Validatoren nicht einmal manuell aufrufen. Spring übergibt die Validierung an den Hibernate-Validator. Mit Hibernate Validator 4.1 hatte ich das Problem, dass plötzlich alle Entitäten zweimal validiert wurden. Einmal durch Spring (mit Autowinding) und einmal durch HV (ausgefallen, weil nicht autowired). Es funktionierte jedoch mit Hibernate Validator 4.0. – Koraktor

+0

sowie "javax.persistence.validation.mode" auf "none" setzen. – Matt

1

Es besteht auch die Möglichkeit, die ValidatorFactory als Eigentum an den EntityManager Schaffung passieren die Eigenschaft javax.persistence.validation.factory verwenden. Wenn unter dieser Eigenschaft eine ValidatorFactory Instanz gespeichert ist, verwendet der Entitätsmanager diese Validator Factory, anstatt eine neue zu erstellen.

7

Obwohl JSR-303 (Hibernate Validator als Referenzimplementierung) unseren benutzerdefinierten ConstraintValidator instanziiert, delegiert er tatsächlich die eigentliche Erstellung an eine ValidatorFactory. Sie sind also richtig zu denken, dass die Verwendung von Spring LocalValidatorFactoryBean ermöglichen Abhängigkeit Injektion für Ihre benutzerdefinierten Validatoren in Ihrer Anwendung aktivieren. Die Besonderheit liegt darin, dass Hibernate selbst bei der Verarbeitung von Entity-Lifecycle-Ereignissen (Pre-Update, Pre-Insert, Pre-Delete) eine andere ValidatorFactory als die im Spring-Kontext konfigurierte verwendet. Dies ist von Entwurf ich denke: Hibernate Validator ist Spring nicht bekannt, also müssen wir Hibernate mitteilen, Spring LocalValidatorFactoryBean zu verwenden, anstatt seine eigene zu erstellen.

Grundsätzlich, so etwas wie dies in Ihrem Spring-Anwendungskontext XML erforderlich:

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

Da ich für eine Weile zu kämpfen haben, herauszufinden, wie es zu tun, ich habe hier eine Demo-Anwendung zusammen on Github Die README-Datei ist selbsterklärend.

+1

funktioniert nicht mit Hibernate 4, Spring 3.1. Die Eigenschaft Event Listeners kann in der neuen Version nicht festgelegt werden. Ich versuche auch einen auf Integratoren basierenden Ansatz. http://stackoverflow.com/a/11672377/161628. Aber es meldet einen Duplicate Event Listener registrierten Fehler. –

12

Die beste Möglichkeit, eine Spring-Context-fähige ValidatorFactory innerhalb Ihres EntityManager zu injizieren, besteht in der Verwendung der Eigenschaft javax.persistence.validation.factory. Die Konfiguration läuft wie folgt ab:

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

Viel Spaß!

+0

Sie könnten diese Frage interessant finden. Ich bin nicht in der Lage, das Problem in einem ähnlichen Szenario zu lösen. http://stackoverflow.com/questions/13599821/autowired-repository-is-null-in-custom-constraint-validator –

+0

Verwenden von Spring 4 + Hibernate 4.3 + HV 5.1. Es hat mir unglaublich viel Zeit gekostet, die Lösung für das Problem zu finden, mehrere ValidationFactories im laufenden System zu haben. Das funktioniert gut. –

+0

@MartinFrey Das gleiche hier! Erreichen Sie diese Seite, nachdem Sie die Lösung gefunden haben, indem Sie den Hibernate-Code debuggen. Dies scheint eine undokumentierte "geheime" Eigenschaft zu sein ... – Beccari

2

für Frühling 4.0.5 und Hibernate 4.3.5 Ich war in der Lage, dieses Problem mit EL zu lösen:

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