Опубликован: 18.03.2010 | Доступ: свободный | Студентов: 840 / 85 | Оценка: 4.48 / 4.33 | Длительность: 12:01:00
Лекция 3:

Технология 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;
}
Создание таблицы в базе данных
Мифологическая модель базы данных

Рис. 3.29. Мифологическая модель базы данных

Теперь необходимо создать таблицу курсов валют в базе данных, в которой будут храниться строки следующего вида: 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

увеличить изображение
Рис. 3.30. . Вход в SQL Plus

Теперь необходимо выполнить этот скрипт в базе данных. Открываем приложение SQL*Plus,устанавливаемое вместе с компонентом Oracle Client СУБД Oracle 9i.Оно находится в меню "Пуск" в закладке Oracle - OraClient 9 -> Application Development -> SQL*Plus. Запускаем его. SQL*Plus предложит нам ввести имя пользователя, пароль и строку для связи с базой данных.

Консоль SQL Plus

увеличить изображение
Рис. 3.31. Консоль SQL Plus

Если вход был выполнен удачно, то будет выведена консоль SQL*Plus,при помощи которой можно выполнять различные команды языка SQL (Рис. 3.31).

Необходимая таблица создана и заполнена данными

увеличить изображение
Рис. 3.32. Необходимая таблица создана и заполнена данными

Теперь можно выполнить приведенный выше 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.33. Компонент развернут на сервере
Получение информации о курсах валют

Рис. 3.34. Получение информации о курсах валют

Интерфейс клиентского приложения аналогичен интерфейсу из предыдущего примера (Рис. 3.34).

Курс не найден

Рис. 35. Курс не найден

При возникновении CurrencyRateNotFoundException, оно обрабатывается и выводится сообщение Rate not found.

Антон Зубеков
Антон Зубеков

Здравствуйте, подскажите пожалуйста где можно достать материалы по курсу Кросс-платформенные и многозвенные технологии, о которых говориться, к примеру, в Лекции 2. Пример "Служба мгновенных сообщений"