четверг, 15 мая 2008 г.

Java EE Transactions

Оригинал: http://java.sun.com/javaee/5/docs/tutorial/doc/bncih.html

Глава 33

Транзакции

Типичное enterprise приложение получает и хранит данные в одной или нескольких БД. Поскольку это информация критически важна для бизнес операций, она должна быть точной, достоверной и актуальной. Целосность данных может быть потеряна, если несколько приложений будут изменять данные одновременно. Целосность также будет нарушена, если данные останутся частично изменены ввиду какого-либо сбоя. Транзакции гарантируют целосность данных, предотвращая оба этих сценария. Транзакции контролируют совместный доступ к данным, а в случае сбоя гарантируют корректное восстановление данных.

Что такое транзакция?

Для моделирования бизнес-транзакции программе может потребоваться выполнить несколько шагов, например

начало транзакции
проверка состояния счета
обновление состояния счета
запись информации в лог
коммит транзакции

Либо все шаги должны завершиться коректно, либо ни один из них. Иначе целосность данных нарушится. Транзакция может завершиться двумя путями: коммитом, либо откатом (rollback).

Управляемые контейнером транзакции (Container-Managed Transactions)

В enterprise bean с container-managed transaction demarcation границы транзакций выставляет EJB контейнер. Вы можете использовать container-managed transactions как с session так и с message-driven bean. В этом случае разработка упрощается, поскольку Вы не имеете дело с транзакциями явно.

Обычно контейнер начинает транзакцию сразу после начала метода и коммитит непосредственно перед выходом из метода. Каждый метод ассоциирован с одной транзакцией, встроенные или множественные транзакции внутри метода не доступны.

Не во всех методах требеются транзакции, поэтому с помощью аттрибутов Вы можете настраивать необходимость использования транзакций.
Бины, не должны использовать методы, которые пересекаются с определением границ транзакций, например методы commit, setAutoCommit, rollback класса java.sql.Connection или commit, rollback класса javax.jms.Session. Если это необходимо, то используйте application-managed transaction demarcation.
Также нельзя использовать интерфейс javax.transaction.UserTransaction.

Аттрибуты транзакции

Аттрибуты транзакции определяют охват транзакции. На рис. 33-1 ставится вопрос: необходимо ли открывать новую транзакцию? Ответ зависит от аттрибутов method-B()

Рис. 33-1
A diagram showing a transaction between two beans.

Аттрибуты могут принимать значения:

  • Required

  • RequiresNew

  • Mandatory

  • NotSupported

  • Supports

  • Never

Сводная таблица

Table 33-1 В таблице 33-1 показано поведение контейнера исходя в различных ситуациях при различных значениях аттрибутов.

Таблица 33-1.

Значение аттрибута

Клиентская транзакция

Транзакция метода

Required

None

T2


T1

T1

RequiresNew

None

T2


T1

T2

Mandatory

None

error


T1

T1

NotSupported

None

None


T1

None

Supports

None

None


T1

T1

Never

None

None


T1

Error

Установка значения аттрибута

Аттрибуты определяюся с помощью анотации javax.ejb.TransactionAttribute, им присваивается одно из значений javax.ejb.TransactionAttributeType.

Можно аннотировать как отдельные методы, так и целые классы. Аннотация метода переопределяет аннотацию класса.
TransactionAttributeType содержит соответствующие значения:

  • Required: TransactionAttributeType.REQUIRED

  • RequiresNew: TransactionAttributeType.REQUIRES_NEW

  • Mandatory: TransactionAttributeType.MANDATORY

  • NotSupported: TransactionAttributeType.NOT_SUPPORTED

  • Supports: TransactionAttributeType.SUPPORTS

  • Never: TransactionAttributeType.NEVER

Ниже приведен пример использования аннотации

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

Откат Container-Managed Transaction

Есть два способа откатить container-managed transaction. Первый: контейнер автоматически откатывает транзакцию, если брошен системный exception (в случае application exception откат не произойдет). Второй - можно вызвать метод EJBContext.setRollbackOnly.

Synchronizing a Session Bean’s Instance Variables

Интерфейс SessionSynchronization позволяет stateful session бинам получать синхронизационные уведомлени. Контейнер будет вызывать соответствующие методы (afterBegin, beforeCompletion, and afterCompletion) в нужное время.

Недозволенные методы Container-Managed Transactions

Не следует пользоваться следующими методами:
  • commit, setAutoCommit, rollback класса java.sql.Connection

  • getUserTransaction класса javax.ejb.EJBContext

  • любыми методами javax.transaction.UserTransaction

Если они вам необходимы, пользуйтеся application-managed transactions.

Bean-Managed Transactions

При использовании bean-managed transaction demarcation, вы должны явно определять границы транзакции. Главное преимущество по сравнению с container-managed является возможность определять несколько транзакций в одном методе.

Ниже приведен псевдокод примера

begin transaction
...
update table-a
...
if (condition-x)
commit transaction
else if (condition-y)
update table-b
commit transaction
else
rollback transaction
begin transaction
update table-c
commit transaction
Во время кодирования application-managed transaction, необходимо решить что использовать: JDBC или JTA транзакции.

JTA транзакции

JTA позволяет управлять транзакциями независимо от реализации. Сервер приложений реализует управление транзакциями с помощью Java Transaction Service (JTS).

Преимущество JTA в том, что можно охватывать несколько баз данных различных вендоров. Однако, Java EE transaction manager имеет ограничение: встроенные (nested) транзакции не поддерживаются. Другими словами, нельзя создать транзакцию для экземпляра, пока предыдущая не закончена.

Для определения границ транзакции вызываются методы begin, commit, and rollbac интерфейса javax.transaction.UserTransaction.

Возврат без коммита

В stateless session бинах с bean-managed transactions метод должен закоммитить или откатить транзакцию до возврата значения. Но stateful session bean не имеет такой обязанности.
В stateful session bean ассоциация бина и транзакции сохраняется между вызовами.

Запрещенные методы в Bean-Managed Transactions

Не вызывайте методы getRollbackOnly and setRollbackOnly интерфейса EJBContext, вместо этого используйте getStatus и rollback интерфейса UserTransaction.

Таймаут транзакции

Длч container-managed transactions можно использовать Admin Console для определения таймаута транзакций.

  1. В Admin Console -> Configuration -> Transaction Service.

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

Для bean-managed используйте метод UserTransaction.setTransactionTimeout

Обновление нескольких баз данных

Бинам разрешается обновлять несколько баз данных в пределах одной транзакции. Ниже на рисунках показаны два возможных сценария.


На рис. 33-2 все обновления происходят в одной транзации.

Figure 33-2
A diagram showing Bean-A updating databases X and Y, and Bean-B updating database Z.


Во втором случае (рис. 33-3), transaction managers Java EE сервера также гарантирует, что все обновления произойдут в пределах одной транзакции.

Figure 33-3
A diagram showing Bean-A on one Java EE server updating database X, and Bean-B on another Java EE server updating database Y.


Транзакции в Web компонентах

Вы можете определять транзакции в web-компонентах использую java.sql.Connection или javax.transaction.UserTransaction. Использование аналогично использованию в session beans.

среда, 14 мая 2008 г.

Критерии

Столкнулся с выборкой данных по критериям.

Если необходимо отобрать объекты по нескольким полям, поступаем просто:
Session session = (Session) em.getDelegate();
Criteria criteria = session.createCriteria(Payment.class);

criteria.addOrder(Order.desc("paymentDate"));
criteria.add(Restrictions.eq("id", request.getPaymentId()));
criteria.add(Restrictions.ge("paymentDate", request.getSince()));
criteria.add(Restrictions.le("paymentDate", request.getTill()));

return criteria.list();
Все просто, все понятно.
Но если необходимо отобрать записи не по полю объекта, а по полю объекта, на который ссылается данный?

В этом случае правильное рещение выглядит так (допустим, что entity Payment имеет ссылку на entity Account)
criteria = criteria.createCriteria(Payment.class);

// добавляем отбор по полям класса Payment

// Опускаемся к объекту Account
criteria = criteria.createCriteria("Account");

// Добавляем отбор по полям Account
criteria.add(Expression.eq("accountNumber", 123));
Но если нужно сделать отбор по embedded полю, поступить следует так:
criteria.add(Restrictions.eq("account.accountNumber", 123));