Ruhezustand nativeQuery - Transaktion?

8

Wir haben einen Service, der @Statefull ist. Die meisten Datenoperationen sind atomar, aber innerhalb eines bestimmten Satzes von Funktionen wollen wir mehrere native queries innerhalb einer Transaktion ausführen.

Wir haben EntityManager mit einem kontextbezogenen Persistenzkontext injiziert. Bei der Erstellung eines "Bündels" normaler Entitäten funktioniert em.persist() alles gut.

Aber wenn native Abfragen verwendet werden (einige Tabellen werden nicht von @Entity dargestellt), führt Hibernate sie nicht innerhalb derselben Transaktion aus, sondern verwendet grundsätzlich EINE Transaktion pro Abfrage.

Also habe ich bereits versucht, die manuellen START TRANSACTION; und COMMIT; Einträge zu verwenden - aber das scheint die Transaktionen zu stören, die Hibernate verwendet, um Entitäten persistent zu machen, wenn native Abfragen und Persistenz-Aufrufe gemischt werden.

%Vor%

Alles innerhalb dieser Methode sollte innerhalb einer Transaktion geschehen. Ist das mit Hibernate möglich? Beim Debuggen wird deutlich, dass jede Anweisung "unabhängig" von einer Transaktion erfolgt. (D.h. Änderungen werden direkt nach jeder Anweisung in die Datenbank übertragen.)

Ich habe das unten angegebene Beispiel mit einer minimalen Konfiguration getestet, um alle anderen Faktoren des Problems zu eliminieren (Strings dienen nur dazu, die Datenbank nach jeder Abfrage auf Haltepunkte zu überprüfen):

%Vor%

Der Ruhezustand ist wie folgt konfiguriert:

%Vor%

Das Ergebnis ist das gleiche wie beim Autocommit-Modus: Nach jeder systemeigenen Abfrage wird die Datenbank (Überprüfung des Inhalts einer zweiten Verbindung) sofort aktualisiert.

Die Idee, die Transaktion manuell zu verwenden, führt zu demselben Ergebnis:

%Vor%     
dognose 09.09.2014, 13:55
quelle

3 Antworten

3

Falls Sie keine containergesteuerten Transaktionen verwenden, brauchen Sie um auch die Transaktionsrichtlinie hinzuzufügen:

@Statusvoll    @TransactionManagement (Wert = TransactionManagementType.CONTAINER)    @TransactionAttribute (Wert = REQUIRED)

Ich habe dieses Phänomen nur in zwei Situationen gesehen:

  • Die DataSource wird im Auto-Commit-Modus ausgeführt, daher werden alle Anweisungen in einer separaten Transaktion
  • ausgeführt
  • Der EntityManager wurde nicht mit @Transactional konfiguriert, aber dann können nur Abfragen ausgeführt werden, da jede DML-Operation dazu führen würde, dass eine Ausnahme für Transaktionen angefordert wird.

Lassen Sie uns wiederholen, dass Sie die folgenden Hibernate-Eigenschaften festgelegt haben:

%Vor%

Die endgültige Eigenschaft muss mit Ihrem Application Server UserTransaction JNDI-Namensschlüssel festgelegt werden.

Sie könnten auch das:

verwenden %Vor%

oder eine andere Strategie gemäß Ihrem aktuellen Anwendungsserver.

    
Vlad Mihalcea 14.09.2014 05:43
quelle
0

Nachdem ich einige weitere Stunden über das Thema gelesen habe, während ich mit jeder Konfigurationseigenschaft und / oder Annotation herumspielte, konnte ich eine funktionierende Lösung für meinen Anwendungsfall finden. Es ist vielleicht nicht die beste oder einzige Lösung, aber da die Frage einige Lesezeichen und Upvotes erhalten hat, würde ich gerne teilen, was ich bisher habe:

Zunächst gab es keine Möglichkeit, die Funktion wie erwartet auszuführen, wenn die Persistenzeinheit im verwalteten Modus ausgeführt wurde. ( <persistence-unit name="test" transaction-type="JTA"> - JTA ist Standard, wenn kein Wert angegeben wird.)

Ich habe beschlossen, der Persistenz-XML, die für die Ausführung im nicht verwalteten Modus konfiguriert ist, eine weitere Persistenzeinheit hinzuzufügen: <persistence-unit name="test2" transaction-type="RESOURCE_LOCAL"> .

(Hinweis: Das Waring um Multiple Persistence Units ist nur die Ursache, die Eclipse nicht verarbeiten kann. Es hat keinerlei funktionale Auswirkungen)

Der nicht verwaltete Persistenzkontext erfordert eine lokale Konfiguration der Datenbank, da diese nicht mehr containerbereit ist:

%Vor%

Eine für das Projekt erforderliche Änderung wäre nun, dass Sie ein unitName hinzufügen, wenn Sie die Annotation @PersistenceContext verwenden, um eine verwaltete Instanz des EntityManager abzurufen.

Beachten Sie jedoch, dass Sie @PersistenceContext nur für die verwaltete Persistenzeinheit verwenden können. Für die nicht verwaltete Version können Sie eine einfache Producer implementieren und den EntityManager mit CDI injizieren, wenn dies erforderlich ist:

%Vor%

Nun müssen Sie in dem Beispiel, das im ursprünglichen Beitrag angegeben ist, den EntityManager einsprühen und manuell auf Transaktionen achten.

%Vor%

Meine bisherigen Tests haben gezeigt, dass das Mischen von nativen Abfragen und Persistenzaufrufen zum gewünschten Ergebnis führt: Entweder wird alles festgeschrieben oder die Transaktion wird als Ganzes rückgängig gemacht.

Für den Moment scheint die Lösung zu funktionieren. Ich werde weiterhin seine Funktionalität im Hauptprojekt überprüfen und prüfen, ob es andere Nebenwirkungen gibt.

Eine andere Sache, die ich überprüfen muss, ist, ob es speichern würde:

  • Injizieren Sie beide Versionen der EM in eine Bean und mischen Sie sie. (Erste Überprüfungen scheinen zu funktionieren, auch wenn beide ems gleichzeitig auf demselben (n) Tisch (en) stehen)
  • Beide Versionen der EM laufen auf derselben Datenquelle. (Die gleiche Datenquelle wäre höchstwahrscheinlich kein Problem, die gleichen Tabellen, die ich annahm, könnten zu unerwarteten Problemen führen.)

ps .: Das ist Entwurf 1 . Ich werde weiterhin die Antwort verbessern und auf Probleme und / oder Nachteile hinweisen, die ich finden werde.

    
dognose 22.09.2014 15:09
quelle
0

Sie müssen <hibernate.connection.release_mode key="hibernate.connection.release_mode" value="after_transaction" /> zu Ihren Eigenschaften hinzufügen. Nach einem Neustart sollte die Transaktionsverarbeitung funktionieren.

    
walerik 16.03.2015 14:41
quelle