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.
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>
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.
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.
@Resource UserTransaction
wird Ihnen die Benutzertransaktion per Injektion geben java:comp/UserTransaction
wird Ihnen die Benutzertransaktion über die Suche @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%
@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. Einige Dinge können dazu führen, dass Ihre laufende Transaktion angehalten, ausgesetzt oder auf andere Weise nicht an die aufgerufene Bean weitergegeben wird.
@ApplicationException
oder EntityTransaction
umgeht die Transaktionsverwaltung. 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!
Tags und Links java-ee transactions java-ee-6 ejb ejb-3.1