Подтвердить что ты не робот

Распространение транзакций EJB3

У меня есть безстоящий 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 (...), который фактически не взаимодействует с менеджером сущности. Он выполняет то, что ему нужно, и вызывает процесс (...) индивидуально для каждого обрабатываемого объекта. Продолжительность процесса (...) относительно короткая, но processObjects (...) может занять очень много времени, чтобы пробежать все. Поэтому я не хочу, чтобы он поддерживал открытую транзакцию. Мне нужны отдельные процессы (...) для работы в рамках их собственной транзакции. Это должна быть новая транзакция для каждого вызова. Наконец, я хотел бы оставить опцию открытой для того, чтобы клиент напрямую вызывал процесс (...).

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

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

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

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

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

4b9b3361

Ответ 1

Другой способ сделать это - фактически использовать оба метода в одном и том же 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 и сохраняя только один класс. Уф!

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

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

TransactionAttributeTypes учитываются только при пересечении границ Bean. При вызове методов в пределах того же Bean TransactionAttributeTypes эффект не влияет, независимо от того, какие типы помещаются в методы.

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

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

Ответ 5

Если кто-то наткнется на этот день:

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

@IgnoreDependency @EJB MySelf selfRef;

Ответ 6

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

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

Ответ 7

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

TransactionAttributeType.Never

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

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

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

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

Ответ 8

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

Так как он не работает с ссылкой EJB по умолчанию, мне было немного неудобно с этим решением.

Следовательно, я дал bluecarbon solution шанс, таким образом, начав внутреннюю транзакцию "вручную".

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