Warum schreibt CMT beim Beenden der EJB-Methode ein, wenn das Transaktionsattribut "Required" lautet?

8

Ich stelle fest, dass meine bereits bestehende Transaktion in einer beliebigen Methode eines EJB Committed Committee @ejb.transaction type="Required" festgeschrieben wird. Kann das richtig sein?

Ich erwarte, dass eine EJB, die eine Transaktion "benötigt", bedeutet: Wenn bereits eine vorhanden ist, wird sie höflich belassen, wenn sie ausgeführt wird, damit derjenige, der anfängt (), sie für weitere Operationen verwenden kann, bevor sie commit() aufruft. oder rollback() . [Natürlich, wenn es überhaupt keine Transaktion gäbe, würde die EJB-Methode sowohl begin() als auch commit() / rollback() aufrufen.]

Ist meine Erwartung falsch oder sollte ich nach einem Konfigurationsfehler suchen?

Es könnte wichtig sein hinzuzufügen, dass ich Hibernate 3 innerhalb des EJB verwende. Ich erhalte eine UserTransaction, bevor ich die EJB-Methode aufruft. Der von EJB generierte Wrapper ruft ServerTransaction.commit() beim Beenden auf, in den sich Hibernate einklinkt und die Gelegenheit nutzt, um seine Sitzung zu schließen. Der Fehler, den ich erhalte, ist eine Hibernate-Lazy-Lade-Ausnahme, weil die Sitzung geschlossen wird, wenn ich versuche, auf Getter auf einem Hibernate-Persistent-Objekt zuzugreifen. Technisch gesehen bin ich nicht 100% ig sicher, ob das ServerTransaction.commit() ich beobachtet habe, das UserTransaction , das ich gestartet habe, notwendigerweise festgeschrieben hat (vielleicht geht ServerTransaction.commit() nicht immer mit einem "echten" Commit durch?), Aber wenn es so wäre nicht - auf welcher Grundlage schließt Hibernate die Sitzung?

Update: Ich glaube, meine obigen Annahmen waren richtig, aber meine Beobachtungen waren ein bisschen aus. Siehe unten für meine selbstgestellte Antwort.

    
nclark 25.08.2011, 19:42
quelle

4 Antworten

1

Bei genauerer Betrachtung ergibt sich eine andere Antwort als oben vorgeschlagen. Was ich tatsächlich sehe, ist, dass die von mir gestartete UserTransaction offen geblieben ist, aber CMT hat trotz des "Required" -Attributs eine neue Transaktion beim Eintritt in die EJB-Methode erstellt.

Ich glaube, das passiert, weil ich die Regeln gebrochen habe. :) Sie dürfen nicht auf die UserTransaction-API zugreifen, wenn Sie CMT verwenden. CMT ignorierte meine UserTransaction glücklicherweise und startete seine eigene, indem sie ihren Platz als Beauftragter aller Transaktionsgrenzen einnahm. Da es die Transaktion gestartet hat, hat es es auch übernommen und natürlich meine UserTransaction unberührt gelassen.

Sieht für mich spröde und albern aus, vielleicht eine naive Meinung, scheint aber mit den "Regeln" übereinzustimmen, so wie ich sie gelesen habe. Ich weiß nicht, warum CMT entscheidet, mit UserTransactions, die auf einer höheren Ebene gestartet wurden, nicht nett zu spielen. Vielleicht, um Entwickler zu zwingen, "die richtige J2EE-Sache zu machen" und einen weiteren Session-Bean-Layer zu erstellen, um den breiteren Transaktionskontext zu behandeln. Dies würde funktionieren, weil CMT die äußere Transaktion verwalten würde und daher keine inneren Transaktionen in Anspruch nehmen würde, und ich glaube, dass in einem solchen Fall die "Umbrella" -Transaktion nicht von den inneren EJBs begangen würde; CMT würde warten, bis die äußere Transaktion abgeschlossen ist, und dann die ganze Sache begehen. Das müsste es eigentlich tun.

Ich bin nicht in der Stimmung, mehr Session-EJBs in dieser bereits EJB-aufgeblähten App zu erstellen, aber es könnte die einzige Lösung sein, die CMT an einer ganzen Reihe von Orten, die ich lieber nicht anfasse, nicht zu zerreißen. p>     

nclark 26.08.2011, 15:08
quelle
19

ERFORDERLICH kann böse sein

Mir persönlich gefällt das Attribut REQUIRED nicht, und ich rate dringend davon ab, es zu benutzen.

Das mühsame Erstellen von Transaktionen (was REQUIRED tut) führt dazu, dass man nicht wirklich weiß, wann und wo eine Transaktion tatsächlich gestartet wurde und wann sie ausgeführt wird. Das ist nicht eine gute Sache. Leute sollten Transaktionsgrenzen explizit entwerfen.

MANDATORY und UserTransaction sind Seelenverwandte

Ihr Wunsch, UserTransaction zu verwenden, ist sehr gut und funktioniert mit CMT - es gibt keinen Unterschied zwischen einer JTA-Transaktion, die über UserTransaction vom Container gestartet wurde, oder einer JTA-Transaktion, die für Sie gestartet wurde durch den Container.

Der Ansatz, den ich empfehlen würde, besteht darin, die gesamte erforderliche Verwendung auf OBLIGATORISCH umzustellen. Mit MANDATORY wird der Container nicht Transaktionen für Sie starten. Stattdessen schützt es Ihre Bean, indem sichergestellt wird, dass sie nicht aufgerufen werden kann, solange keine Transaktion läuft. Dies ist eine erstaunliche und wenig genutzte Funktion. MANDATORY ist der beste Freund von allen, die eine wahrhaft deterministische Transaktions-App erstellen und durchsetzen wollen. Mit diesem Setup könnten Sie sich in CMT verlieben.

In diesem Szenario starten Sie die Transaktionen mit UserTransactions und dann ist der Container wie Ihr großer Bodyguard, der Leute an den Randstein tritt, es sei denn, sie haben eine Transaktion ordnungsgemäß gestartet, bevor Sie versuchen, Ihren Code aufzurufen.

Überlegungen

  • Ein Servlet oder ein BMT EJB kann eine UserTransaction verwenden.
  • CMT-Beans können keine UserTransaction verwenden, aber sie können an einer Transaktion teilnehmen, die von einer UserTransaction gestartet wurde (wie oben erwähnt, ist eine JTA-Transaktion eine JTA-Transaktion).
  • BMT-Beans können nicht an einer bestehenden Transaktion teilnehmen. Wenn Sie eine BMT-Bean aufrufen, während eine Transaktion bereits ausgeführt wird, wird die Transaktion vor dem Aufrufen der BMT-Bean vom Container angehalten und nach Abschluss der Methode fortgesetzt.
  • @Resource UserTransaction wird Ihnen die Benutzertransaktion per Injektion geben
  • java:comp/UserTransaction wird Ihnen die Benutzertransaktion über die Suche
  • liefern
  • @TransactionAttribute(MANDATORY) , das auf Klassenebene verwendet wird, wirkt sich auf die Methoden dieser exakten Klasse aus (d. h. die Methoden fooClass.getDecaredMethods() ). Methoden von Superklassen und Unterklassen werden standardmäßig auf @TransactionAttribute(REQUIRED) gesetzt, es sei denn, diese Klassen sind explizit mit Anmerkungen versehen.% Co_de%
  • Wenn es Ihnen schlecht wird, @TransactionAttribute(MANDATORY) und userTransaction.begin() aufzurufen und die zugehörige Ausnahmebehandlung durchzuführen, betrachten Sie userTransaction.commit() . Ihre Transaktionsgrenzen sind weiterhin dokumentiert und explizit.
  • @TransactionAttribute(REQUIRES_NEW) s, das von einer Methode einer CMT-Bean ausgelöst wird, bewirkt, dass die Transaktion für das Rollback markiert wird, selbst wenn Sie die Ausnahme im aufrufenden Code abfangen und behandeln. Verwenden Sie RuntimeException , um dies von Fall zu Fall für benutzerdefinierte Laufzeitausnahmeklassen zu deaktivieren.

Verlust des Transaktionskontextes

Einige Dinge können dazu führen, dass Ihre laufende Transaktion angehalten, ausgesetzt oder auf andere Weise nicht an die aufgerufene Bean weitergegeben wird.

  • BMT-Beans stoppen die Transaktionsausbreitung. Wenn eine laufende Transaktion eine BMT-Bean aufruft, wird diese Transaktion vor dem Aufruf der BMT-Bean angehalten und nach der Rückkehr der Bean fortgesetzt. BMT-Beans können Ursprung von Transaktionen sein, können aber nicht an bestehenden Transaktionen teilnehmen. Wenn die Weitergabe auf mysteriöse Weise fehlschlägt, stellen Sie sicher, dass während der Transaktion keine unbeabsichtigten Aufrufe an BMT-Beans erfolgen.
  • Verwenden Sie keine anderen als von einem Container bereitgestellten UserTransaction- oder Container-Methoden wie SessionContext.setRollbackOnly, um Transaktionen zu verwalten. Die Verwendung einer "Ressourcen-Manager-spezifischen Transaktions-Demarkations-API" wie JPA @ApplicationException oder EntityTransaction umgeht die Transaktionsverwaltung.
  • Beginne nicht deine eigenen Threads. Die Transaktionsausbreitung erfolgt auf einer Pro-Thread-Basis. In Java EE gibt es no Standard-APIs, die eine Transaktion unterstützen, die mehrere Threads umfasst. Wenn Sie den Thread entweder davon abhalten, einen eigenen Thread zu starten oder @Asynchronous zu verwenden, hinterlassen Sie Ihre bestehende Transaktion.
David Blevins 28.08.2011 00:34
quelle
1

NClark, betrachte den folgenden Code, den ich auf GlassFish 3.1.1 ausgeführt habe. Ich hoffe es hilft in keiner Weise: -)

%Vor%

Dieser EJB kann aufgerufen werden, d. h. von Singleton EJB mit @Startup, um sofort zu sehen, wie Ihr AS reagieren wird.

Auf Glassfish 3.1.1 werden Sie mit folgendem Ergebnis kommen:

  

INFO: #### testMethod #### Sendezustand: 0

     

INFO: #### testIt #### Sendezustand: 1

     

INFO: #### testMethod #### Sendezustand: 1

Prost!

    
Piotr Nowicki 30.08.2011 23:14
quelle
0

So funktionieren CMT-Transaktionen. Der Container bestätigt die Transaktion automatisch, wenn die Geschäftsmethode zurückgegeben wird. Wenn Sie dieses Verhalten nicht verwenden, verwenden Sie BMT anstelle von CMT.

>     
Kris 26.08.2011 12:23
quelle