Question

j'ai un haricot sans état, quelque chose comme:

@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();
    }
}

En règle générale, le client appelle processObjects (...), qui n'interagit pas avec le gestionnaire d'entités. Il fait ce dont il a besoin et appelle process (...) individuellement pour chaque objet à traiter. La durée du processus (...) est relativement courte, mais processObjects (...) peut prendre beaucoup de temps pour tout parcourir. Par conséquent, je ne veux pas qu'il maintienne une transaction ouverte. J'ai besoin des opérations de processus individuelles (...) pour fonctionner au sein de leur propre transaction. Cela devrait être une nouvelle transaction pour chaque appel. Enfin, je souhaite laisser la possibilité au client d’appeler directement le processus (...).

J'ai essayé plusieurs types de transaction: jamais, non pris en charge, pris en charge (sur processObjects) et requis, nécessite une nouvelle (sur le processus), mais j'obtiens une exception TransactionRequiredException chaque fois que merge () est appelée.

J'ai réussi à le faire fonctionner en divisant les méthodes en deux haricots différents:

@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();
    }
}

mais je suis toujours curieux de savoir s'il est possible d'accomplir cela en une classe. Il me semble que le gestionnaire de transactions ne fonctionne qu'au niveau du bean, même lorsque des méthodes individuelles reçoivent des annotations plus spécifiques. Donc, si je marque une méthode de manière à empêcher la transaction d’appeler d’autres méthodes au sein de la même instance ne créera pas de transaction, peu importe la façon dont elles sont marquées?

J'utilise JBoss Application Server 4.2.1.GA, mais les réponses non spécifiques sont les bienvenues / préférées.

Était-ce utile?

La solution

Une autre façon de le faire est d’avoir les deux méthodes sur le même bean et d’avoir une référence @EJB à lui-même! Quelque chose comme ça:

// 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();
    }
}

De cette façon, vous forcez la force à accéder à la méthode process () via la pile de proxies ejb, en prenant donc le @TransactionAttribute tout en conservant une seule classe. Ouf!

Autres conseils

Matt, la question que vous posez est assez classique, je pense que la solution de référence par Herval / Pascal est géniale. Il existe une solution plus générale non mentionnée ici.

C’est le cas pour l’EJB "utilisateur". transactions. Puisque vous êtes dans un bean session, vous pouvez obtenir la transaction utilisateur à partir du contexte de la session. Voici à quoi ressemblera votre code lors des transactions utilisateur:

// 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();
    }
}

Je pense que chaque bean est encapsulé dans un proxy qui contrôle le comportement transactionnel. Lorsque vous appelez d'un bean à un autre, vous passez par le proxy de ce bean et le comportement de transaction peut être modifié par le proxy.

Mais lorsqu'un bean appelle une méthode sur lui-même avec un attribut de transaction différent, l'appel ne se fait pas via le proxy, le comportement ne change donc pas.

Matt, je suis parvenu à la même conclusion que vous.

Les types TransactionAttributeTypes ne sont pris en compte que lors du franchissement des limites de beans. Lorsque vous appelez des méthodes dans le même bean, TransactionAttributeTypes n’a aucun effet, quels que soient les types placés sur les méthodes.

Autant que je sache, rien dans la spécification de persistance EJB ne spécifie le comportement à adopter dans ces circonstances.

J'ai aussi vécu cela à Jboss. Je vais également essayer Glassfish et vous informer des résultats.

Au cas où quelqu'un tomberait sur cette journée:

pour éviter les dépendances circulaires (autorisant l'auto-référence par exemple) dans JBoss, utilisez l'annotation "IgnoreDependency" par exemple:

@IgnoreDependency @EJB MySelf yourselfRef;

Je ne l'ai pas encore essayé (je vais le faire), mais une alternative à l'injection d'une référence automatique via l'annotation @EJB est le SessionContext.getBusinessObject () méthode. Ce serait une autre façon d’éviter la possibilité d’une référence circulaire vous faisant exploser, même si au moins pour une injection de fèves sans état semble fonctionner.

Je travaille sur un grand système dans lequel les deux techniques sont utilisées (probablement par des développeurs différents), mais je ne suis pas sûr de savoir quelle est la "bonne" solution. façon de le faire.

Je pense que cela a à voir avec @TransationAttribute (TransactionAttributeType.Never) sur la méthode processObjects .

TransactionAttributeType.Never

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

  

Si le client est exécuté dans un   transaction et invoque l'entreprise   la méthode de haricot, le conteneur jette un   RemoteException. Si le client n'est pas   associé à une transaction, le   le conteneur ne commence pas une nouvelle   transaction avant d'exécuter la méthode.

Je suppose que vous êtes client de la méthode processObjects à partir du code client. Parce que votre client n'est probablement pas associé à une transaction, l'appel de méthode avec TransactionAttributeType.Never est satisfait au départ. Ensuite, vous appelez la méthode process de processObjects qui, même si l'annotation TransactionAttributeType.Required n'était pas un appel à la méthode de bean et que la stratégie de transaction n'est pas appliquée . Lorsque vous appelez fusion , vous obtenez une exception, car vous n'êtes toujours pas associé à une transaction.

Essayez d’utiliser TransactionAttributeType.Required pour les deux méthodes de bean pour voir si cela fonctionne.

J'ai eu ces problèmes de dépendance circulaire dont Kevin a parlé. Cependant, l'annotation proposée @IgnoreDependency est une annotation spécifique à jboss et il n'y a pas d'équivalent dans, par exemple, Glassfish.

Comme cela ne fonctionne pas avec la référence EJB par défaut, je me suis senti un peu mal à l'aise avec cette solution.

C’est pourquoi j’ai laissé une chance à la solution de bluecarbon, démarrant ainsi la transaction interne "à la main".

À côté de cela, je ne vois pas de solution mais implémenter le processus interne () dans un autre bean qui est également moche, car nous voulons simplement perturber notre modèle de classe pour de tels détails techniques.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top