Dieses Projekt zeigt beide Modelle nebeneinander, absichtlich an zwei verschiedenen Beans:
PolicyManagementBean (CMT, der EJB-Normalfall) und PremiumCalculationBean (BMT, die
bewusste Ausnahme). Der Kontrast ist der eigentliche Lernpunkt.
Bei CMT deklariert man was passieren soll, nicht wie. Jede Business-Methode traegt
(implizit oder explizit) ein @TransactionAttribute, und der Container setzt es um -
Transaktion starten, beitreten, committen oder zurueckrollen geschieht automatisch, ohne
dass die Bean selbst begin()/commit() aufruft.
Die sechs TransactionAttributeType-Werte:
| Attribut | Verhalten |
|---|---|
REQUIRED (Default) |
Tritt einer laufenden Transaktion bei, sonst startet eine neue. |
REQUIRES_NEW |
Startet immer eine neue Transaktion; eine laufende wird pausiert. |
MANDATORY |
Erfordert eine bereits laufende Transaktion; sonst Exception. |
SUPPORTS |
Tritt bei, falls vorhanden; laeuft sonst ganz ohne Transaktion. |
NOT_SUPPORTED |
Laeuft nie transaktional; eine laufende wird pausiert. |
NEVER |
Darf nie innerhalb einer Transaktion aufgerufen werden; sonst Exception. |
PolicyManagementBean nutzt drei davon konkret:
REQUIRED (Default) fuer createPolicy, activatePolicy, cancelPolicy - der
Normalfall: schreibende Operationen sollen entweder ganz oder gar nicht wirksam werden.SUPPORTS fuer getPolicy und findActivePoliciesForCustomer - reine Lesezugriffe
brauchen keine eigene Transaktion; sie laufen einfach in der des Aufrufers mit, falls
vorhanden, sonst ganz ohne.REQUIRES_NEW fuer recordClaimAgainstPolicy - siehe die ausfuehrliche Begruendung
direkt im Javadoc dieser Methode: eine MDB-Verarbeitung soll diese Erfassung nicht
ruecklaeufig machen koennen, nur weil ein spaeterer Schritt in derselben MDB-Transaktion
fehlschlaegt.Automatischer Rollback bei System Exceptions: Wirft eine CMT-Methode eine unchecked
Exception (z.B. LegacySystemUnavailableException), markiert der Container die Transaktion
automatisch fuer Rollback - ganz ohne Zutun des Codes. Bei Application Exceptions
(PolicyNotFoundException, UnderwritingRejectedException) entscheidet dagegen explizit das
@ApplicationException(rollback = ...)-Attribut, siehe
docs/10-ejb-konzepte/06-exceptions.md.
PremiumCalculationBean ist mit @TransactionManagement(TransactionManagementType.BEAN)
markiert - damit ist CMT fuer die gesamte Klasse abgeschaltet, und der Code muss
Transaktionsgrenzen selbst ueber die injizierte javax.transaction.UserTransaction
verwalten:
userTransaction.begin();
try {
// ... Arbeit ...
userTransaction.commit();
} catch (Exception e) {
userTransaction.rollback();
}
Warum ist das so viel Boilerplate? Jede der vier UserTransaction-Methoden deklariert
eine eigene Handvoll checked Exceptions (NotSupportedException, SystemException,
RollbackException, HeuristicMixedException, HeuristicRollbackException, ...) - siehe
PremiumCalculationBean.lookupHistoricalBenchmark(...) fuer die vollstaendige Behandlung.
Bei CMT nimmt einem der Container all das ab; das ist der Hauptgrund, warum CMT in der
grossen Mehrheit der EJB-Beans verwendet wird und BMT die Ausnahme bleibt.
Wann lohnt sich BMT trotzdem? Wenn eine Bean feingranularer steuern muss, wann genau eine Transaktion beginnt und endet, als es die deklarativen CMT-Attribute erlauben - z.B. mehrere unabhaengige Transaktionsabschnitte innerhalb einer Methode, mit Zwischenverarbeitung dazwischen, die explizit ausserhalb jeder Transaktion laufen soll. In der Praxis (und auch in diesem Projekt) ist das selten genug, dass man BMT bewusst als Sonderfall kennzeichnen sollte, wenn man es einsetzt - nicht als Standardwerkzeug.
CMT: "Ich sage dem Container, WAS ich fuer eine Transaktionsgrenze brauche." BMT: "Ich baue die Transaktionsgrenze selbst, Zeile fuer Zeile."
Fast immer will man Ersteres. PremiumCalculationBean existiert in diesem Projekt
ausschliesslich, um das Zweite einmal konkret gesehen zu haben.