Korrekte Art, eine EntityManager-Abfrage während der Hibernate-Validierung auszuführen

8

Ich bin ein wenig Java EE / EJB noob, aber aus den Dokumenten und anderen Posts, die ich gesammelt habe, können Sie die Datenbank nicht mit demselben Entitymanager / Session während der Entitätsvalidierung abfragen.

  

Im Allgemeinen sollte die Lebenszyklusmethode einer portablen Anwendung EntityManager nicht aufrufen   oder Abfragenoperationen, Zugriff auf andere Entitätsinstanzen oder Ändern von Beziehungen innerhalb der   gleicher Persistenzkontext. [43] Eine Lebenszyklus-Callback-Methode kann die Nicht-Beziehung ändern   Status der Entität, auf der sie aufgerufen wird.

Übersetzung bitte?

Das ist ziemlich abstrakt ... kann es genauer erklärt werden? Es führt zu mehr Fragen als es beantwortet. Wenn meine Entität beispielsweise über eine Lazy-Loaded-Sammlung verfügt, darf ich während der Überprüfung darauf zugreifen? Die Auflistung ist "eine andere Entität" und erfordert eine DB-Abfrage, die gegen die Dokumente zu verstoßen scheint.

Diese 'Lebenszyklusanforderung' scheint merkwürdig, da es nur eine Tatsache ist, dass bestimmte Validierungen tatsächlich eine Abfrage der Datenbank erfordern.

Von anderen Posts habe ich auch gesehen, wie Leute dieses Anfrageproblem umgehen, indem sie einen neuen Entitymanager / Session mit dem Entitymanagerfactory erstellen.

Dies führt mich zu zwei Fragen über die Verwendung von EntityManagers und Hibernate Validation:

  1. Ist es möglich, dass ich einen Konstruktionsfehler habe oder die Hibernate-Validierung missbrauche, weil ich die Datenbank während der Validierung abfragen muss?
  2. Da ich Java EE mit JBoss verwende, wie injiziere ich meinen Validator mit einer EntityManagerFactory?

Ich habe so etwas versucht:

%Vor%

Aber der EMF wird nie injiziert. Ich nehme an, dass das @Stateless-Tag irrelevant wird, weil ich eine ConstraintValidator-Schnittstelle implementiere, die für den Hibernate-Validator benötigt wird.

Wie lautet das allgemeine Muster, um von einem Validator zu einer EntityManagerFactory zu kommen?

Danke!

    
lostdorje 16.08.2013, 06:41
quelle

3 Antworten

6

Durch einige der Kommentare und genug herumstreicheln, fand ich schließlich eine etwas "kanonische" Art und Weise, meine Frage zu beantworten.

Aber um das klarzustellen, habe ich wirklich zwei Dinge gefragt, die zwei verschiedene Antworten haben:

  1. Wie injizieren Sie Dinge in Validatoren, die im Hibernate Validation Framework verwendet werden?
  2. Vorausgesetzt, wir können Dinge injizieren, ist es sicher, eine EntityManagerFactory oder einen EntityManager zu injizieren und sie für Abfragen während eines JPA-Lebenszyklusereignisses zu verwenden?

Wenn ich zuerst die zweite Frage beantworte, sage ich einfach: Es wird dringend empfohlen, einen zweiten EntityManager zu verwenden, um während der Validierung Abfragen auszuführen. Das bedeutet, dass Sie eine EntityManagerFactory injizieren und einen neuen EntityManager für Abfragen erstellen sollten (anstatt einen EntityManager zu injizieren, der derselbe sein wird, der das Lebenszyklusereignis erstellt hat).

Im Allgemeinen werden Sie zu Validierungszwecken nur die Datenbank abfragen und nicht einfügen / aktualisieren, daher sollte dies ziemlich sicher sein.

Ich habe eine sehr ähnliche SO-Frage hier .

Nun zur Beantwortung der ersten Frage.

Ja, es ist durchaus möglich, Dinge in Validatoren zu injizieren, die im Hibernate Validation Framework verwendet werden. Um dies zu erreichen, müssen Sie 3 Dinge tun:

  1. Erstellen Sie eine benutzerdefinierte ConstraintValidatorFactory, die die im Framework verwendeten Validatoren erstellt (überschreibt die Standardfactory von Hibernate). (Mein Beispiel verwendet Java EE, nicht Spring, daher verwende ich BeanManager, aber im Frühjahr würden Sie wahrscheinlich ApplicationContext dafür verwenden.)
  2. Erstellen Sie eine validation.xml-Datei, die dem Hibernate Validation-Framework mitteilt, welche Klasse für die ConstraintValidatorFactory verwendet werden soll. Stellen Sie sicher, dass diese Datei in Ihrem Klassenpfad endet.
  3. Schreiben Sie einen Validator, der etwas injiziert.

Hier sehen Sie ein Beispiel für eine benutzerdefinierte ConstraintValidatorFactory, die "verwaltete" (injizierbare) Validatoren verwendet:

%Vor%

Hier ist eine Beispieldatei validation.xml, die dem Hibernate Validator mitteilt, welche Klasse als ValidatorFactory verwendet werden soll:

%Vor%

Und schließlich eine Validator-Klasse mit Injektionspunkten:

%Vor%     
lostdorje 22.08.2013, 13:11
quelle
0

Ich glaube, ich habe den Wunsch, die Validierung mit der genialen Bean-Validierungs-API durchzuführen, aber denken Sie daran, dass dies nicht erforderlich ist.

Denken Sie darüber hinaus an diese beiden Anforderungen:

  1. Das Passwort darf nicht leer sein.
  2. Der Benutzer darf kein Passwort verwenden, das einem der vorherigen Passwörter entspricht.

Die erste hängt eindeutig nur vom Passwort selbst ab und ich würde es als Validierungsdaten klassifizieren und deshalb gehört diese Validierung in die Datenschicht.

Die zweite hängt von der Beziehung eines Datenelements zu einer Anzahl anderer Entitäten oder dem aktuellen Status des Systems ab. Ich würde dies als etwas, das in der Business-Schicht gehört, klassifizieren.

Statt zu versuchen, die Validierungsbeschränkungen für die Entitätsklasse festzulegen, müssen Sie sie auf eine Business-Schicht-Klasse setzen (ja, Sie können sogar die Bean-Validierung verwenden, wenn Sie dies jetzt wünschen).

Beispiel: Sie haben eine User -Entität mit dem aktuellen Kennwortfeld und eine Passwords -Entität, aus der Sie nach den alten Kennwörtern eines Benutzers fragen können. Machen Sie nun Ihr Benutzerdaten-Zugriffsobjekt:

%Vor%

Erstellen Sie Ihre Einschränkung auf Klassenebene:

%Vor%

Und Validator:

%Vor%

So etwas sollte es tun. Da die eigentliche Validierung jetzt von einem EJB durchgeführt wird, wird als Nebeneffekt die Validierungslogik selbst ausgeführt, wenn Sie die transnationalen Standardattribute beibehalten, dh.

    
rdcrng 16.08.2013 11:58
quelle
0
  

Übersetzung bitte?

Die Lebenszyklusereignisse sollten nicht den Entity Manager verwenden, da dies zu Regressionen führen könnte. Stellen Sie sich vor, dass Sie während eines Voraktualisierungsereignisses eine andere Entität ändern. Dies sollte ein weiteres Ereignis vor der Aktualisierung erzeugen, das das vorherige Ereignis vor der Aktualisierung enthält. Um solche Probleme zu vermeiden, wird von der Verwendung von Entity Manager abgeraten.

Aber wenn Sie nur einige zusätzliche Daten lesen möchten, gibt es konzeptionell kein Problem. Die Daten werden implizit in Ereignissen vor und nach dem Einfügen gespeichert.

Wenn Sie keine Post-Load-Ereignisse verwenden, sollte das Lesen von Daten in einem Lebenszyklusereignis keine geschachtelten Lebenszyklusereignisse auslösen. Soweit ich die Spezifikation verstehe, ist die Abfrage von Entitäten nicht streng verboten, wird jedoch dringend davon abgeraten. In diesem Fall könnte es in Ordnung sein. Hast du versucht, ob das funktioniert?

  

Was ist das allgemeine Muster, um zu einer EntityManagerFactory zu kommen?   von einem Validator?

Die Injektion funktioniert nur in verwalteten Einheiten. Wenn eine Injektion nicht möglich ist, sollten Sie in der Lage sein, eine gute alte Suche durchzuführen, um einen Entity Manager zu erhalten . Bei Verwendung des zweiten Entitätsmanagers werden jedoch möglicherweise geschachtelte Lebenszyklusereignisse generiert. Aber wenn Sie nur etwas Triviales tun, wie das Lesen einer alten Passwortliste, sollte das in Ordnung sein.

    
ewernli 21.08.2013 06:44
quelle