Показаны сообщения с ярлыком javaee. Показать все сообщения
Показаны сообщения с ярлыком javaee. Показать все сообщения

четверг, 28 июля 2011 г.

Транзакции, управляемые приложением

В предыдущем посте я писал про транзакции, которые управляются контейнером EJB. Сейчас я продолжаю знакомство с транзакциями и выкладываю свои заметки про транзакции, управляемые приложением.

Транзакции, управляемые контейнером имеют одно ограничение - когда метод запускается, он может быть ассоциирован только с одной транзакцией или без транзакций вовсе. Если требуется иное, имеет смысл использовать транзакции, управляемые бином (bean-managed transactions).

Когда транзакции находятся под управлением приложения, можно использовать JDBC или JTA-транзакции.

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

Для управления транзакциями используются следующие методы: begin, commit и rollback. В JDBC-транзакции метод begin опускается, т.к. Подразумевается, что транзакция стартовала перед началом работы с БД.

Метод stateless session бина должен завершить транзакцию до того, как вернет результат, в то время как stateful session bean может сохранять состояние транзакции между обращениями клиента к методу. Даже если клиент закрывал и заново открывал соединение с БД, транзакция будет существовать до тех пор, пока не будет вызван метод commit или rollback.

Если stateful session bean использует JDBC-транзакцию, то её состояние так же может сохраняться между вызовами пользователя за тем исключением, что если jdbc-соединение будет закрыто - транзакция оборвется.

В транзакциях, управляемых приложением нельзя запускать методы управления транзакциями уровня контейнера getRollbackOnly и setRollbackOnly интерфейса EJBContext. Вместо этого надо использовать методы getStatus и rollback интерфейса UserTransaction.

Пример JDBC-транзакции:
public void ship (String productId, String orderId, int quantity) {
    try {
        con.setAutoCommit(false);
        updateOrderItem(productId, orderId);
        updateInventory(productId, quantity);
        con.commit();
    } catch (Exception ex) {
        try {
            con.rollback();
            throw new EJBException("Transaction failed:" +
            ex.getMessage());
        } catch (SQLException sqx) {
            throw new EJBException("Rollback failed:" +
            sqx.getMessage());
        }
    }
}
Пример JTA-транзакции:
public void withdrawCash(double amount) {
    UserTransaction ut = context.getUserTransaction();
    try {
        ut.begin();
        updateChecking(amount);
        machineBalance -= amount;
        insertMachine(machineBalance);
        ut.commit();
    } catch (Exception ex) {
        try {
            ut.rollback();
        } catch (SystemException syex) {
            throw new EJBException ("Rollback failed:" + syex.getMessage());
        }
        throw new EJBException ("Transaction failed:" + ex.getMessage());
    }
}

Таймаут транзакций под управлением EBJ-контейнера выставляется сервером приложений, а транзакций под управлением приложением - методом setTransactionTimeout интерфейса  UserTransaction. По умолчанию таймаут равен нулю, что значит, что он никогда не наступит.

Транзакции, управляемые контейнером EJB.

Enterprise beans поддерживают два типа управления транцакциями: container-managed или bean-managed. По умолчанию, если явно не задано, то используется первый, с которым я сегодня разбирался и хочу рассказать.

По сути, это просто заметки, сделанные с документации Java EE 6 Tutorial.

Container-managed транзакции упрощают разработку, т.к. за границами транзакции следит контейнер EJB. Транзакции, управляемые контейнером, могут работать как с session, так и с message-driven бинами.

Транзакция автоматически стартует перед началом запуска метода в бине и заканчивается по завершении этого метода. Container-managed транзакции не поддерживают вложенные и мульти-транзакции.

Container-managed транзакция не должна использовать методы управления транзакциями, т. к. эти операции возложены на контейнер EJB.

Существует возможность управления запуском транзакций для ejb-методов. Если внутри транцакции вызывается метод другого бина, то как должна вести себя транзакция с этим методом? На этот метод можно поставить различные ограничители принадлежности к текущей транзакции, при помощи аннотации @TransactionAttribute:
  • REQUIRED - если метод запущен внутри транзакции, то он является частью транзакции. Если запущен вне транзакции, то в нем стартует новая транзакция.
  • REQUIRES_NEW - если метод запускается внутри транзакции, то она останавливается, запускается новая, отрабатывается, а затем продолжает работу первая. Если метод запускается самостоятельно, то просто стартует новая транзакция. Данный модификатор служит для того, что бы быть уверенным, что всегда будет запущена новая транзакция.
  • MANDATORY - если метод запущен внутри транзакции, то он является её частью. Если же запускается вне транзакции, то бросается исключение TransactionRequiredException.
  • NOT_SUPPORTED - если метод запускается внутри транзакции, то на время работы метода транзакция останавливается, пока не отработается метод, травзакция в которм не стартуетс, затем транзакция продолжает работу. Если метод запускается вне транзакции, то транзакция в нем не стартует. Этот атрибут увеличивает производительность, т.к. транзакции связаны с дополнительными нагрузками.
  • SUPPORTS - если внутри транзакции, то является частью транзакции, если запускается самостоятельно, то транзакция не стартует.
  • NEVER - Если внутри транзакции, то бросается исключение RemoteException, если вне транзакции, то метод запускается без старта транзакции.
Аннотация  @TransactionAttribute, может быть установлена на весь класс или на отдельные методы.
Пример:
@TransactionAttribute(NOT_SUPPORTED)
@Stateful
public class TransactionBean implements Transaction {
    ...
    @TransactionAttribute(REQUIRES_NEW)
    public void firstMethod() {...}
    @TransactionAttribute(REQUIRED)
    public void secondMethod() {...}
    public void thirdMethod() {...}
    public void fourthMethod() {...}
}
Если во время работы транзакции бросается системное исключение, то автоматом происходит откат транзакции. Управление откатом при эксепшенах приложения задается в методе setRollbackOnly интерфейса EJBContext, который обрабатывается с REQUIRED, REQUIRES_NEW, или MANDATORY атрибутами транзакции. Если данный метод не будет запущен — транзакция завершится успешно:

public void transferToSaving(double amount) throws
InsufficientBalanceException {
    checkingBalance -= amount;
    savingBalance += amount;
    try {
        updateChecking(checkingBalance);
        if (checkingBalance < 0.00) {
            context.setRollbackOnly();
            throw new InsufficientBalanceException();
        }
        updateSaving(savingBalance);
    } catch (SQLException ex) {
        throw new EJBException
        ("Transaction failed due to SQLException: "
        + ex.getMessage());
    }
}
Session Beans поддерживают не обязательный интерфейс SessionSynchronization, который позволяет автоматически запускать методы на разных этапах выполнения транзакции: afterBegin, beforeCompletion, and afterCompletion. Первый может уведомлять сущность, что транзакция стартовала, второй запускается перед коммитом - последняя возможность сделать роллбек. Третий уведомляет, что транзакция выполнена и имеет только один булевый параметр со значением - true, если транзакция закоммичена или если роллбек - false

в container-managed транзакциях существуют запрещенные методы, т.к. могут пересекаться с границами текущей транзакции:
  • commit, setAutoCommit, и rollback методы класса java.sql.Connection
  • getUserTransaction метод класса javax.ejb.EJBContext
  • любой метод класса javax.transaction.UserTransaction