Здравствуйте, подскажите пожалуйста где можно достать материалы по курсу Кросс-платформенные и многозвенные технологии, о которых говориться, к примеру, в Лекции 2. Пример "Служба мгновенных сообщений" |
Технология Enterprise Java Beans. Часть 1
Удаленный интерфейс OrdersListBean
В удаленном интерфейсе определим два метода:
- List getOrdersForUser(String username) throws RemoteException, SQLException. Этот метод возвращает список из объектов Order для пользователя с именем username.
- List getCommodityOrdersForOrder(int orderId) throws RemoteException, SQLException. Этот метод возвращает список из объектов CommodityOrder для заказа с ID = orderId.
public interface OrdersListRemote extends EJBObject { public List getOrdersForUser(String username) throws RemoteException, SQLException; public List getCommodityOrdersForOrder(int orderId) throws RemoteException, SQLException; }
Домашний интерфейс OrdersListBean
Этот интерфейс аналогичен домашним интерфейсам из предыдущих примеров.
public interface OrdersListHome extends EJBHome { public OrdersListRemote create() throws RemoteException, CreateException; }
Класс компонента OrdersListBean
Выполнение запроса к базе данных и формирование объектов Order и CommodityOrder выделено шрифтом.
public class OrdersListBean implements SessionBean { private static final long serialVersionUID = 2767604097827994556L; private DataSource dataSource = null; public List getOrdersForUser(String username) throws SQLException { if (dataSource == null) throw new SQLException("Datasource not loaded"); Connection conn = dataSource.getConnection(); PreparedStatement p = conn.prepareStatement ("SELECT * FROM " + " ORDERS WHERE USERNAME=?"); p.setString(1, username); ResultSet r = p.executeQuery(); List arrayList = new ArrayList(); while(r.next()) { Order order = new Order(); order.setId(r.getInt("ID")); order.setName(r.getString("USERNAME")); order.setDate(r.getDate("DATE_CREATED")); arrayList.add(order); } conn.close(); return arrayList; } public List getCommodityOrdersForOrder(int orderId) throws SQLException { if (dataSource == null) throw new SQLException("Datasource not loaded"); Connection conn = dataSource.getConnection(); PreparedStatement p = conn.prepareStatement ("SELECT * FROM " + " COMMODITY_ORDER WHERE ORDER_ID=?"); p.setInt(1, orderId); ResultSet r = p.executeQuery(); List arrayList = new ArrayList(); while(r.next()) { CommodityOrder order = new CommodityOrder(); order.setId(r.getInt("ID")); order.setCommodityId(r.getInt("COMMODITY_ID")); order.setCommodityQuantity(r.getInt("QUANTITY")); arrayList.add(order); } conn.close(); return arrayList; } public void ejbCreate() {} public void ejbActivate() throws EJBException, RemoteException {} public void ejbPassivate() throws EJBException, RemoteException {} public void ejbRemove() throws EJBException, RemoteException {} public void setSessionContext(SessionContext arg0) throws EJBException, RemoteException { try { InitialContext ctx = new InitialContext(); if (dataSource == null) dataSource = (DataSource) ctx.lookup("java:/OracleDS"); System.out.println("Data source aquired"); } catch (NamingException ex) { throw new EJBException(ex); } } }
Тестовый клиент для OrdersListBean
Для того чтобы проверить работоспособность компонента OrdersListBean, было написано тестовое консольное клиентское приложение. В нем выполнялся вызов обеих методов компонента, а результат выводился в консоль. Код этого клиента приводится далее.
public class TestClient3 { public static void main(String args[]) { try { Context jndiContext = createJBossContext(); Object ref = jndiContext.lookup("OrdersListBean"); OrdersListHome home = (OrdersListHome) PortableRemoteObject.narrow(ref, OrdersListHome.class); OrdersListRemote remote = home.create(); List orders = remote.getOrdersForUser("Boris"); System.out.println("Orders for user Boris"); for (Iterator iter = orders.iterator(); iter.hasNext();) { Order order = (Order) iter.next(); List commodityList =remote. getCommodityOrdersForOrder(order.getId()); System.out.println("Commodities for order " + order.getId() + " " + order.getDate().toString()); for (Iterator iter2 = commodityList.iterator(); iter2.hasNext();) { CommodityOrder cOrder = (CommodityOrder) iter2.next(); System.out.println(cOrder.getCommodityId() + " " + cOrder.getCommodityQuantity()); } } } catch (RemoteException e) { e.printStackTrace(); } catch (NamingException e) { e.printStackTrace(); } catch (CreateException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } public static Context createJBossContext() throws NamingException { Properties p = new Properties(); p.put("java.naming.factory.initial", "org.jnp.interfaces.NamingContextFactory"); p.put("java.naming.provider.url", "jnp://127.0.0.1:1099"); p.put("java.naming.factory.url.pkgs", "org.jboss.naming:org.jnp.interfaces"); Context jndiContext = new InitialContext(p); return jndiContext; } }
Вывод тестового клиента показан на Рис. 3.39.
Удаленный интерфейс ShoppingBasketBean
Интерфейс этого сеансового компонента с состоянием отражает те возможные опреации, которые пользователь может производить с корзиной. Эти методы перечислены далее:
- public List getCurrentCommoditiesList() throws RemoteException. Этот метод возвращает список из объектов CommodityOrder, соответствующих товарам в корзине.
- public boolean isEmpty() throws RemoteException. Этот метод возвращает булевское значение true, если корзина пуста.
- public addCommodity(int commodityId) throws RemoteException. Этот метод добавляет одну единицу товара с ID = commodityId в корзину.
- public recalculateCommodity(int commodityId, int newCount) throws RemoteException. Этот метод изменяет количество товаров с ID = commodityId в корзине на newCount.
- public deleteCommodity(int commodityId) throws RemoteException. Этот метод удаляет товар из корзины.
- public void processOrder() throws RemoteException, SQLException. Этот метод создает новый объект в таблице ORDERS базы данных, а затем добавляет записи с ORDERID, соответствущему только что добавленному заказу, в таблицу COMMODITYORDERS.
Как вы уже, наверное, успели убедиться, вызов каждого из этих методов может менять состояние компонента ShoppingBasketBean.
public interface ShoppingBasketRemote extends EJBObject { public List getCurrentCommoditiesList() throws RemoteException; public boolean isEmpty() throws RemoteException; public void addCommodity(int commodityId) throws RemoteException; public void recalculateCommodity(int commodityId, int newCount) throws RemoteException; public void deleteCommodity(int commodityId) throws RemoteException; public void processOrder() throws RemoteException, SQLException; }
Домашний интерфейс OrdersListBean
Этот интерфейс отличается от домашних интерфейсов предыдущих компонентов. Компонент ShoppingBasketBean создается отдельно для каждого пользователя, поэтому удобно в него передать имя пользователя. Имя пользователя будет использоваться при выполнении запроса в методе processOrder (). В метод create(String username) домашнего интерфейса передается имя пользователя.
public interface ShoppingBasketHome extends EJBHome { public ShoppingBasketRemote create(String username) throws RemoteException, CreateException; }
Класс компонента ShoppingBasketBean
Компонент ShoppingBasketBean должен постоянно поддерживать текущий список товаров. В классе компонента это делается посредством списка из объектов класса CommodityOrder. Все операции по добавлению, пересчету, и удалению товаров из списка текущих товаров в корзине сводятся к операциям поиска, удаления и добавления соответствующего объекта CommodityOrder в этом списке. Определен дополнительный вспомогательный метод private CommodityOrder findOrderById(int commodityId). Он ищет соответствующий объект в списке и возвращает либо его, либо значение null, если он найден не был. Код метода, осуществляющий поиск по commodityId представлен далее.
Iterator iter = commodityOrderList.iterator(); CommodityOrder order = null; while (iter.hasNext()) { CommodityOrder currentOrder = (CommodityOrder) iter.next(); if (currentOrder.getCommodityId() == commodityId) { order = currentOrder; break; } } return order;
Так как теперь компонент при создании должен получать имя пользователя, метод public void ejbCreate(String username) также несколько изменился. Теперь он сохраняет переданное имя пользователя во внутреннее поле String username, а также создает список, в котором в дальнейшем будет храниться текущий список товаров в корзине. Код этого метода представлен далее.
this.username = username; commodityOrderList = new ArrayList();
В методе public void processOrder() throws SQLException выполняется создание заказа в базе данных по товарам, которые есть в корзине на данный момент. Если в корзине нет товаром, то этот метод не делает ничего. В противном случае он сначала создает объект в таблице ORDERS посредством следующего запроса:
PreparedStatement p = conn.prepareStatement("insert into " "ORDERS(DATE_CREATED, USERNAME) values (?, ?)"); p.setDate(1,new java.sql.Date(System.currentTimeMillis())); p.setString(2,username); p.executeUpdate();
Теперь необходимо получить ID этого объекта. В Интернете много рекомендаций на тему того, что это необходимо делать путем выборки максимального ID из таблицы в базе данных.
select max(ID) from ORDERS where USERNAME = 'BORIS'
Не используйте этот подход! Во-первых, он будет работать только тогда, когда генерируемый индекс получается путем увеличения предыдущего, а во-вторых, если один пользователь попробует оформить два заказа одновременно, то может быть выбран ID другого оформляемого в этот момент заказа, и список товаров далее будет добавлен не к тому заказу.
Поэтому в базе данных был создан пакет, в который посредством триггера сохранялось значение поля ID последнего добавленного объекта в таблицу ORDERS. Стоит отметить, что пакеты в СУБД Oracle сохраняют свое состояние для каждой сессии (от создания подключения до его закрытия), поэтому можно не опасаться, что в пакете окажется ID другого заказа.
p = conn.prepareStatement("select ORDER_ID_PKG.GET_LAST_ID from dual"); ResultSet r = p.executeQuery(); int orderId = -1; if (r.next()) { orderId = r.getInt(1); }
То есть выбирается максимальное значение ID для текущего пользователя. Учитывая, что в нашем магазине не может быть создано двух корзин одновременно для одного пользователя, такое решение будет корректно работать.
Затем для каждого товара в корзине добавляется по записи в таблице COMMODITYORDER.
while (iter.hasNext()) { CommodityOrder currentOrder = (CommodityOrder) iter.next(); p = conn.prepareStatement("insert into COMMODITY_ORDER (ORDER_ID, " + "COMMODITY_ID, QUANTITY) values (?, ?, ?)"); p.setInt(1, orderId); p.setInt(2, currentOrder.getCommodityId()); p.setInt(3, currentOrder.getCommodityQuantity()); p.execute(); }
Метод void deleteCommdity(int commodityId) сводится к вызову метода recalculateCommodity(commodityId, 0).
Метод void addCommodity(int commodityId) вначале проверяет, есть ли товар с ID=commodityId в корзине, и либо добавляет новый объект CommodityOrder в список, либо просто увеличивает количество экземпляров в корзине товара на один.
CommodityOrder order = findOrderById(commodityId); if (order == null) { order = new CommodityOrder(); order.setCommodityId(commodityId); order.setCommodityQuantity(1); commodityOrderList.add(order); } else { order.setCommodityQuantity(order.getCommodityQuantity() + 1); }
Метод void recalculateCommodity(int commodityId,int newCount) изменяет количество экземпляров товара c ID=commodityId на newCount. Если newCount = 0, то товар удаляется из списка. Если товара не было в списке, то он туда добавляется.
CommodityOrder order = findOrderById(commodityId); if (order == null) { if (newCount > 0) { order = new CommodityOrder(); order.setCommodityId(commodityId); order.setCommodityQuantity(1); commodityOrderList.add(order); } } else { if (newCount > 0) { order.setCommodityQuantity(newCount); } else { commodityOrderList.remove(order); } }
Код класса компонента ShoppingBasketBean приводится далее.
public class ShoppingBasketBean implements SessionBean { private static final long serialVersionUID = 8173942031447022589L; private String username = null; private DataSource dataSource = null; private List commodityOrderList; public void ejbCreate(String username) { this.username = username; commodityOrderList = new ArrayList(); } private CommodityOrder findOrderById(int commodityId) { Iterator iter = commodityOrderList.iterator(); CommodityOrder order = null; while (iter.hasNext()) { CommodityOrder currentOrder = (CommodityOrder) iter.next(); if (currentOrder.getCommodityId() == commodityId) { order = currentOrder; break; } } return order; } public List getCurrentCommoditiesList() { return commodityOrderList; } public boolean isEmpty() { return (commodityOrderList.size() == 0); } public void addCommodity(int commodityId) { CommodityOrder order = findOrderById(commodityId); if (order == null) { // Creating new order order = new CommodityOrder(); order.setCommodityId(commodityId); order.setCommodityQuantity(1); commodityOrderList.add(order); } else { order.setCommodityQuantity(order.getCommodityQuantity() + 1); } } public void recalculateCommodity(int commodityId, int newCount) { CommodityOrder order = findOrderById(commodityId); if (order == null) { if (newCount > 0) { // Creating new order order = new CommodityOrder(); order.setCommodityId(commodityId); order.setCommodityQuantity(1); commodityOrderList.add(order); } } else { if (newCount > 0) { order.setCommodityQuantity(newCount); } else { commodityOrderList.remove(order); } } } public void deleteCommodity(int commodityId) { recalculateCommodity(commodityId, 0); } public void processOrder() throws SQLException { if (dataSource == null) throw new SQLException("Datasource not loaded"); Connection conn = dataSource.getConnection(); if (commodityOrderList.size() == 0) return; PreparedStatement p = conn.prepareStatement ("insert into " + "ORDERS(DATE_CREATED, USERNAME) " + "values (?, ?)"); p.setDate(1, new java.sql.Date(System.currentTimeMillis())); p.setString(2, username); p.executeUpdate(); p = conn.prepareStatement("select max(ID) from ORDERS where " + "USERNAME = ?"); p.setString(1, username); ResultSet r = p.executeQuery(); int orderId = -1; if (r.next()) { orderId = r.getInt(1); } Iterator iter = commodityOrderList.iterator(); while (iter.hasNext()) { CommodityOrder currentOrder = (CommodityOrder) iter.next(); p = conn.prepareStatement ("insert into COMMODITY_ORDER (" + " ORDER_ID, COMMODITY_ID, QUANTITY) " + "values (?, ?, ?)"); p.setInt(1, orderId); p.setInt(2, currentOrder.getCommodityId()); p.setInt(3, currentOrder.getCommodityQuantity()); p.execute(); } commodityOrderList.clear(); conn.close(); } public void ejbActivate() throws EJBException, RemoteException {} public void ejbPassivate() throws EJBException, RemoteException {} public void ejbRemove() throws EJBException, RemoteException {} public void setSessionContext(SessionContext arg0) throws EJBException, RemoteException { try { InitialContext ctx = new InitialContext(); if (dataSource == null) dataSource = (DataSource) ctx.lookup("java:/OracleDS"); System.out.println("Data source aquired"); } catch (NamingException ex) { throw new EJBException(ex); } } }