Domanda

Ho un bean stateless qualcosa del tipo:

@Stateless
public class MyStatelessBean implements MyStatelessLocal, MyStatelessRemote {
    @PersistenceContext(unitName="myPC")
    private EntityManager mgr;

    @TransationAttribute(TransactionAttributeType.SUPPORTED)
    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.process(obj);
        }
    }

    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(Object obj) {
        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();
    }
}

L'utilizzo tipico è quindi che il client chiamerebbe processObjects (...), che in realtà non interagisce con il gestore entità. Fa quello che deve fare e chiama il processo (...) individualmente per ogni oggetto da elaborare. La durata del processo (...) è relativamente breve, ma processObjects (...) potrebbe richiedere molto tempo per eseguire tutto. Pertanto non voglio che mantenga una transazione aperta. devo che le singole operazioni di processo (...) funzionino all'interno della propria transazione. Questa dovrebbe essere una nuova transazione per ogni chiamata. Infine, vorrei mantenere l'opzione aperta affinché il client possa chiamare direttamente il processo (...).

Ho provato diversi tipi di transazione: mai, non supportato, supportato (su processObjects) e richiesto, richiede nuovo (sul processo) ma ricevo TransactionRequiredException ogni volta che viene chiamato merge ().

Sono stato in grado di farlo funzionare suddividendo i metodi in due bean diversi:

@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {
    @EJB
    private MyStatelessBean2 myBean2;

    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.myBean2.process(obj);
        }
    }
}

@Stateless
public class MyStatelessBean2 implements MyStatelessLocal2, MyStatelessRemote2 {
    @PersistenceContext(unitName="myPC")
    private EntityManager mgr;

    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(Object obj) {
        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();
    }
}

ma sono ancora curioso di sapere se è possibile farlo in una classe. Mi sembra che il gestore delle transazioni funzioni solo a livello di bean, anche quando ai singoli metodi vengono date annotazioni più specifiche. Quindi, se contrassegno un metodo in modo da impedire alla transazione di iniziare a chiamare altri metodi all'interno della stessa istanza, non creerò una transazione, indipendentemente da come siano contrassegnati?

Sto usando JBoss Application Server 4.2.1.GA, ma le risposte non specifiche sono benvenute / preferite.

È stato utile?

Soluzione

Un altro modo per farlo è in realtà avere entrambi i metodi sullo stesso bean - e avere un riferimento @EJB a se stesso! Qualcosa del genere:

// supposing processObjects defined on MyStatelessRemote1 and process defined on MyStatelessLocal1
@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {
    @EJB
    private MyStatelessLocal1 myBean2;

    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.myBean2.process(obj);
        }
    }


    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(Object obj) {
        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();
    }
}

In questo modo in realtà 'forzate' l'accesso al metodo process () tramite la pila di proxy di ejb, rendendo quindi attivo @TransactionAttribute - e mantenendo comunque solo una classe. Accidenti!

Altri suggerimenti

Matt, la domanda che poni è piuttosto classica, penso che la soluzione di autoreferenziazione di Herval / Pascal sia chiara. Esiste una soluzione più generale non menzionata qui.

Questo è un caso per EJB " user " transazioni. Dato che si trova in un bean di sessione, è possibile ottenere la transazione dell'utente dal contesto della sessione. Ecco come apparirà il tuo codice con le transazioni dell'utente:

// supposing processObjects defined on MyStatelessRemote1 and process defined on MyStatelessLocal1
@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {

    @Resource
    private SessionContext ctx;

    @EJB
    private MyStatelessLocal1 myBean2;

    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.myBean2.process(obj);
        }
    }


    public void process(Object obj) {

        UserTransaction tx = ctx.getUserTransaction();

        tx.begin();

        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();

        tx.commit();
    }
}

Penso che il fatto sia che ogni bean sia racchiuso in un proxy che controlla il comportamento transazionale. Quando si chiama da un bean a un altro, si passa attraverso il proxy di quel bean e il comportamento della transazione può essere modificato dal proxy.

Ma quando un bean chiama un metodo su se stesso con un attributo di transazione diverso, la chiamata non passa attraverso il proxy, quindi il comportamento non cambia.

Matt, per quello che vale sono giunto esattamente alla tua stessa conclusione.

TransactionAttributeTypes viene preso in considerazione solo quando si attraversano i confini del bean. Quando si chiamano metodi all'interno dello stesso bean TransactionAttributeTypes non ha alcun effetto, indipendentemente dai tipi inseriti nei metodi.

Per quanto posso vedere non c'è nulla nelle specifiche di persistenza di EJB che specifichi quale comportamento dovrebbe essere in queste circostanze.

L'ho sperimentato anche in Jboss. Ci proverò anche con Glassfish e ti farò sapere i risultati.

Nel caso qualcuno dovesse imbattersi in questo giorno:

per evitare dipendenze circolari (consentendo ad esempio l'autoreferenziazione) in JBoss utilizzare l'annotazione "IgnoreDependency" ad esempio:

@IgnoreDependency @EJB MySelf myselfRef;

Non l'ho ancora provato (sto per farlo), ma un'alternativa all'iniezione di un auto-riferimento tramite l'annotazione @EJB è SessionContext.getBusinessObject () metodo. Questo sarebbe un altro modo per evitare la possibilità che un riferimento circolare ti esploda, anche se almeno per l'iniezione di fagioli senza stato sembra funzionare.

Sto lavorando su un sistema di grandi dimensioni in cui entrambe le tecniche sono impiegate (presumibilmente da diversi sviluppatori), ma non sono sicuro di quale sia il "corretto" modo per farlo.

Penso che abbia a che fare con @TransationAttribute (TransactionAttributeType.Never) sul metodo processObjects .

TransactionAttributeType.Never

http://docs.sun.com / app / docs / doc / 819-3669 / 6n5sg7cm3? a = vista

  

Se il client è in esecuzione in a   transazione e invoca l'impresa   il metodo bean, il contenitore lancia a   RemoteException. Se il client non lo è   associato a una transazione, il   il contenitore non inizia un nuovo   transazione prima di eseguire il metodo.

Suppongo che tu sia cliente il metodo processObjects dal codice client. Poiché probabilmente il tuo cliente non è associato a una transazione, la chiamata del metodo con TransactionAttributeType.Never è felice in primo luogo. Quindi si chiama il metodo processo da processObjects che pur avendo l'annotazione TransactionAttributeType.Rired non era una chiamata del metodo bean e la politica di transazione non viene applicata . Quando chiami unisci ottieni l'eccezione perché non sei ancora associato a una transazione.

Prova a utilizzare TransactionAttributeType.Required per entrambi i metodi bean per vedere se funziona.

Ho avuto questi problemi di dipendenza circolare citati da Kevin. Tuttavia, l'annotazione proposta @IgnoreDependency è un'annotazione specifica di jboss e non esiste una controparte, ad esempio Glassfish.

Dato che non funziona con il riferimento EJB predefinito, questa soluzione mi è sembrata un po 'scomoda.

Pertanto, ho dato una possibilità alla soluzione di bluecarbon, iniziando così la transazione interna "a mano".

Accanto a questo, non vedo altra soluzione se non quella di implementare il processo interno () in un altro bean che è anche brutto perché vogliamo semplicemente disturbare il nostro modello di classe per tali dettagli tecnici.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top