Java >> Java-zelfstudie >  >> Java

Voorbeeld van EJB-transactiebeheer

1. Inleiding

Transacties bevrijden de applicatieprogrammeur van het omgaan met de complexe problemen van foutherstel en multi-user programmering.

Het transactiesysteem zorgt ervoor dat een werkeenheid ofwel volledig wordt voltooid, ofwel dat het werk volledig wordt teruggedraaid.

2. Transactiebeheer Typ EJB

De Enterprise Bean Provider en de programmeur van de clienttoepassing worden niet blootgesteld aan de complexiteit van gedistribueerde transacties.

De Bean Provider kan kiezen tussen het gebruik van programmatische transactieafbakening in de enterprise bean-code (deze stijl wordt bean-managed transactie genoemd) afbakening)  of  declaratieve transactieafbakening automatisch uitgevoerd door de EJB-container (deze stijl wordt door container beheerde transactie genoemd) afbakening).

Standaard heeft een sessiebean of berichtgestuurde bean een containerbeheerde transactieafbakening als het transactiebeheertype niet is opgegeven. De Bean Provider van een session bean of een message-driven bean kan de TransactionManagement . gebruiken annotatie om het transactietype te declareren. De waarde van de TransactionManagement annotatie is ofwel CONTAINER of BEAN .

3. Door container beheerde transactie (CMT)

Met containerbeheerde transactieafbakening bakent de container transacties af volgens instructies die door de ontwikkelaar zijn verstrekt in metadata-annotaties of in de implementatiedescriptor.

Met CMT worden transacties gestart en voltooid (met een commit of rollback) door de container.

CMT Boon

Enterprise JavaBeans definieert de volgende waarden voor de TransactionAttribute metadata annotatie:

  • VEREIST
  • REQUIRES_NEW
  • VERPLICHT
  • NOT_SUPPORTED
  • ONDERSTEUNING
  • NOOIT

Transactiekenmerken en bereik

Een T1-transactie is gekoppeld aan de client die een methode aanroept in de enterprisebean en een T2-transactie wordt gestart door de container net voordat de methode wordt uitgevoerd. Het woord "Geen" betekent dat de bedrijfsmethode niet wordt uitgevoerd binnen een transactie die wordt beheerd door de container.

3.1 Transactiekenmerken instellen

Transactiekenmerken worden gespecificeerd door de enterprise bean-klasse of -methode te versieren met een javax.ejb.TransactionAttribute annotatie en zet deze op een van de javax.ejb.TransactionAttributeType constanten.
Standaard, als een TransactionAttribute annotatie is niet gespecificeerd voor een methode van een enterprisebean met containerbeheerde transactieafbakening, de waarde van het transactiekenmerk voor de methode is gedefinieerd als REQUIRED .

Als je de enterprise bean class versiert met @TransactionAttribute , de opgegeven TransactionAttributeType wordt toegepast op alle bedrijfsmethoden in de klas. Een bedrijfsmethode verfraaien met @TransactionAttribute past de TransactionAttributeType . toe alleen voor die methode. Als een @TransactionAttribute annotatie siert zowel de klasse als de methode, de methode TransactionAttributeType overschrijft de klasse TransactionAttributeType .

Het volgende codefragment laat zien hoe u de @TransactionAttribute . gebruikt annotatie:

package com.javacodegeeks.example.beans;

import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;

@Stateless
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class SampleBean {
    ...
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void firstMethod() {...}

    @TransactionAttribute(TransactionAttributeType.MANDATORY)
    public void secondMethod() {...}

    public void thirdMethod() {...}

}

In dit voorbeeld is het transactiekenmerk van de SampleBean-klasse ingesteld op NotSupported , firstMethod is ingesteld op RequiresNew , en secondMethod is ingesteld op Verplicht . Omdat een @TransactionAttribute ingesteld op een methode overschrijft de klasse @TransactionAttribute , zullen oproepen naar firstMethod een nieuwe transactie maken en oproepen naar secondMethod moeten de transactie van de client gebruiken. Aanroepen naar thirdMethod vinden niet plaats binnen een transactie.

3.2 Door container beheerde transactieafbakening

Bereik van transactiecontext

3.3 Een door een container beheerde transactie terugdraaien

Er zijn twee manieren om een ​​door een container beheerde transactie terug te draaien. Ten eerste, als er een systeemuitzondering wordt gegenereerd, zal de container de transactie automatisch terugdraaien. Ten tweede, door de setRollbackOnly . aan te roepen methode van de EJBContext interface, instrueert de bean-methode de container om de transactie terug te draaien. Als de bean een toepassingsuitzondering genereert, is het terugdraaien niet automatisch, maar kan het worden gestart door een aanroep van setRollbackOnly .

3.4 Voorbeeldscenario voor transactiekenmerk om in actie te komen

Nieuw vereist – Een voorbeeld nemen van een betaalmethode naar een rekening van een derde partij van een bankmodule die het bedrag van de bankrekening aftrekt wanneer een overboeking succesvol is (of niets wordt uitgevoerd als het mislukt), ongeacht of deze overboeking succesvol is of niet (met een rollback ), zou de logfunctie nog steeds functioneel moeten zijn om de status van de transactie te loggen en mag deze niet worden beïnvloed door het terugdraaien van de overdracht.

Verplicht – Als het proces te lang is om in één methode te worden opgenomen en de codes in twee of meer subprocesmethoden moeten worden opgesplitst, moeten de andere subprocesmethoden worden geannoteerd met MANDATORY is een goede keuze.

Nooit – Een methode moet worden geannoteerd met TransactionAttributeType.NEVER als het alleen bestaat uit logica die "NOOIT" de database raakt of enige aanroep van andere methoden die transactioneel zijn.

Niet ondersteund –  Beter geschikt voor methoden die query's uitvoeren op objecten die statische gegevens bevatten waarvan niet wordt verwacht dat ze worden gewijzigd of die transacties met andere zakelijke transacties te maken hebben. Het kunnen querymethoden zijn voor statisch permanente gegevens zoals landenlijst, regiolijst, geslachtslijst, enz. Methoden die gegevens opvragen om met name vervolgkeuzelijstopties in het selectievak van webformulieren vast te stellen, zijn zeer geschikt om te worden geannoteerd met NOT_SUPPORTED . Annoteren NOT_SUPPORTED in methoden als deze zullen applicaties aanzienlijk worden bespaard op transactieoverhead(s).

3.5 Sessiesynchronisatie (Stateful Session bean-transactie)

In het geval van een stateful session bean, is het mogelijk dat de bedrijfsmethode of interceptormethode waarmee een transactie is gestart, wordt voltooid zonder de transactie vast te leggen of terug te draaien. In een dergelijk geval moet de container de koppeling tussen de transactie en de instantie behouden over meerdere clientaanroepen totdat de instantie de transactie vastlegt of terugdraait. Wanneer de klant de volgende bedrijfsmethode aanroept, moet de container in deze transactiecontext de bedrijfsmethode aanroepen.

Als een session bean-klasse de javax.ejb.SessionSynchronization . implementeert interface of de sessiesynchronisatie-annotaties gebruikt, moet de container de afterBegin aanroepen , beforeCompletion , en afterCompletion callbacks op de instance als onderdeel van het transactie commit protocol.

  • De container roept de afterBegin . op methode op een instantie voordat deze de eerste zakelijke methode in een transactie aanroept.
  • De container roept de beforeCompletion . op methode om de enterprise bean-instantie de laatste kans te geven om de transactie terug te draaien. De instantie kan ervoor zorgen dat de transactie wordt teruggedraaid door de EJBContext.setRollbackOnly . aan te roepen methode.
  • De container roept de afterCompletion(boolean committed) . op methode na de voltooiing van het transactiebevestigingsprotocol om de enterprise bean-instantie op de hoogte te stellen van het transactieresultaat.

CartBean.java

package com.javacodegeeks.example.beans;

import java.util.ArrayList;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.AfterBegin;
import javax.ejb.AfterCompletion;
import javax.ejb.BeforeCompletion;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;

/**
 *
 * @author jGauravGupta
 */

@Stateful
@TransactionManagement(value=TransactionManagementType.CONTAINER)
public class CartBean  {
    private ArrayList items;

    @PostConstruct
    public void init() {
        items = new ArrayList();
        System.out.println("CartBean: init");
    }

    @PreDestroy 
    public void destroy() {
        System.out.println("CartBean: destroy");
    }
    
    @Remove
    public void checkOut() {
        // Release any resources.
        System.out.println("Cart checkout...");
    }

    public void addItem(String item) {
        getItems().add(item);
        System.out.println(item + " item added to cart");
    }

    public void removeItem(String item) {
        getItems().remove(item);
        System.out.println(item + " item removed from cart");
    }

    public ArrayList getItems() {
        return items;
    }
    
    @AfterBegin
    private void afterBegin(){
        System.out.println("A new transaction has started.");
    }
    
    @BeforeCompletion
    private void beforeCompletion(){
        System.out.println("A transaction is about to be committed.");
    }
    
    @AfterCompletion
    private void afterCompletion(boolean committed) {
        System.out.println("a transaction commit protocol has completed, and tells the instance whether the transaction has been committed or rolled back , based on committed value : " + committed);
    }
    
}

Als het verzoek van de klant niet is gekoppeld aan een transactie

NO_TX_Client_Tester.java

package com.javacodegeeks.example.tester.non_tx;

import com.javacodegeeks.example.beans.CartBean;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 *
 * @author jGauravGupta
 */
@WebServlet(name = "NO_TX_Client_Tester", urlPatterns = {"/NO_TX_Client_Tester"})
public class NO_TX_Client_Tester extends HttpServlet {

    protected void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        try (PrintWriter out = response.getWriter()) {

            CartBean cartBean = lookupCartBeanBean();

            cartBean.addItem("Smart Watch");
            cartBean.addItem("iPhone");
            cartBean.addItem("Shoes");

            out.println("Cart Item Size : " + cartBean.getItems().size());

            cartBean.checkOut();
        }
    }


    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        processRequest(request, response);
    }

    private CartBean lookupCartBeanBean() {
        try {
            Context c = new InitialContext();
            return (CartBean) c.lookup("java:global/CMT_Example/CartBean!com.javacodegeeks.example.beans.CartBean");
        } catch (NamingException ne) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", ne);
            throw new RuntimeException(ne);
        }
    }

}
Uitvoer

Controleer de volgende uitvoer in de NetBeans-console:

Info:   A new transaction has started.
Info:   Smart Watch item added to cart
Info:   A transaction is about to be committed.
Info:   a transaction commit protocol has completed, and tells the instance whether the transaction has been committed or rolled back , based on committed value : true
Info:   A new transaction has started.
Info:   iPhone item added to cart
Info:   A transaction is about to be committed.
Info:   a transaction commit protocol has completed, and tells the instance whether the transaction has been committed or rolled back , based on committed value : true
Info:   A new transaction has started.
Info:   Shoes item added to cart
Info:   A transaction is about to be committed.
Info:   a transaction commit protocol has completed, and tells the instance whether the transaction has been committed or rolled back , based on committed value : true
Info:   A new transaction has started.
Info:   Cart checkout...

Als het verzoek van de klant is gekoppeld aan een transactie

TX_Client_Tester.java

package com.javacodegeeks.example.tester.tx;

import com.javacodegeeks.example.beans.CartBean;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Resource;
import javax.ejb.Singleton;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.HeuristicMixedException;
import javax.transaction.HeuristicRollbackException;
import javax.transaction.NotSupportedException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.UserTransaction;

/**
 *
 * @author jGauravGupta
 */
@Singleton
@TransactionManagement(TransactionManagementType.BEAN)
public class CartProcess {

    @Resource
    private UserTransaction ut;

    public void executeCartProcess() {
        try {
            Context c = new InitialContext();
            CartBean cartBean = (CartBean) c.lookup("java:global/CMT_Example/CartBean!com.javacodegeeks.example.beans.CartBean");

            ut.begin();
            cartBean.addItem("Smart Watch");
            cartBean.addItem("iPhone");
            cartBean.addItem("Shoes");

            System.out.println("Cart Item Size : " + cartBean.getItems().size());
            ut.commit();

            cartBean.checkOut();

        } catch (NamingException ex) {
            Logger.getLogger(CartProcess.class.getName()).log(Level.SEVERE, null, ex);
        } catch (RollbackException | HeuristicMixedException | HeuristicRollbackException | SecurityException | IllegalStateException | SystemException | NotSupportedException ex) {
            try {
                ut.rollback();
                Logger.getLogger(CartProcess.class.getName()).log(Level.SEVERE, null, ex);
            } catch (IllegalStateException | SecurityException | SystemException ex1) {
                Logger.getLogger(CartProcess.class.getName()).log(Level.SEVERE, null, ex1);
            }
        }
    }

}

Uitvoer

Controleer de volgende uitvoer in de NetBeans-console:

Info:   CartBean: init
Info:   A new transaction has started.
Info:   Smart Watch item added to cart
Info:   iPhone item added to cart
Info:   Shoes item added to cart
Info:   Cart Item Size : 3
Info:   A transaction is about to be committed.
Info:   a transaction commit protocol has completed, and tells the instance whether the transaction has been committed or rolled back , based on committed value : true
Info:   A new transaction has started.
Info:   Cart checkout...
Info:   CartBean: destroy

4. Door bonen beheerde transactie (BMT)

Hoewel het waar is dat de ejb-container meestal behoorlijk slim is in het afhandelen van transacties, is hij ook niet zo slim als een echt mens en is hij waarschijnlijk niet in staat om complexe databasetransacties en terugdraaiingen aan te kunnen. Dit is waar bean managed transacties om de hoek komen kijken. Door uw eigen transacties af te handelen kunt u enkele grote valkuilen vermijden.

Met bean-beheerde transactieafbakening demarceert de enterprisebean-code transacties met behulp van de javax.transaction.UserTransaction koppel. Alle toegangen tot resourcemanagers tussen de UserTransaction.begin- en UserTransaction.commit-aanroepen maken deel uit van een transactie.

Terwijl een instantie zich in een transactie bevindt, mag de instantie niet proberen de resourcemanager-specifieke transactiedemarcatie-API te gebruiken (het mag bijvoorbeeld niet de commit- of rollback-methode aanroepen op de java.sql.Connection interface of op de javax.jms.Session interface).

package com.javacodegeeks.example.beans;

import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionManagement;
import javax.ejb.TransactionManagementType;
import javax.transaction.UserTransaction;

@Stateless
@TransactionManagement(value=TransactionManagementType.BEAN)
public class AccountBean {
   
   @Resource
   private UserTransaction userTransaction;

   public void withdrawAmount(long accountId , double fund) {

      try{
         userTransaction.begin();
         
         // TO DO withdrawAmount ....
        
         userTransaction.commit();
      } catch (InsufficientFundException exception){
         userTransaction.rollback();
      }
   }

}

In dit voorbeeld hebben we gebruik gemaakt van UserTransaction interface om het begin van de transactie te markeren met userTransaction.begin() methode oproep. We markeren de voltooiing van de transactie met userTransaction.commit() methode en als er een uitzondering is opgetreden tijdens de transactie, draaien we de volledige transactie terug met behulp van userTransaction.rollback() methode oproep.

4.1 BMT De klanttransactie opschorten

Als een transactie aan de gang is wanneer een methode op een BMT-bean wordt aangeroepen, wordt de transactie opgeschort. Tijdelijk. De transactie wacht gewoon tot de BMT-boon zijn werk voltooit. Werk dat geen deel uitmaakt van de oorspronkelijke transactie van de beller. Zodra de BMT-methode is voltooid en van de stapel is verwijderd, begint de oorspronkelijke transactie weer, precies waar deze was gebleven.

BMT-boon

De dingen die gebeuren terwijl de transactie is opgeschort, worden niet teruggedraaid als de opgeschorte transactie (nadat deze weer tot leven komt) niet wordt vastgelegd.

5. setRollbackOnly() leeft in TWEE interfaces

CMT-bonen kunnen alleen de EJBContext.setRollbackOnly() . gebruiken en BMT-bonen kunnen alleen de UserTransaction.setRollbackOnly() . gebruiken .

De CMT-bean kent de status van de transactie met behulp van EJBContext.getRollbackOnly() methode  , Als de transactie is gemarkeerd voor terugdraaien, dan getRollbackOnly() methode  retourneert waar en retourneert anders false.

De BMT-bean kent de status van de transactie met behulp van UserTransaction.getStatus() methode , De getStatus() methode retourneert een int die een constante vertegenwoordigt voor zaken als:STATUS_ACTIVE, STATUS_COMMITTED, STATUS_COMMITTING, STATUS_MARKED_ROLLBACK en STATUS_ROLLING_BACK enz.

6. Transactiegrenzen

6.1 JMS API

  • De Bean Provider mag geen gebruik maken van het JMS request/reply-paradigma (verzenden van een JMS-bericht, gevolgd door de synchrone ontvangst van een antwoord op dat bericht) binnen een enkele transactie. Omdat een JMS-bericht doorgaans pas op de eindbestemming wordt afgeleverd als de transactie is vastgelegd, zal de ontvangst van het antwoord binnen dezelfde transactie niet plaatsvinden.
  • Een transactie begint vóór het uit de wachtrij halen van het JMS-bericht en dus vóór het aanroepen van de onMessage-methode van de berichtgestuurde bean. Als de onMessage-methode niet succesvol wordt voltooid of de transactie wordt teruggedraaid, is de semantiek voor het opnieuw bezorgen van berichten van toepassing.

6.2 Asynchrone methode

De transactiecontext van de klant verspreidt zich niet met een asynchrone methodeaanroep. De semantiek van de REQUIRED transactiekenmerk voor een asynchrone methode zijn hetzelfde als REQUIRES_NEW .

6.3 Timing van Return Value Marshalling

Bij het afbakenen van een door een container beheerde transactie voor een aanroep van een bedrijfsmethode via een weergave op afstand of een webserviceweergave, moet de container het commit-protocol voltooien voordat de geretourneerde waarde wordt gerangschikt.

7. Download het NetBeans-project

Download het NetBeans-project voor deze tutorial:

8. Conclusie

BMT-bean wordt alleen uitgevoerd in de transacties die de bean zelf maakt en start, zodat het het hele punt van de bruikbaarheid van een componentmodel verslaat. Met BMT kunt u het bereik van een transactie verkleinen, maar met CMT kunt u een transactie niet markeren op iets dat kleiner is dan een enkele methode.

Java-tag