Einen Verweis auf EntityManager in Java EE-Anwendungen mit CDI abrufen
Ich bin mit Java EE 7. Ich würde gerne wissen, was ist der richtige Weg, um zu injizieren, eine JPA EntityManager
in eine application scoped CDI-bean. Sie können nicht nur injizieren Sie mit @PersistanceContext
Anmerkung, weil EntityManager
Instanzen sind nicht thread-sicher. Nehmen wir an, wir wollen, dass unsere EntityManager
erstellt zu Beginn jeder HTTP-request-Verarbeitung und-geschlossen, nachdem die HTTP-Anfrage bearbeitet wird. Zwei Optionen kommen mir in den Sinn:
1.
Erstellen eines request-scoped CDI-bean, die einen Verweis auf eine EntityManager
und dann injizieren Sie die Bohne in ein application-scoped CDI-bean.
import javax.enterprise.context.RequestScoped;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@RequestScoped
public class RequestScopedBean {
@PersistenceContext
private EntityManager entityManager;
public EntityManager getEntityManager() {
return entityManager;
}
}
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
@ApplicationScoped
public class ApplicationScopedBean {
@Inject
private RequestScopedBean requestScopedBean;
public void persistEntity(Object entity) {
requestScopedBean.getEntityManager().persist(entity);
}
}
In diesem Beispiel wurde ein EntityManager
werden erstellt, wenn der RequestScopedBean
erstellt wurde, und geschlossen, wenn der RequestScopedBean
zerstört wird. Jetzt konnte ich verschieben, die Injektion zu einer abstrakten Klasse, um es zu entfernen aus der ApplicationScopedBean
.
2.
Erstellen Sie einen Hersteller, der produziert Instanzen von EntityManager
, und dann Spritzen Sie die EntityManager
Instanz in einer Anwendung scoped CDI-bean.
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
public class EntityManagerProducer {
@PersistenceContext
@Produces
@RequestScoped
private EntityManager entityManager;
}
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.persistence.EntityManager;
@ApplicationScoped
public class ApplicationScopedBean {
@Inject
private EntityManager entityManager;
public void persistEntity(Object entity) {
entityManager.persist(entity);
}
}
In diesem Beispiel werden wir haben auch eine EntityManager
erstellt wird jeder HTTP-request, aber was schließen die EntityManager
? Wird es auch geschlossen wird, nachdem die HTTP-Anfrage bearbeitet wird? Ich weiß, dass die @PersistanceContext
annotation injiziert container-managed EntityManager
. Dies bedeutet, dass ein EntityManager
geschlossen werden, wenn ein client eine bean zerstört wird. Was ist eine client-bean in dieser situation? Ist es die ApplicationScopedBean
die nie zerstört werden, bis die Anwendung beendet, oder ist es die EntityManagerProducer
? Irgendwelche Tipps?
Ich weiß, ich könnte ein stateless EJB, statt von application-scoped bean und dann einfach Spritzen EntityManager
durch @PersistanceContext
Anmerkung, aber das ist nicht der Punkt 🙂
InformationsquelleAutor der Frage Flying Dumpling | 2013-10-17
Du musst angemeldet sein, um einen Kommentar abzugeben.
Du hast fast Recht mit deiner CDI Produzent. Einzige Sache ist, dass Sie verwenden sollten, eine producer-Methode anstelle eines Produzenten-Feld.
Wenn Sie mit Weld als CDI-container (GlassFish 4.1 und WildFly 8.2.0), dann ist dein Beispiel für die Kombination
@Produces
@PersistenceContext
und@RequestScoped
Hersteller Feld werfen sollte diese Ausnahme, die bei der Bereitstellung:Stellt sich heraus, dass der container nicht erforderlich, um Unterstützung andere Tragweite als @Abhängig davon, Wann die Verwendung eines producer-Feld lookup-Java EE-Ressourcen.
CDI 1.2, Abschnitt 3.7. Ressourcen:
Dieses Zitat wurde alles über producer-Felder. Mit einer producer-Methode zum nachschlagen einer Ressource ist völlig legitim:
Erste, der container zu instanziieren, der Produzent und einer container-managed entity manager-Referenz injiziert werden in die
em
Feld. Dann wird der container rufen Sie Ihren producer-Methode und wickeln, was er kehrt in einem request-scoped CDI-proxy. Diese CDI-proxy ist das, was Ihr client-code erhalten, wenn mit@Inject
. Da die producer-Klasse ist mit @Dependent (Standardeinstellung), werden die zugrunde liegenden container-managed entity manager-Referenz wird nicht geteilt durch andere CDI-proxies hergestellt. Jedes mal, wenn ein anderer Antrag will der entity-manager, die eine neue Instanz der Erzeuger-Klasse instanziiert werden und eine neue entity-manager-Referenz injiziert werden, in der Produzent und die wiederum ist eingewickelt in eine neue CDI-proxy.Technisch korrekt, die zugrunde liegenden und Unbenannte container, die Ressource-injection in das Feld
em
zulässig ist die Wiederverwendung von alten entity-Manager (siehe Fußnote in JPA 2.1-Spezifikation, Abschnitt "7.9.1 Container Zuständigkeiten", Seite 357). Aber so weit, wir Ehren das Programmiermodell erforderlich PPV.In dem vorhergehenden Beispiel, wäre es nicht egal, wenn Sie daneben
EntityManagerProducer
@Abhängigen oder @RequestScoped. Mit @Dependent ist semantisch korrekt. Aber wenn Sie einen größeren Umfang hat als request-scope auf der producer-Klasse, die Sie der Gefahr aussetzen, das zugrunde liegende entity-manager-Verweis auf viele threads in denen wir beide wissen, ist nicht eine gute Sache zu tun. Das zugrunde liegende entity-manager-Implementierung ist wohl eine thread-lokale Objekt, sondern tragbare Anwendungen dürfen sich nicht auf Einzelheiten der Implementierung.CDI nicht wissen, wie Sie zu schließen, was auch immer Dinge, die es ist, dass Sie in den request-Kontext gebunden. Mehr als alles andere, ein container-managed entity-manager muss nicht geschlossen werden, die von Anwendungs-code.
JPA 2.1, Abschnitt "7.9.1 Container Verantwortlichkeiten":
Leider viele Menschen tun, verwenden Sie eine
@Disposes
- Methode, schließen Sie das container-managed entity-manager. Wer kann es Ihnen verdenken, wenn die offiziellen Java EE 7 tutorial von Oracle sowie die CDI-Spezifikation selbst einen Entsorger zu schließen, eine container-managed entity-manager. Das ist einfach falsch, und der Aufruf vonEntityManager.close()
werfen wirdIllegalStateException
egal, wo Sie das nennen, in einem Entsorger Methode oder irgendwo anders. Das Oracle-Beispiel ist der größte Sünder der beiden, indem er die producer-Klasse, um eine@javax.inject.Singleton
. Wie wir gelernt haben, dieser Gefahr aussetzen, das zugrunde liegende entity-manager-Verweis auf viele verschiedene threads.Es ist erwiesen,hierdass durch die Verwendung von CDI-Erzeuger und-Entsorger zu Unrecht, 1) die nicht thread-safe entity-manager kann zugespielt werden zu viele threads und 2) der Entsorger hat keine Wirkung, so dass der entity-manager öffnen. Was geschah, war die IllegalStateException, die die container geschluckt, ohne eine Spur von es (eine mysteriöse log-Eintrag vorgenommen, welcher sagt, es war ein "Fehler löschen einer Instanz").
Im Allgemeinen mit CDI-lookup-container-managed entity-Manager ist nicht eine gute Idee. Die Anwendung ist wahrscheinlich besser dran, nur mit
@PersistenceContext
und glücklich sein mit ihm. Aber es gibt immer Ausnahmen von der Regel wie in deinem Beispiel, und die CDI kann auch nützlich sein, zu abstrahieren derEntityManagerFactory
bei der Handhabung des Lebenszyklus von application-managed entity-Manager.Bekommen ein vollständiges Bild, wie Sie eine container-managed entity-manager und die Verwendung von CDI-lookup entity-Manager, möchten Sie vielleicht zu Lesen diese und diese.
InformationsquelleAutor der Antwort Martin Andersson
Verstehe ich dein problem. aber das ist nicht echt. Nicht Durcheinander kommen mit der CDI deklariert Rahmen einer übergeordneten Klasse, das wird übertragen der Umfang der Attribute zu erwarten, dass diejenigen, die die Verwendung von @Inject'ion!
Die @Inject ' ed berechnet werden Referenz in depencency der CDI-Erklärung die Umsetzung Klasse. So haben Sie Applicationscoped-Klasse mit einer @Inject EntityManager em drin, aber jeder controlflow findet seine eigene em-Transaktion Verweis auf eine disjount em-Objekt, da der EntityManager CDI Erklärung der Implementierung der Klasse hinter sich.
Die falsche Sache Ihres Codes ist, dass Sie eine innere getEntityManager() access-Methode. Nicht-pass-Injiziert-Objekt, wenn Sie einen brauchen, einfach die @Inject .
InformationsquelleAutor der Antwort Groovieman
Sollten Sie die
@Dispose
Anmerkung zu schließen, dieEntityManager
wie im Beispiel unten:InformationsquelleAutor der Antwort Claudio Miranda
Können Sie injizieren sicher EntityManagerFactory, es ist thread-save
dann können Sie ermittelt werden EntityManager von entityManagerFactory.
InformationsquelleAutor der Antwort Piotr Kochański