Warum unterschiedliche Persistenzeinheiten mit getrennten Datenquellen die gleiche Datenquelle abfragen?
Ich bin die Entwicklung einer webapp, die benötigt Zugriff auf zwei verschiedene Datenbank-Server (H2 und Oracle).
Der container ist eine Apache Tomee 1.5.1 und ich bin mit dem Java EE stack mit Bibliotheken (JSF, JPA, CDI, EJB, etc.).
Ich versuche, zwei entity-Manager innerhalb einer XA-Transaktion zum extrahieren von Daten aus der Oracle Datenbank und speichern Sie es in der H2 nach der Umwandlung, ABER alle Abfragen ausgeführt werden, die H2-Datenbank egal den entity-manager, die ich benutze. Keine Hilfe?
BEARBEITEN: ich fand, dass wenn ich versuche, Zugriff auf den entity-Manager in der umgekehrten Reihenfolge, Sie Verhalten ist das gleiche, aber den Zugriff auf Oracle. I. e.: der entity-Manager bleiben mit der ersten Datenbank zugegriffen.
EJB-wo dies geschieht (Aufruf service.getFoo()
von JSF):
@Named
@Stateless
public class Service {
@Inject
@OracleDatabase
private EntityManager emOracle;
@Inject
@H2Database
private EntityManager emH2;
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public List<Foo> getFoo() {
TypedQuery<Foo> q = emH2.createQuery(
"SELECT x FROM Foo f", Foo.class);
List<Foo> l = q.getResultList();
if (l == null || l.isEmpty()) {
update();
}
return q.getResultList();
}
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void update() {
//FAIL: This query executes against H2 with Oracle entity manager!
List<Object[]> l = emOracle.createNativeQuery("SELECT * FROM bar ").getResultList();
//more stuff...
}
}
Die Ressource Produzent (CDI) für den entity-Manager (wo @H2Database und @OracleDatabase sind Qualifier):
public class Resources {
@Produces
@PersistenceContext(unitName = "OraclePU")
@OracleDatabase
private EntityManager emOracle;
@Produces
@PersistenceContext(unitName = "H2PU")
@H2Database
private EntityManager emH2;
}
Meine peristence.xml sieht wie folgt aus:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="H2PU"
transaction-type="JTA">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<jta-data-source>H2DS</jta-data-source>
<class>my.app.h2.Foo</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
<persistence-unit name="OraclePU" transaction-type="JTA">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
<jta-data-source>OracleDS</jta-data-source>
<class>my.app.oracle.Bar</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
</persistence>
Und schließlich die Daten-Quellen innerhalb tomee.xml (es gibt keine anderen Datenquellen konfiguriert, die in dieser Datei):
<Resource id="OracleDS" type="javax.sql.DataSource">
jdbcDriver = oracle.jdbc.xa.client.OracleXADataSource
jdbcUrl = jdbc:oracle:thin:@server:port:instance
jtaManaged = true
password = abcde
userName = user
</Resource>
<Resource id="H2DS" type="javax.sql.DataSource">
jdbcDriver=org.h2.jdbcx.JdbcDataSource
jdbcUrl=jdbc:h2:h2/db;AUTO_SERVER=TRUE
jtaManaged = true
password = edcba
userName = user
</Resource>
InformationsquelleAutor der Frage |
Du musst angemeldet sein, um einen Kommentar abzugeben.
Container Managed Persistence Contexts
Wenn mit container-managed Persistenz-Kontexte (wie Sie über @PersistenceContext-Annotationen), die JPA-Spezifikation legt fest, dass nur EIN persistenzkontext zugeordnet werden kann mit einer JTA-Transaktion.
Den Persistenz-Kontext wird erstellt, indem die Java-EE-container. Trotz Auftritten der code (@PersistenceContext-Annotationen scheinen zu behaupten, dass PC injiziert wird direkt in die EntityManager Instanz-Variablen), die Persistenz-Kontext wird gespeichert als Referenz INNERHALB DER JTA-TRANSAKTION. Jedes mal, wenn ein EntityManager Betrieb tritt es nicht auf eigene interne Persistenz-Kontext. Stattdessen tut es eine spezielle operation, weil es container-managed - es sieht immer up der persistence context in die JTA-Transaktion und verwendet. Dies wird als JTA-Persistenz Kontext-propagation.
Einige Zitate aus der JPA Spezifikation:
So ist das dein problem.
Die offensichtliche $64-Frage: WARUM funktioniert die Skillung Fragen???
Gut, es ist, weil es eine bewusste trade-off, das bringt leistungsstarke EntityManager Magie zu EJBs.
Verwendung von JTA-Transaktionen zu propagieren einer einzigen Persistenz-Kontext hat eine Einschränkung: Transaktionen können nicht überspannen mehrere Persistenz-Kontexte, so kann nicht überspannen mehrere Datenbanken.
Aber es hat auch einen unschätzbaren Vorteil: jeder entityManager erklärt in EJBs können automatisch teilen die gleichen persistence context und damit arbeiten können auf den gleichen Satz von JPA-Entitäten und nehmen Sie in der gleichen Transaktion. Sie können eine Kette von EJBs aufrufen von anderen EJBs von beliebiger Komplexität, und Sie alle Verhalten sich vernünftig und konsequent gegen die JPA-entity-Daten. Und Sie brauchen auch nicht die Komplexität der konsequent letzteres/sharing-entity manager-Bezüge über Methodenaufrufe - die EntityManagers erklärt werden kann privat in jeder Methode. Die Logik kann ganz einfach sein.
Die Antwort Auf Dein Problem: mit Application-Managed Persistence Contexts (per application-managed EntityManagers)
Erklären entityManager über einen dieser Ansätze:
ODER
Rufen Sie em.close (), wenn Sie fertig sind mit jedem EM - prefereably über ein final { } - Klausel oder über eine Java 7 try-with-resources statement.
Application-managed-EMs noch teilnehmen (d.h. synchronisiert mit) JTA-Transaktionen. Eine beliebige Anzahl von application-managed-EMs teilnehmen kann, in einem einzigen JTA Transaktion - aber keiner von beiden wird jemals Ihre Persistenz Kontext zugeordnet oder weitergegeben an irgendwelche container managed EM.
Wenn der EntityManager erstellt wird, die außerhalb des Kontexts einer JTA-Transaktion (bevor die Transaktion begonnen hat), dann müssen Sie Fragen es ausdrücklich beitreten die JTA-Transaktion:
Oder noch einfacher, wenn der EntityManager ist erstellt im Kontext einer JTA-Transaktion, dann die application-managed EntityManager automatisch verbindet die JTA-Transaktion implicity - keine joinTransaction() benötigt.
Also Application-managed-EMs kann eine JTA-Transaktion umfasst mehrere Datenbanken. Natürlich, Sie können immer ausführen einer lokalen Ressource JDBC Transaktion unabhängig von JTA:
EDIT: ZUSÄTZLICHE DETAILS für die Transaktion-Management mit Application-Managed Entity-Manager
WARNUNG: die folgenden Codebeispiele sind für den schulischen Einsatz - ich hab Sie eingetippt aus der Spitze von meinem Kopf zu erklären meine Punkte & hatte noch keine Zeit ihn zu kompilieren/Debuggen/testen.
Den Standard @TransactionManagement parameter für EJBs ist TransactionManagement.CONTAINER-und Standard - @TransactionAttribute parameter für EJB-Methoden ist TransactionAttribute.ERFORDERLICH.
Gibt es vier Permutationen für Transaktions-management:
A) EJB CONTAINER verwaltet JTA-Transaktionen
Dies ist die bevorzugte Java-EE-Ansatz.
EJB-Klasse @TransactionManagement Anmerkung:
eingestellt werden müssen, um TransactionManagement.CONTAINER explizit oder auslassen, es zu verwenden implizit den default-Wert.
EJB-Methode, die @TransactionAttribute annotation:
eingestellt werden müssen, um TransactionAttribute.ERFORDERLICH, ausdrücklich oder lassen Sie es zu implicity verwenden Sie den Standardwert. (Hinweis: wenn Sie noch eine andere business-Szenario, die Sie nutzen könnten TransactionAttribute.OBLIGATORISCHE oder TransactionAttribute.REQUIRES_NEW, wenn Ihre Semantik Ihre Bedürfnisse abgestimmt ist.)
Application-managed entity-Manager:
Sie müssen erstellt werden, über die Persistenz.createEntityManagerFactory("unitName") und emf.createEntityManager(), wie oben beschrieben.
Join the EntityManagers mit der JTA-Transaktion:
Erstellen des EntityManagers INNERHALB einer Transaktion EJB-Methode und Sie werden automatisch an die JTA-Transaktion. ODER wenn EntityManagers sind angelegt, Anruf-em.joinTransaction() innerhalb einer Transaktion EJB-Methode.
Rufen Sie EntityManager.close (), wenn Sie fertig sind mit Ihnen.
Das sollte alles sein, was erforderlich ist.
Basic-Beispiele - verwenden Sie einfach mehr EntityManagers für eine Transaktion über mehrere Datenbanken:
B) EJB mit BEAN-managed-JTA-Transaktionen
@TransactionManagement.BEAN.
Injizieren der JTA UserTransaction Schnittstelle, so dass die bean kann direkt daneben JTA-Transaktionen.
Manuell zu markieren/zu synchronisieren, die Transaktion über UserTransaction.begin()/commit()/rollback().
Sicherstellung der EntityManager schließt sich der JTA Transaktion - entweder erstellen Sie die EM in einer aktiven JTA-Transaktions-Kontext ODER rufen Sie em.joinTransaction().
Beispiele:
C) POJO/Nicht-EJB-mit hand-codiert (bean managed) - resource-local transactions (nicht JTA)
Verwenden Sie einfach die JPA EntityTransaction-Schnittstelle für tx-Abgrenzung (die über em.getTransaction()).
Beispiel:
D) POJO/Nicht-EJB-mit hand-codiert (POJO-managed) JTA-Transaktionen
Dieser übernimmt die POJO/Bauteil ausgeführt wird, in einige container, JTA unterstützen.
Wenn in einem Java EE-container, können Java EE-Ressource Injektion von JTA-UserTransaction-Schnittstelle.
(Alternativ kann explizit lookup ein handle für die JTA-Schnittstelle und Abgrenzung tun, dann rufen Sie em.getTransaction().joinTransaction() - siehe JTA-spec.)
Beispiel:
InformationsquelleAutor der Antwort
Versuchen, erstellen Sie eine Abfrage, nicht um eine native query erste, der Rückkehr eine Liste der Bars.
Versuchen Sie auch zu kommentieren H2-Injektion in Ihre EJB. Wenn es funktioniert, dann wissen Sie, dass es ein CDI-Konflikt-problem.
InformationsquelleAutor der Antwort javadev