Распространение транзакции EJB3 - PullRequest
26 голосов
/ 20 сентября 2008

У меня есть bean без состояния что-то вроде:

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

Как правило, тогда клиент использует функцию processObjects (...), которая фактически не взаимодействует с менеджером сущностей. Он делает то, что ему нужно, и вызывает process (...) индивидуально для каждого объекта для обработки. Длительность процесса (...) относительно короткая, но для выполнения processObjects (...) может потребоваться очень много времени. Поэтому я не хочу поддерживать открытую транзакцию. Мне do нужны отдельные операции процесса (...) для работы в пределах их собственной транзакции. Это должна быть новая транзакция для каждого звонка. Наконец, я бы хотел оставить опцию открытой, чтобы клиент мог напрямую вызывать процесс (...).

Я пробовал несколько разных типов транзакций: никогда, не поддерживается, поддерживается (для processObjects) и требуется, требует нового (on process), но я получаю TransactionRequiredException при каждом вызове merge ().

Мне удалось заставить его работать, разделив методы на две разные компоненты:

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

но мне все еще любопытно, возможно ли сделать это в одном классе. Мне кажется, что менеджер транзакций работает только на уровне компонентов, даже когда отдельным методам даются более конкретные аннотации. Поэтому, если я помечу один метод таким образом, чтобы транзакция не начала вызывать другие методы в этом же экземпляре, это также не приведет к созданию транзакции, независимо от того, как они помечены?

Я использую JBoss Application Server 4.2.1.GA, но неспецифичные ответы приветствуются / предпочтительны.

Ответы [ 8 ]

24 голосов
/ 04 февраля 2009

Еще один способ сделать это на самом деле - иметь оба метода на одном и том же bean-объекте - и иметь ссылку @EJB на себя! Примерно так:

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

Таким образом, вы фактически «принудительно» получаете доступ к методу process() через стек прокси-серверов ejb, следовательно, берете в действие @TransactionAttribute - и при этом сохраняете только один класс. Уф!

4 голосов
/ 18 апреля 2010

Мэтт, вопрос, который вы задаете, является довольно классическим, я думаю, что решение Herval / Pascal для самообращения - это аккуратно Существует более общее решение, не упомянутое здесь.

Это случай для пользовательских транзакций EJB. Поскольку вы находитесь в сессионном компоненте, вы можете получить пользовательскую транзакцию из контекста сеанса. Вот как будет выглядеть ваш код с пользовательскими транзакциями:

// 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();
    }
}
2 голосов
/ 04 декабря 2008

Я думаю, что каждый бин обернут в прокси, который управляет поведением транзакций. Когда вы обращаетесь от одного компонента к другому, вы проходите через прокси этого компонента, и поведение транзакции может быть изменено прокси.

Но когда компонент вызывает сам метод с другим атрибутом транзакции, вызов не проходит через прокси, поэтому поведение не меняется.

1 голос
/ 11 апреля 2012

Я еще не пробовал (сейчас собираюсь), но альтернативой внедрению самореференции через аннотацию @EJB является метод SessionContext.getBusinessObject(). Это был бы еще один способ избежать возможности циклической ссылки взорвать вас - хотя по крайней мере для бобов без сохранения состояния кажется, что работает.

Я работаю над большой системой, в которой используются оба метода (предположительно, разными разработчиками), но я не уверен, какой это "правильный" способ сделать это.

1 голос
/ 07 мая 2010

На случай, если кто-нибудь встретит этот день:

, чтобы избежать циклических зависимостей (например, допуская самоссылку) в JBoss, используйте аннотацию 'IgnoreDependency', например:

@ IgnoreDependency @EJB MySelf себяRef;

1 голос
/ 04 декабря 2008

Мэтт, за то, что он стоит, я пришел к тому же выводу, что и вы.

TransactionAttributeTypes учитываются только при пересечении границ Бина. При вызове методов в одном и том же bean-компоненте TransactionAttributeTypes не действует, независимо от того, какие типы помещены в методы.

Насколько я вижу, в спецификации персистентности EJB нет ничего, что определяло бы поведение при этих обстоятельствах.

Я также испытал это в Jboss. Я также попробую в Glassfish и сообщу вам результаты.

0 голосов
/ 26 сентября 2011

У меня были проблемы с круговой зависимостью, о которых говорил Кевин. Тем не менее, предлагаемая аннотация @IgnoreDependency является аннотацией, специфичной для jboss, и ее нет, например, в Glassfish.

Так как он не работает с эталонной ссылкой EJB, я чувствовал себя немного некомфортно с этим решением.

Поэтому я дал шанс решению bluecarbon запустить внутреннюю транзакцию "вручную".

Кроме этого, я не вижу решения, кроме как реализовать внутренний процесс () в другом bean-компоненте, что также ужасно, потому что мы просто хотим помешать нашей модели класса для таких технических деталей.

0 голосов
/ 20 сентября 2008

Я думаю, что это связано с @ TransationAttribute (TransactionAttributeType.Never) для метода processObjects .

TransactionAttributeType.Never

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

Если клиент работает в транзакция и вызывает предприятие метод бина, контейнер бросает RemoteException. Если клиент не связанные с транзакцией, Контейнер не запускается новый транзакция перед запуском метода.

Я предполагаю, что вы являетесь клиентом processObjects из клиентского кода. Поскольку, вероятно, ваш клиент не связан с транзакцией, вызов метода с TransactionAttributeType.Never в первую очередь рад. Затем вы вызываете метод process из processObjects , который, хотя аннотация TransactionAttributeType.Required не была вызовом метода компонента, и политика транзакций не применяется. Когда вы звоните merge , вы получаете исключение, потому что вы все еще не связаны с транзакцией.

Попробуйте использовать TransactionAttributeType.Required для обоих методов bean-компонента, чтобы увидеть, справится ли он с этим.

...