|
Здравствуйте, подскажите пожалуйста где можно достать материалы по курсу Кросс-платформенные и многозвенные технологии, о которых говориться, к примеру, в Лекции 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.






