Главная » Java, Web » Транзакции и серверные компоненты EJB

0

Что такое транзакция? Этот термин часто встречается при работе с базами данных. Этот термин часто используется при проведении различных банковских операций со счетами. Транзакция подразумевает под собой одну или несколько операций, объединяемых в один целый блок, когда выполнение одной операции зависит от результата выполнения другой операции. Все операции, включенные в транзакцию, составляют одно общее целое. Если транзакция проводится успешно, выполняются все условия, то есть все операции, составляющие транзакцию, будут выполнены. Если транзакция отклоняется, то никакие изменения не будут внесены ни в базы данных, ни в любые другие источники. Все операции транзакции, в случае отклонения транзакции, будут отменены. В частности, если после выбора товара по удовлетворяющей покупателя цене начинается выполнение транзакции, то во время проведения этой транзакции необходимо удостовериться в том, что во время проведения транзакции покупки цена на товар не изменится. Для этого все операции, связанные с покупкой, объединяются в одно целое, что и составляет транзакцию.

Предположим, что покупатель собирается купить некоторое количество акций компании МММ, этот покупатель имеет счет в банке и будет осуществлять покупку акций через банк, деньги будут сниматься с его банковского счета автоматически. Предположим, что он хотел бы купить 500 акций за $10. При обработке такого заказа банку потребуется проверять состояние счета покупателя во время покупки, так, чтобы не произошло чрезмерного снятия денег через банк или через банковский автомат во время проведения транзакции, после которого на счете могло бы остаться такое количество денег, которое было бы недостаточно для оплаты купленных акций. Все указанные действия составляют одну транзакцию. Если все проверки проходят успешно, то сделка совершается, баланс счета уменьшается на сумму стоимости купленных акций. Если же что-то не так, например, покупка акций привела бы к отрицательному значению баланса, а счет не позволяет кредитование, то транзакция будет отменена. Состояние баланса не будет изменено, он останется таким, как если бы никакие действия, связанные с транзакцией, не производились вовсе.

При работе с транзакциями в компоненте EJB BobAktsii нам потребуется имплементировать метод buy (), соответствующий покупке акций. При этом может образоваться множество запросов к базе данных. Например, к базе данных могут обратиться два независимых клиента практически в одно и то же время. При этом один покупатель обратиться к базе данных для определения цены акций, а к моменту покупки, то есть спустя несколько мгновений, содержимое базы данных уже будет изменено в связи с тем, что другой клиент также производит покупку этих акций, и минимальная цена на эти акции уже будет другой. Чтобы избежать ошибок в таких ситуациях, следует использовать транзакции. Приятным сюрпризом является то, что программист не должен обеспечивать все тонкости работы с транзакциями своими усилиями. Технология серверных компонентов EJB имеет встроенные возможности для работы с транзакциями. Транзакции можно организовать так, что в одной транзакции будут участвовать несколько серверных компонентов EJB и несколько баз данных, связанных с транзакцией ресурсов.

При выполнении транзакции наступает такой момент, когда необходимо (средствами программы) принять решение о том, следует ли принять транзакцию (commit), либо условия не позволяют принять транзакцию, тогда транзакция будет отменена (abort). В этот момент контейнер серверных компонентов EJB связывается с клиентом на предмет отмены транзакции. Если клиент не принимает решение об отмене транзакции (все условия транзакции при этом должны быть выполнены, решение только за клиентом), то транзакция будет принята. Если транзакция отклоняется, то все базы данных, в которые были бы внесены изменения в случае проведения транзакции, будут возвращены в исходное состояние.

Поддержка работы с транзакциями описывается в файле дескриптора размещения компонента EJB (описатель размещения), файле ejb-jar.xml. Для того чтобы указать, что данный метод работает в режиме транзакций, необходимо использовать элемент <trans-attribute>. Атрибут транзакций может принимать несколько значений (табл. 5.2).

Таблица 5.2. Значения атрибута транзакций (элемент<trans-attribute>)

Значение

Объяснение

NotSupported

В данном методе транзакции не поддерживаются

Supports

Этот метод может быть вызван как часть транзакции или как неза

 

висимый метод

Required

Этот метод может быть вызван только как часть транзакции. Если

 

методы вызывается вне транзакции, то автоматически создается

 

новая транзакция

RequiresNew

При вызове данного метода создается новая транзакция

Mandatory

Этот метод может быть вызван только в составе транзакции

Never

Этот метод не может быть использован в составе транзакции

Метод покупки buy () должен приводить к тому, что создается новая транзакция, то есть ему будет соответствовать значение RequiresNew.

В дескрипторе размещения транзакции описываются внутри элемента <assembiy-descriptor>. Существует несколько ярлыков описания транзакции. Элемент <assembiy-descriptor> находится на том же уровне, что и элемент <enterprise-beans> (листинг 5.21).

j Листинг 5.21. Пример описания транзакции                                                            j

<assembly-descriptor>

<container-transaction>

<method>

<ejb-name>BobA.ktsii</ejb-name> <method-name>buy</method-name> </method>

<trans-attribute>RequiresNew</trans-attribute> </container-transaction> </assembly-descriptor>

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

Как отменить транзакцию? Пусть, например, имеется компонент EJB BankBob, для возможности осуществления покупки акций удаленный интерфейс будет содержать метод buyAktsii ():

boolean buyAktsii(String name, float price, int number);

Этот метод в описателе размещения должен быть помечен как RequiresNew. Этот метод будет обращаться к компоненту EJB BobAktsii для проведения покупки. Если в процессе покупки выяснится, что покупка не может быть совершена, то компонент EJB BankBob сообщит контейнеру о том, что транзакцию следует отменить. Чтобы информировать контейнер об отмене транзакции, следует использовать интерфейс sessionsynchronization.

Вот как это можно сделать. В компонент EJB BankBob вставим переменную (boolean) fail. В имплементации метода buyAktsii о зададим значение переменной, равное false (иными словами, транзакция удалась). Затем производим изменение баланса счета, уменьшая его на сумму, уплаченную за покупаемые акции, это делается при помощи SQL-инструкции update, потом вызываем компонент EJB BobAktsii, который покупает акции. Если в процессе покупки акций компонентом EJB BobAktsii транзакция покупки не проходит, то изменяем значение переменной fail на новое значение true. После покупки возвращаем все неиспользованные деньги обратно на счет заказчика. В описание компонента EJB BankBob необходимо добавить implements j avax.ejb.SessionSynchronization.

Компонент EJB будет содержать следующие методы, основное внимание уделим методу beforeCompletion ():

public void afterBegin() throws javax.ejb.EJBException, j ava.rmi.RemoteException

{ }

public void beforeCompletion () throws javax.ejb.EJBException, j ava.rmi.RemoteException

{

if (fail) {

ejbSessionContext.setRollbackOnly();

fail = false; }

}

public void afterCompletion() throws javax.ejb.EJBException, j ava.rmi.RemoteException

{ }

В методе beforeCompletion () в случае отмены транзакции мы вызываем метод контекста компонента EJB setRollbackOnly (), который приводит к тому, что никакие изменения в базах данных не будет произведены, то есть базы данных останутся такими, как до транзакции.

Конечно, транзакции можно организовать и вручную, при этом следует описать тип транзакции в элементе <transaction-type> иным образом, а именно, вместо container написать веап. Для этого случая существует интерфейс j avax.transaction.UserTransaction.

В заключение скажем несколько слов о компонентах EJB-сущности, которые поддерживают свое долговременное существование собственными средствами. Такая ситуация может возникнуть тогда, когда требуется создать, например, особую конфигурацию при работе с базами данных, при работе с центральным сервером, которому передаются нестандартные данные, при работе с базами данных, которые не поддерживают JDBC. При этом создаются компоненты EJB, которые имеют тип поддержания своего жизненного цикла, называемый Bean-Managed Persistence (поддержка работы с базой данных средствами компонента EJB).

Сервер Bllizard создает следующие функции при работе с компонентами EJB-сущности, самостоятельно поддерживающими свой жизненный цикл: ejbCreate (), ejbPostCreate (), ejbLoadO, ejbStore(). Однако эти функции пусты, их необходимо имплементировать. Эти функции вызываются на разных этапах жизненного цикла компонента EJB. Функция ejbCreate (листинг 5.22) вызывается в ответ на запрос домашнего интерфейса о создании компонента EJB. Функция ejbPostCreate вызывается сразу после того, как экземпляр компонента EJB был создан. Функция ejbRemove вызывается тогда, когда необходимо удалить компонент EJB. Особое значение для поддержания жизненного цикла компонента EJB типа Bean-Managed Persistence

имеют функции ejbLoad (листинг 5.23) и ejbstore (листинг 5.24). Эти функции ответственны за чтение и сохранение данных, хранимых в источниках данных (не обязательно в базах данных). Кроме этого, в компонентах EJB с самостоятельной поддержкой жизненного цикла необходимо определить методы нахождения компонента EJB, соответствующие описанным в домашнем интерфейсе методам нахождения компонента EJB, при этом может потребоваться метод нахождения компонента EJB по первичному ключу (или набору ключей), список этих методов обязательно содержит по крайней мере ОДИН метод, а именно метод findByPrimaryKey.

Листинг 5.22. Метод ejbCreate

public java.lang.String ejbCreate(String key, String value) throws javax.ejb.CreateException, java.rmi.RemoteException

this.key = key; this, value = valued- boolean duplicateKey = false; try {

InitialContext ctx = new InitialContext();

DataSource ds = (DataSource)

ctx.lookup("java:comp/env/j dbc/NashiDannye");

Connection conn = ds.getConnection();

PreparedStatement stmt = conn.prepareStatement(

"SELECT * FROM PropsTable WHERE KEY=?");

stmt.setString(1, key);

ResultSet rs = stmt.executeQuery() ;

duplicateKey = rs.nextO;

rs.close();

stmt. close () ;

if (! duplicateKey) {

stmt = conn.prepareStatement(

"INSERT INTO NashaTablitsa (KEY,\"VALUE\") VALUES (?,?)");

stmt.setString(1, key);

stmt.setString(2, value);

stmt.executeUpdate() ;

stmt. close () ;

}

conn.close ();

catch (Exception ex) {

throw new java.rmi.RemoteException("ejbCreate Error", ex);

if (duplicateKey)

throw new javax.ejb.DuplicateKeyException(); return null;

Листинг 5.23. Метод ejbLoad

public void ejbLoad() throws java.rmi.RemoteException

boolean found = false; try {

InitialContext ctx = new InitialContext(); DataSource ds = (DataSource)

ctx.lookup("java:comp/env/j dbc/NashiDannye"); Connection conn = ds.getConnection(); PreparedStatement stmt = conn.prepareStatement( "SELECT \"VALUE\" FROM NashaTablitsa WHERE KEY= ?"); stmt.setString(1, key); ResultSet rs = stmt.executeQuery() ; if (rs.next()) { found = true; value = rs.getString(1);

rs.close(); stmt. close () ; conn.close ();

catch (Exception ex) {

throw new java.rmi.RemoteException("ejbLoad Error", ex);

if (! found)

throw new java.rmi.RemoteException("Bean not found"); Имплементация метода осуществляется похожим способом.

j Листинг 5.24. Фрагмент метода ejbstore

PreparedStatement stmt = conn.prepareStatement(

"UPDATE PropsTable SET \"VALUE\"=? WHERE KEY= ?");

stmt.setString(1, value);

stmt.setString(2, key);

if (stmt.executeUpdate() > 0)

found = true;

Источник: Будилов В. А. Интернет-программирование на Java. — СПб.: БХВ-Петербург, 2003. — 704 е.: ил.

По теме:

  • Комментарии