Pergunta

Eu tenho um feijão algo apátrida como:

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

O uso normalmente, em seguida, é o cliente chamaria processObjects (...), que na verdade não interagem com o gerente entidade. Ele faz o que precisa fazer e chama processo (...) individualmente para cada objeto para processo. A duração do processo (...) é relativamente curto, mas processObjects (...) poderia levar muito tempo para ser executado através de tudo. Portanto, eu não quero isso para manter uma transação aberta. I fazer precisa as operações de processo individual (...) para operar dentro de sua própria transação. Esta deve ser uma nova transação para cada chamada. Finalmente eu gostaria de manter o aberto opção para que o cliente processo de chamada (...) diretamente.

Eu tentei um número de diferentes tipos de transação:. Nunca, nem apoiado, suportado (em processObjects) e necessário, requer um novo (no processo), mas eu recebo TransactionRequiredException cada merge () é chamado

Eu tenho sido capaz de fazê-lo funcionar, dividindo-se os métodos em dois feijões diferentes:

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

mas ainda estou curioso para saber se é possível fazer isso em uma classe. Parece-me que o gerente de transação somente opera no nível de feijão, mesmo quando os métodos individuais são dadas anotações mais específicas. Então, se eu marcar um método em uma maneira de impedir a transação de iniciar chamar outros métodos dentro dessa mesma instância também não vai criar uma transação, não importa como eles são marcados?

Eu estou usando JBoss Application Server 4.2.1.GA, mas respostas específicas não são bem-vindos / preferido.

Foi útil?

Solução

Outra maneira de fazer isso é realmente ter ambos os métodos no mesmo bean - e ter uma referência @EJB para si! Algo assim:

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

Desta forma, você realmente 'força' o método process() para ser acessado via a pilha ejb de proxies, portanto, tomar o @TransactionAttribute em vigor - e ainda mantendo apenas uma classe. Ufa!

Outras dicas

Matt, a pergunta que você faz é um bem clássico, acho que a solução de auto-referência por Herval / Pascal é puro. Há uma solução mais geral não mencionados aqui.

Este é um caso para EJB transações "usuário". Como você está em um bean de sessão que você pode obter a transação do usuário a partir do contexto sessão. Veja como o seu código vai olhar com transações do usuário:

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

Eu acho que a coisa é cada grão é envolto em um proxy que controla o comportamento transacional. Quando você chamar a partir de um feijão para outro, você está indo via proxy que de feijão e o comportamento transação pode ser alterado pelo proxy.

Mas quando um feijão chama um método em si mesmo com um atributo de transação diferente, a chamada não vai através do proxy, de modo que o comportamento não muda.

Matt, por que vale a pena eu vim exatamente à mesma conclusão que você.

TransactionAttributeTypes só são levados em consideração ao cruzar fronteiras feijão. Ao chamar métodos dentro das mesmas TransactionAttributeTypes feijão não têm nenhum efeito, não importa quais os tipos são colocados sobre os métodos.

Tanto quanto eu posso ver que não há nada no EJB Persistência Spec que especifica o que o comportamento deve ser sob estas circunstâncias.

Eu também já experimentou isso em Jboss. Eu também vou dar-lhe uma tentativa em Glassfish e deixá-lo saber os resultados.

No caso de alguém se depara com isso um dia:

para evitar dependências circulares (permitindo auto referência, por exemplo) no JBoss usar a anotação 'IgnoreDependency' por exemplo:

@IgnoreDependency @EJB me myselfRef;

Eu não tentei ainda (eu estou prestes a), mas uma alternativa para a injeção de uma auto-referência via a anotação @EJB é o método SessionContext.getBusinessObject(). Esta seria uma outra maneira de evitar a possibilidade de uma referência circular explodir coisas em você -. Embora pelo menos para injecção feijão apátridas parece funcionar

Eu estou trabalhando em um grande sistema em que ambas as técnicas são empregadas (presumivelmente por diferentes desenvolvedores), mas não tenho certeza de qual é a maneira "correta" de fazê-lo.

Eu acho que tem a ver com o @TransationAttribute (TransactionAttributeType.Never) no método processObjects .

TransactionAttributeType.Never

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

Se o cliente está sendo executado dentro de um transação e invoca a empresa O método de feijão, o recipiente de uma lança RemoteException. Se o cliente não é associado a uma transação, o O recipiente não iniciar uma nova transação antes de executar o método.

Eu supor que você é cliente do método processObjects a partir do código do cliente. Porque, provavelmente, o seu cliente não está associado a uma transação a chamada de método com TransactionAttributeType.Never está feliz em primeiro lugar. Em seguida, você chamar o processo método de processObjects que apesar de ter o TransactionAttributeType.Required anotação não era uma política de transação feijão chamada de método e não é aplicada . Quando você chama merge você começa a exceção, porque você ainda não estão associadas a uma transação.

Tente usar TransactionAttributeType.Required para ambos os métodos de feijão para ver se ele faz o truque.

Eu tive esses problemas circulares de dependência que Kevin mencionados. No entanto, o @IgnoreDependency anotação proposto é uma anotação específica de jboss e não há contrapartida no exemplo Glassfish.

Uma vez que ele não funciona com referência EJB padrão, eu me senti um pouco desconfortável com esta solução.

Por isso, eu dei a solução da bluecarbon uma chance, começando assim a transação interna "à mão".

Ao lado disso, não vejo solução, mas para implementar o processo interno () em outro bean que também é feio porque nós simplesmente quer perturbar o nosso modelo de classe para esses detalhes técnicos.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top