Здравствуйте, подскажите пожалуйста где можно достать материалы по курсу Кросс-платформенные и многозвенные технологии, о которых говориться, к примеру, в Лекции 2. Пример "Служба мгновенных сообщений" |
Технология Enterprise Java Beans. Часть 1
Пример "Конвертор валют с использованием базы данных"
Компонент
Разработаем компонент с функциональностью, похожей на функциональность компонента из предыдущего примера. Но теперь компонент будет брать информацию о курсах валют не из констант классов (как это было в предыдущем примере), а из базы данных. В качестве базы данных используется Oracle 9i.
Удаленный интерфейс
В удаленном интерфейсе (Remote Interface) определим главный метод, реализующий требуемую функциональность:
double convert(String curl, String cur2, double amount) throws RemoteException, CurrencyRateNotFoundException
Этот метод, получив на вход сумму в одной валюте, описываемой строкой cur1, переведет ее в сумму в другой валюте, описываемой строкой cur2. Если одна или обе из валют не найдены, то генерируется исключение CurrencyRateNotFoundException.
public interface DBCurrencyRemote extends EJBObject { public double convert(String cur1, String cur2, double amount) throws RemoteException, CurrencyRateNotFoundException; }
Домашний интерфейс
Домашний интерфейс (Home Interface) по сути дела ничем не отличается от домашних интерфейсов (Home Interface) компонентов из предыдущих примеров.
public interface DBCurrencyHome extends EJBHome { public DBCurrencyRemote create() throws RemoteException, CreateException; }
Создание таблицы в базе данных
Теперь необходимо создать таблицу курсов валют в базе данных, в которой будут храниться строки следующего вида: CUR1, CUR2, RATE. Строка уникальным образом определяется упорядоченной парой идентификаторов типов валют: ( CUR1, CUR2 ), то есть для двух заданных валют CUR1 и CUR2 может быть задан только один курс перевода RATE валюты CUR1 в валюту CUR2 (паре ( CUR2, CUR1 ) естественно, соответствует другой курс). Инфологическая модель этой простой базы данных представлена на Рис. 3.29. Создадим такую таблицу, и введем значения курсов для некоторых пар валют. Ниже приводится SQL-код,который создает необходимую таблицу и заполняет ее информацией.
create table CURRENCY_EXCHANGE_RATE ( CUR1 varchar(30), CUR2 varchar(30), RATE float, primary key (CUR1, CUR2) ); insert into CURRENCY_EXCHANGE_RATE (CUR1, CUR2, RATE) values ('EUR', 'USD', 1.3); insert into CURRENCY_EXCHANGE_RATE (CUR1, CUR2, RATE) values ('USD', 'EUR', 0.8); insert into CURRENCY_EXCHANGE_RATE (CUR1, CUR2, RATE) values ('EUR', 'RUR', 34.4); insert into CURRENCY_EXCHANGE_RATE (CUR1, CUR2, RATE) values ('USD', 'RUR', 26.5); insert into CURRENCY_EXCHANGE_RATE (CUR1, CUR2, RATE) values ('RUR', 'EUR', 0.33); insert into CURRENCY_EXCHANGE_RATE (CUR1, CUR2, RATE) values ('RUR', 'USD', 0.45);
Если таблица с таким названием уже существует в базе данных, то перед выполнением приведенного выше скрипта необходимо выполнить следующую SQL -команду:
drop table CURRENCY_EXCHANGE_RATE
Теперь необходимо выполнить этот скрипт в базе данных. Открываем приложение SQL*Plus,устанавливаемое вместе с компонентом Oracle Client СУБД Oracle 9i.Оно находится в меню "Пуск" в закладке Oracle - OraClient 9 -> Application Development -> SQL*Plus. Запускаем его. SQL*Plus предложит нам ввести имя пользователя, пароль и строку для связи с базой данных.
Если вход был выполнен удачно, то будет выведена консоль SQL*Plus,при помощи которой можно выполнять различные команды языка SQL (Рис. 3.31).
Теперь можно выполнить приведенный выше SQL -код в консоли. В результате будет создана и заполнена данными таблица CURRENCY_EXCHANGE_RATE (Рис. 3.32).
Программный интерфейс к базе данных
Доступ к базе данных будет осуществляться посредством объекта DBQuery (файл DBQuery.java ). В нем будет реализовано два основных метода:
- ResultSet selectQuery(String query). Этот метод позволяет выполнить некоторую выборку из базы данных. В качестве параметра передается SQL -запрос. К примеру - если необходимо из некоторой гипотетической базы данных выбрать информацию обо всех пользователей с именем "John", то при помощи этого метода запрос мог бы быть выполнен следующим образом: ResultSet set = query.selectQuery(" select * from USERS where NAME='John'") ;
- ResultSet selectQueryParam(String query, Object[] params). Этот метод также позволяет выполнить некоторую выборку из базы данных. В качестве первого параметра передается шаблон SQL -запроса, в котором некоторые параметры заменены символом '?'. В качестве второго параметра передается массив объектов (в качестве объектов обычно выступают строки, целые, а также вещественные числа). При вызове метода объект DBQuery подставит значения из массива объектов вместо знаков '?' в запросе. На место первого вхождения знака '?' будет подставлено значение первого объекта, и т.д. Количество знаков '?' в первом параметре должно равняться количеству объектов из массива во втором параметре.
Ниже приводится исходный код класса DBQuery.
package db; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class DBQuery { private Connection connection = null; private Statement statement = null; public DBQuery(String driver, String url, String username, String password) throws ClassNotFoundException, SQLException { Class.forName(driver); connection = DriverManager.getConnection(url, username, password); } public ResultSet selectQuery(String sql) throws SQLException { if (connection == null) { return null; } ResultSet r = null; statement = connection.createStatement(); r = statement.executeQuery(sql); return r; } public ResultSet selectQueryParam(String sql, Object[] params) throws SQLException { ResultSet r = null; PreparedStatement pst = connection.prepareStatement(sql); statement = pst; pst.clearParameters(); for (int i = 0; i < params.length; ш++) { pst.setObject(i + 1, params[i]); } r = pst.executeQuery(); return r; } public void close() throws SQLException { if (statement != null) { statement.close(); statement = null; } } public void finalize() { try { close(); } catch (SQLException e) { } } }
Помимо этого был создан класс-утилита, который несколько упрощает подключение к базам данных. При помощи его методов можно получать строку подключения к разным типам баз данных, а также получать имя класса драйвера базы данных. На данный момент реализация выполнена только для базы данных Oracle 9i.
package db; public class DBDriverUtilities { public static final int ORACLE = 1; /** *Creates the URL for the ORACLE database *@param host *@param dbName *@return */ public static String makeURL(String host, String dbName, int dbType) { if (dbType == ORACLE) { return "jdbc:oracle:thin:@" + host + ":1521:" + dbName; } else { return null; } } public static String getDriver(int dbType) { if (dbType == ORACLE) { return "oracle.jdbc.OracleDriver"; } else { return null; } } }
Подключение необходимого драйвера
Для того, чтобы можно было подключаться к базе данных Oracle 9i необходимо подключить библиотеку с соответствующим драйвером от Oracle.Она называется ojdbc14.jar и находится в каталоге jdbc\lib установочного каталога Oracle.Эту библиотеку нужно скопировать в каталог server\default\lib установочного каталога JBoss.Это необходимо для того, чтобы компонент, размещенный на сервере смог использовать данную библиотеку для связи с базой данных. Подключать библиотеку к проекту в Eclipse-WTP не обязательно, хотя может оказаться полезной следующая практика - создать небольшой Java -проект, подключить к нему библиотеку доступа к базе данных и отладить все запросы в этом небольшом проекте, так как отладка непосредственно компонента на сервере - более длительный по времени процесс.
Класс компонента
В классе компонента задан один метод, который производит выборку необходимого курса валют из базы данных. Если какая-либо из валют не найдена в базе данных, то генерируется исключение - CurrencyRateNotFoundException, которое в дальнейшем может быть обработано на стороне клиента. Выборка в базе данных производится при помощи уже реализованного класса DBQuery.
public class DBCurrencyBean implements SessionBean { private static final long serialVersionUID = 1858369246980240402L; private String username = "boris"; private String password = "quasimodo"; private String host = "localhost"; private String dbName = "EJBDB"; public void ejbCreate() { } public double convert(String cur1, String cur2, double amount) throws CurrencyRateNotFoundException { DBQuery query = createDatabaseQuery(); double rate = 0; try { ResultSet set =query.selectQueryParam ("SELECT RATE FROM " + "CURRENCY_EXCHANGE_RATE " + "WHERE CUR1=? AND CUR2=?", new Object[] {cur1, cur2}); if (set.next()) { rate = set.getDouble(1); } else { throw new CurrencyRateNotFoundException(cur1, cur2); } query.close(); } catch (SQLException e) { return -1; } return rate * amount; } private DBQuery createDatabaseQuery() { DBQuery query = null; int db = DBDriverUtilities.ORACLE; try { query = new DBQuery(DBDriverUtilities.getDriver(db), DBDriverUtilities.makeURL(host, dbName, db), username, password); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { System.err.println("Class library not found!!!"); } return query; } 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 { } }
Дескриптор развертывания
Дескриптор развертывания для этого компонента ничем принципиально не отличается от дескриптора развертывания предыдущего компонента.
<?xml version="1.0" encoding="UTF-8"?> <ejb-jar id="ejb-jar ID" version="2.1" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd"> <description> Third session bean </description> <display-name>DBCurrencyBean</display-name> <enterprise-beans> <session> <ejb-name>DBCurrencyBean</ejb-name> <home>dbCurrencyConvertorBean.DBCurrencyHome</home> <remote>dbCurrencyConvertorBean.DBCurrencyRemote</remote> <ejb-class> dbCurrencyConvertorBean.DBCurrencyBean </ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> </session> </enterprise-beans> </ejb-jar>
Клиентское приложение
Клиентское приложение работает аналогично клиентскому приложению из предыдущего примера, за исключением того, что обрабатывается исключение CurrencyRateNotFoundException, и в этом случае выводится сообщение Rate not found.
public class DBCurrencyConvertorClient extends JFrame implements ActionListener { private static final long serialVersionUID = -7439969561347013270L; private Context jndiContext; private JLabel cur1Label; private JTextField cur1; private JLabel cur2Label; private JTextField cur2; private JLabel amountLabel; private JTextField amount; private JButton getRate; private JLabel rate; public DBCurrencyConvertorClient() { super("CurrencyConvertor"); layoutPane(); try { jndiContext = createJBossContext(); } catch (NamingException e) { e.printStackTrace(); } } private DBCurrencyRemote getRemoteInterface() throws NamingException, CreateException, RemoteException { Object ref = jndiContext.lookup("DBCurrencyBean"); DBCurrencyHome home = (DBCurrencyHome) PortableRemoteObject.narrow(ref, DBCurrencyHome.class); DBCurrencyRemote remote = home.create(); return remote; } private void layoutPane() { GridBagConstraints gc = new GridBagConstraints(); gc.insets = new Insets(5, 5, 5, 5); Container content = getContentPane(); content.setLayout(new GridBagLayout()); cur1Label = new JLabel("Currency 1: "); gc.fill = GridBagConstraints.BOTH; gc.weightx = 1.0; gc.gridx = 0; gc.gridy = 0; gc.gridwidth = 2; gc.gridheight = 1; content.add(cur1Label, gc); cur2Label = new JLabel("Currency 2: "); gc.gridy = 1; content.add(cur2Label, gc); amountLabel = new JLabel("Amount: "); gc.gridy = 2; content.add(amountLabel, gc); getRate = new JButton("Get value: "); gc.gridy = 3; content.add(getRate, gc); getRate.addActionListener(this); cur1 = new JTextField(); gc.gridx = 2; gc.gridy = 0; content.add(cur1, gc); cur2 = new JTextField(); gc.gridy = 1; content.add(cur2, gc); amount = new JTextField(); gc.gridy = 2; content.add(amount, gc); rate = new JLabel("Not loaded"); gc.gridy = 3; content.add(rate, gc); } public void actionPerformed(ActionEvent ae) { loadExchangeRate(); } private void loadExchangeRate() { String currency1 = cur1.getText(); String currency2 = cur2.getText(); double am; try { am = Double.parseDouble(amount.getText()); } catch (NumberFormatException e) { rate.setText("Invalid number"); return; } try { DBCurrencyRemote remote = getRemoteInterface(); double res = 0; res = remote.convert(currency1, currency2, am); NumberFormat nf = NumberFormat.getInstance(); nf.setMaximumFractionDigits(2); rate.setText(nf.format(res)); } catch (CurrencyRateNotFoundException e) { rate.setText("Rate not found"); } catch (RemoteException e) { e.printStackTrace(); rate.setText("Error..."); } catch (NamingException e) { e.printStackTrace(); rate.setText("Error..."); } catch (CreateException e) { e.printStackTrace(); rate.setText("Error..."); } } public static void main(String args[]) { DBCurrencyConvertorClient client = new DBCurrencyConvertorClient(); client.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); client.setLocation(400, 400); client.setSize(300, 200); client.setVisible(true); } private 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; } }
Создание проектов
Два проекта - DBCurrencyConvertorBean и DBCurrencyConvertorBeanClient создаются аналогично проектам из предыдущего примера. Добавляем проект с компонентой к JBoss и запускаем JBoss.
Интерфейс клиентского приложения аналогичен интерфейсу из предыдущего примера (Рис. 3.34).
При возникновении CurrencyRateNotFoundException, оно обрабатывается и выводится сообщение Rate not found.