Вопрос

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

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

Я перепробовал несколько различных типов транзакций:никогда, не поддерживается, поддерживается (в processObjects) и требуется, требуется new (в 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 4.2.1.GA, но неспецифические ответы приветствуются / предпочтительны.

Это было полезно?

Решение

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

Другие советы

Мэтт, вопрос, который вы задаете, довольно классический, я думаю, что решение для самостоятельной ссылки от 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();
    }
}

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

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

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

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

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

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

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

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

@Игнорируемая зависимость @EJB Я сам, сам по себе.;

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

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

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

TransactionAttributeType.Никогда

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

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

Я предполагаю, что вы являетесь клиентом этого метода Обрабатываемые объекты из клиентского кода.Поскольку, вероятно, ваш клиент не связан с транзакцией, вызов метода с TransactionAttributeType.Никогда счастлив в первую очередь.Затем вы вызываете процесс способ из Обрабатываемые объекты это несмотря на то, что у TransactionAttributeType.Требуется аннотация не была вызовом метода bean, и политика транзакций не применялась.Когда ты позвонишь слияние вы получаете исключение, потому что вы все еще не связаны с транзакцией.

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

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

Поскольку это не работает со ссылкой EJB по умолчанию, я чувствовал себя немного неловко из-за этого решения.

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top