Введение
Разумеется, что организовать доступ к базам данных из современного языка программирования в наше время не представляет никакой сложности. Более того, и сами языки программирования более всего оцениваются разработчиками по типу и возможностям заложенных в них средств доступа к базам данных, удобству и полноте интерфейсов. В этом смысле Java не представляет исключения. Уже в версии JDK1.1 появился пакет классов java.sql, обеспечивающий больщинство функций, известных к тому времени разработчикам ODBC-приложений. В этом пакете содержится ряд замечательных классов, например: java.sql.CallableStatement, который обеспечивает выполнение на Java хранимых процедур; java.sql.DatabaseMetaData, который исследует базу данных на предмет ее реляционной полноты и целостности с получением самых разнообразных данных о типах и содержимом таблиц, колонок, индексов, ключей и т.д.; наконец, - java.sql.ResultSetMetaData, с помощью которого можно выводить в удобном виде всю необходимую информацию из таблиц базы данных или печатать сами метаданные в виде названий таблиц и колонок.
Однако, коренное отличие Java от других традиционных языков программирования заключается в том, что одни и те же функции доступа к базам данных, с помощью универсальности и кроссплатформенности Java, можно организовать чрезвычайно гибко, используя все преимущества современных объектно-ориентированных технологий, WWW и Intranet/Internet. Рассмотрим по порядку все варианты использования Java-программ при взаимодействии с базами данных.
1. Java-программы и апплеты с интерфейсом JDBC-ODBC
JDBC (Java Database Connectivity) является не протоколом, а интерфейсом и основан на спецификациях SAG CLI (SQL Access Group Call Level Interface - интерфейс уровня вызова группы доступа SQL).
Сам по себе JDBC работать не может и использует основные абстракции и методы ODBC. Хотя в стандарте JDBC API и предусмотрена возможность работы не только через ODBC, а и через использование прямых линков к базам данных по двух- или трех-звенной схеме (см. Рис.1), эту схему используют гораздо реже, чем повсеместно используемый JDBC-ODBC-Bridge занимающий центральное место в общей схеме взаимодействия интерфейсов (см. Рис. 2)

Рис. 1. Непосредственный доступ к базе данных по 3-х-звенной схеме.

Рис. 2. Схема взаимодействия интерфейсов.
Даже беглого взгляда на Рис. 2 вполне достаточно, чтобы понять - общая схема взаимодействия интерфейсов в Java удивительным образом напоминает столь всем знакомую схему ODBC с ее гениальным изобретением драйвер-менеджера к различным СУБД и единого универсального пользовательского интерфейса. JDBC Driver Manager - это основной ствол JDBC-архитектуры. Его первичные функции очень просты - соединить Java-программу и соответствующий JDBC драйвер и затем выйти из игры. Естественно, что ODBC был взят в качестве основы JDBC из-за его популярности среди независимых поставщиков программного обеспечения и пользователей. Но тогда возникает законный вопрос - а зачем вообще нужен JDBC и не легче ли было организовать интерфейсный доступ к ODBC-драйверам непосредственно из Java? Ответом на этот вопрос может быть только однозначное нет. Путь через JDBC-ODBC-Bridge, как ни странно, может оказаться гораздо короче.
1. ODBC нельзя использовать непосредственно из Java, поскольку он основан на C-интерфейсе. Вызов из Java C-кода нарушает целостную концепцию Java, пробивает брешь в защите и делает программу трудно-переносимой.
2. Перенос ODBC C-API в Java-API нежелателен. К примеру, Java не имеет указателей, в то время как в ODBC они используются.
3. ODBC слишком сложен для понимания. В нем смешаны простые и сложные вещи, причем сложные опции иногда применяются для самых простых запросов.
4. Java-API необходим, чтобы добиться абсолютно чистых Java решений. Когда ODBC используется, то ODBC-драйвер и ODBC менеджер должны быть инсталлированы на каждой клиентской машине. В то же время, JDBC драйвер написан полностью на Java и может быть легко переносим на любые платформы от сетевых компьютеров до мэйнфреймов.
JDBC API - это естественный Java-интерфейс к базовым SQL абстракциям и, восприняв дух и основные абстракции концепции ODBC, он реализован, все-таки, как настоящий Java-интерфейс, согласующийся с остальными частями системы Java.
В отличие от интерфейса ODBC, JDBC организован намного проще. Главной его частью является драйвер, поставляемый фирмой JavaSoft для доступа из JDBC к источникам данных. Этот драйвер является самым верхним в иерархии классов JDBC и называется DriverManager. Согласно, установившимся правилам Internet, база данных и средства ее обслуживания идентифируются при помощи URL.
jdbc:где под
В некоторых случаях вместо ODBC может быть использовано имя прямого сетевого сервиса к базе данных, например:
jdbc:dcenaming:accounts-payable,или
jdbc:dbnet://ultra1:1789/state
В последнем случае часть URL //ultra1:1789/state представляет собой
Однако, как уже говорилось выше, чаще всего, все-таки используется механизм ODBC благодаря его универсальности и доступности. Программа взаимодействия между драйвером JDBC и ODBC разработана фирмой JavaSoft в сотрудничестве с InterSolv и называется JDBC-ODBC-Bridge. Она реализована в виде JdbcOdbc.class (для платформы Windows JdbcOdbc.dll) и входит в поставку JDK1.1. Помимо JdbcOdbc-библиотек должны существовать специальные драйвера (библиотеки), которые реализуют непосредственный доступ к базам данных через стандартный интерфейс ODBC. Как правило эти библиотеки описываются в файле ODBC.INI. На внутреннем уровне JDBC-ODBC-Bridge отображает медоды Java в вызовы ODBC и тем самым позволяет использовать любые существующие драйверы ODBC, которых к настоящему времени накоплено в изобилии.
Рассмотрим типичное приложение на Java c доступом к типичному реляционному серверу или даже к обычной dBase-таблице.
// Следующий код на Java используется как пример. Простой подстановкой
// соответствующих значений url, login, и password, и, затем подстановкой
// SQL операторов вы можете посылать их в базу данных.
//--------------------------------------
//
// Module: SimpleSelect.java
//
// Описание: Эта программа для ODBC API интерфейса. Java-приложение
// будет присоединяться к JDBC драйверу, посылать select оператор
// и показывать результаты в таблице
//
// Продукт: JDBC к ODBC Мост
//
// Автор: Karl Moss (С.Дунаев модификация для работы с кириллицей)
//
// Дата: Апрель 1997
//
// Copyright: 1990-1996
INTERSOLV, Inc.
// This software contains
confidential and proprietary
// information of INTERSOLV, Inc.
//--------------------------------------
import java.net.URL;
import java.sql.*;
import java.io.*;
class SimpleSelect {
public static void main (String args[]) {
String url = «jdbc:odbc:dBase»;
String query = «SELECT * FROM my_table»;
try {
// Загрузка jdbc-odbc-bridge драйвера
Class.forName («sun.jdbc.odbc.JdbcOdbcDriver»);
DriverManager.setLogStream(System.out);
// Попытка соединения с драйвером. Каждый из
// зарегистрированных драйверов будет загружаться, пока
// не будет найден тот, который сможет обработать этот URL
Connection con = DriverManager.getConnection (
url, «», «»);
// Если не можете соединиться, то произойдет exception
// (исключительная ситуация). Однако, если вы попадете
// в следующую строку программы, значит вы успешно соединились с URL
// Проверки и печать сообщения об успешном соединении
//
checkForWarning (con.getWarnings ());
// Получить DatabaseMetaData объект и показать
// информацию о соединении
DatabaseMetaData dma = con.getMetaData ();
//System.out.println(«\nConnected to « + dma.getURL());
//System.out.println(«Driver « +
//dma.getDriverName());
//System.out.println(«Version « +
//dma.getDriverVersion());
//System.out.println(«»);
// Создать Оператор-объект для посылки
// SQL операторов в драйвер
Statement stmt = con.createStatement ();
// Образовать запрос, путем создания ResultSet объекта
ResultSet rs = stmt.executeQuery (query);
// Показать все колонки и ряды из набора результатов
dispResultSet (rs);
// Закрыть результирующий набор
rs.close();
// Закрыть оператор
stmt.close();
// Закрыть соединение
con.close();
}
catch (SQLException ex) {
// Случилось SQLException. Перехватим и
// покажем информацию об ошибке. Заметим, что это
// может быть множество ошибок, связанных вместе
//
//System.out.println («\n*** SQLException caught ***\n»);
while (ex != null) {
//System.out.println («SQLState: « +
// ex.getSQLState ());
//System.out.println («Message: « + ex.getMessage ());
//System.out.println («Vendor: « +
//ex.getErrorCode ());
ex = ex.getNextException ();
//System.out.println («»);
}
}
catch (java.lang.Exception ex) {
// Получив некоторые другие типы exception, распечатаем их.
ex.printStackTrace ();
}
}
//----------------------------------
// checkForWarning
// Проверка и распечатка предупреждений. Возврат true если
// предупреждение существует
//----------------------------------
private static boolean checkForWarning (SQLWarning warn)
throws SQLException {
boolean rc = false;
// Если SQLWarning объект был получен, показать
// предупреждающее сообщение.
if (warn != null) {
System.out.println («\n *** Warning ***\n»);
rc = true;
while (warn != null) {
//System.out.println («SQLState: « +
//warn.getSQLState ());
//System.out.println («Message: « +
//warn.getMessage ());
//System.out.println («Vendor: « +
//warn.getErrorCode ());
//System.out.println («»);
warn = warn.getNextWarning ();
}
}
return rc;
}
//----------------------------------
// dispResultSet
// Показать таблицу полученных результатов
//----------------------------------
private static void dispResultSet (ResultSet rs)
throws SQLException, IOException
{
// Объявление необходимых переменных и
// константы для желаемой таблицы перекодировки данных
int i, length, j;
String cp1 = new String(«Cp1251»);
// Получить the ResultSetMetaData. Они будут использованы
// для печати заголовков
ResultSetMetaData rsmd = rs.getMetaData ();
// Получить номер столбца в результирующем наборе
int numCols = rsmd.getColumnCount ();
// Показать заголовок столбца
for (i=1; i<=numCols; i++) {
if (i > 1) System.out.print(«,»);
//System.out.print(rsmd.getColumnLabel(i));
}
System.out.println(«»);
// Показать данные, загружая их до тех пор, пока не исчерпается
// результирующий набор
boolean more = rs.next ();
while (more) {
// Цикл по столбцам
for (i=1; i<=numCols; i++) {
// Следующая группа операторов реализует функции перекодировки
// строк из таблицы базы данных в желаемый формат, потому что в
// различных базах символы могут быть закодированы произвольным
// образом. Если использовать стандартный метод - getString - на выходе
// получается абракадабра. Строки нужно сначала перевести в Unicode,
// затем конвертировать в строку Windows и убрать лидирующие нули
InputStream str1 = rs.getUnicodeStream(i);
byte str2[];
byte str3[];
int sizeCol = rsmd.getColumnDisplaySize(i);
str2 = new byte[sizeCol+sizeCol];
str3 = new byte[sizeCol+sizeCol];
length = str1.read(str2);
// Здесь нужно убрать нули из строки, которые предваряют каждый
// перекодированный символ
k=1;
for (j=1; j
В этой простой программе, приводимой во множестве руководств, мною произведено одно небольшое изменение, позволяющее использовать ее для работы с различными базами данных, содержащих таблицы с полями в кириллической кодировке. Дело в том, что хотя Java автоматически производит преобразования из Unicode и обратно в соответствии с установленными на вашей машине языковыми спецификациями (так называемые locale), эти преобразования не всегда действуют по отношению к кириллическим фонтам, особенно, когда кириллические строки прописаны не непосредственно в Java-программе, а передаются из внешних источников, например из баз данных через несколько промежуточных слоев. Та же проблема, как мы увидим далее, возникает и при использовании сервлетов, работающих в тесной взаимоувязке с Web-серверами.
2. Спец-ориентированные Java-приложения
2.1 RMI-приложения
Вызов удаленных методов (RMI - Remote Method Invocation) обеспечивает средства коммуникации между Java программами, даже если они выполняются на разных компьютерах, находящихся в противоположных точках земного шара.
Важная особенность RMI заключается в том, что он представляет программируемый интерфейс для работы с сетями в отличие от сокетов TCP. Главное преимущество его в том, что он предлагает вам интерфейс более высокого уровеня, основанный на вызовах методов, так, как если бы удаленный объект обрабатывался локально. RMI более удобен и более естественен, чем интерфейс, основанный на сокетах, но он требует выполнения Java-программ на обоих концах соединения. Сетевое соединение, тем не менеее, достигается использованием все того же TCP/IP протокола.
Рассмотрим основные шаги для построения работающего RMI-приложения:
- Разработка удаленных объектов и кодов для сервера и клиента
- Java-компиляция
- RMI-компиляция
- Перемещение .class-файлов в соответствующие директории
- Регистрация
- Старт сервера
- Старт клиента
Следующий ниже пример разработан для клиента и сервера, выполняющихся на одной и той же машине, однако, заложенные здесь принципы можно с успехом применить для программ, выполняющихся на разных машинах и даже с различной архитектурой. Программа клиента ищет имена и номера телефонов в центральной базе, постоянно резидентной на сервере.
Как работает RMI
Вы определяете Java-интерфейс, чтобы описать каждый объект, который будет дистанционно разделяем, и перечисляeте общие методы, которые могут быть вызваны для объекта. Сервер будет использовать RMI-интерфейс и создаст объекты для вызова, специальным образом зарегистрированные и доступные для вызова по URL-основанной схеме, например:
rmi://localhost/LookupServer
Клиент, используя эту запись, будет пытаться отыскать объект с данным именем, и получить удаленную ссылку к нему. Затем вызванный метод будет обработан с помощью RMI компилятора и преобразован из пользовательского кода в последовательную форму объекта, который передается пользователю с помощью TCP/IP.
Разработка удаленного объектного кода
RMI поддерживает объекты Java, осуществляющие связь через их методы, независимо от того, где эти объекты размещены. Первый шаг для создания класса, к которому можно обратиться дистанционно, - это определение интерфейса, описывающего методы, которые являются дистанционно-разделяемыми.
// Lookup.java
import java.rmi.*;
public interface Lookup extends Remote {
public String findInfo(String info) throws RemoteException;
}
Java.rmi.Remote - пустой интерфейс, который указывает, что это удаленный объект, - так объекты класса, выполняющие Поиск(«Lookup») отмечены удаленными ссылками. Все методы в удаленном интерфейсе должны быть объявлены через исключение типа Java.rmi.RemoteException, которое активизируется всякий раз, когда метод удаленного вызова дает сбои.
Разработка серверного кода
После того как вы определили интерфейс к удаленному объекту, нужно выполнить следующий шаг - разработать код сервера. Сервер осуществляет объектный интерфейс и создает образцы объекта, который будет дистанционно-разделяем.
Для нашего примера это будет выглядеть следующим образом:
// LookupServer.java
import java.io.*;
import java.util.*;
import java.rmi.*;
import java.rmi.server.*;
public class LookupServer extends UnicastRemoteObject
implements Lookup {
private Vector save = new Vector();
public LookupServer(String db) throws RemoteException
{
try {
FileReader fr = new FileReader(db);
BufferedReader br = new BufferedReader(fr);
String s = null;
while ((s = br.readLine()) != null)
save.addElement(s);
fr.close();
}
catch (Throwable e) {
System.err.println(«exception»);
System.exit(1);
}
}
public String findInfo(String info)
{
if (info == null)
return null;
info = info.toLowerCase();
int n = save.size();
for (int i = 0; i < n; i++) {
String dbs = (String)save.elementAt(i);
if (dbs.toLowerCase().indexOf(info) != -1)
return dbs;
}
return null;
}
public static void main(String args[])
{
try {
RMISecurityManager security =
new RMISecurityManager();
System.setSecurityManager(security);
String db = args[0];
LookupServer server = new LookupServer(db);
Naming.rebind(«LookupServer», server);
System.err.println(«LookupServer ready...»);
}
catch (Throwable e) {
System.err.println(«exception: « + e);
System.exit(1);
}
}
}
Сервер читает в текстовой базе номера телефонов и имена и сохраняет их внутренне. Метод findInfo ищет затем нужное имя и телефон.
Пример базы данных:
Ivanov, Ivan 295-0083
Petrov, Peter 775-9958
Romanov, Alexander 555-7779
Заметим, что LookupServer является расширением стандартного класса java.rmi.server.UnicastRemoteObject и выполняет Lookup. Один из этих классов обеспечивает некоторые базисные реквизиты, необходимые для удаленных объектов, а другой определяет методы, которые будут вызваны дистанционно.
Установка службы безопасности
Наиболее сложная часть этого кода - то, что происходит в процедуре main(). Первым делом нужно установить защиту. RMI принужден загружать удаленные .class файлы и в этом смысле напоминает какой-нибудь web-браузер с его операциями по загрузке апплетов, что само по себе всегда небезопасно. Если вы не установили защиту, то по умолчанию должны загружаться только локальные файлы, и RMI по определению не может работать с такими ограничениями. Так что вы должны установить security manager, чтобы сделать возможной загрузку удаленных .class файлов.
Образец LookupServer затем регистрируется с помощью службы Naming.rebind и становится доступным клиенту по имени.
Вы могли бы задаться вопросом, как удаленный метод фактически становится вызываемым, если сервер не содержит никакого сетевого кода и никаких TCP/IP примитивов? Это происходит за сценой, поскольку сервер и клиент используют так называемые скелетоны и стабы для коммуникации между собой. Соответствующие .class файлы генерируются из серверного .class файла через RMI транслятор, описанный ниже.
Концептуально, класс stub(заглушка) выглядит так:
public class LookupServer_Stub extends java.rmi.server.RemoteStub
implements Lookup, java.rmi.Remote { ... }
и скелетон - так:
public class LookupServer_Skel implements
java.rmi.server.Skeleton { ... }
Использование команды:
Javap -c LookupServer_Stub
будет показывать байт-код и иллюстрировать то, что происходит за сценой.
Стаб(stub) -это суррогат для удаленного объекта, и скелетон - некая сущность на сервере, которая обрабатывает удаленные вызовы.
Стаб обеспечивает функции приема передачи на стороне клиента, а скелетон - на стороне сервера. При этом производится преобразование объектов в последовательную форму, а проще говоря, в поток байтов, передаваемых с помощью протокола TCP/IP
Разработка клиентского кода
// LookupClient.java
import java.rmi.*;
import java.rmi.server.*;
public class LookupClient {
public static void main(String args[])
{
try {
RMISecurityManager security =
new RMISecurityManager();
System.setSecurityManager(security);
String host = «localhost»;
String server = «LookupServer»;
String name = «rmi://» + host + «/» + server;
Lookup look_obj = (Lookup)Naming.lookup(name);
String results = look_obj.findInfo(args[0]);
if (results == null)
System.err.println(«** not found **»);
else
System.out.println(results);
}
catch (Throwable e) {
System.err.println(«exception: « + e);
System.exit(1);
}
}
}
Если вы активизируете сервер, выполняя прямой клиентский запрос, то защита для пользователя определяется такая же как и на сервере. URL при этом определяется как:
Rmi://localhost/LookupServer
где localhost - имя локального компьютера (IP, адрес = 127.0.0.1), используемого как сервер. Клиент располагается на той же самой машине. Вы можете также использовать и удаленную главную ЭВМ. Когда вызов к нужному методу сделан, результаты незамедлительно передаются клиенту.
Компиляция кода
Три файла - Lookup.java, LookupServer.java, и LookupClient.java компилируются как и обычно в Java:
javac Lookup.java
javac LookupServer.java
javac LookupClient.java
Выполнение RMI компилятора
После того, как вы откомпилируете эти файлы, выполните RMI Compiler (rmic):
rmic LookupServer
чтобы получить LookupServer_Skel.class и LookupServer_Stub.class файлы.
Перемещение.class файлов в соответствующее им место на диске
Вы должны переместить клиентские файлы (Lookup.class, LookupClient.class, и LookupServer_Stub.class) в директорию, откуда вы их желаете выполнять как клиент.
Вы должны переместить серверные файлы (Lookup.class, LookupServer.class, LookupServer_Skel.class, и LookupServer_Stub.class) в директорию, где они станут доступными для публичного доступа.
Регистрация
Объект, к которому обращаются дистанционно, должен быть введен в регистр объектов, т.е. зарегистрирован. В JDK 1.1 имеется специальная программа
Rmiregistry
Rmiregistry может выполняться или в отдельном окне или как фоновый процесс на сервере.
Старт сервера
Вы должны стартовать сервер по команде:
java LookupServer database_name
К примеру, если ваша база данных с набором имен и телефонов находится в файле C:\PHONE.TXT, то вы должны дать команду:
java LookupServer C:\PHONE.TXT
Старт клиента
Клиентская программа стартуется по команде:
java LookupClient Ivanov
«Ivanov» - это искомое имя в базе.
Таким образом, RMI дает возможность создавать распределенные Java-to-Java прикладные программы, в которых методы удаленных объектов Java вызываются из других Java-программ на различных главных ЭВМ так как, если бы эти методы вызывались локально. Естественно, что подобные возможности можно эффективно использовать при работе с SQL-серверами, которые предоставляют Java-API интерфейс для доступа к данным. Например, в группе продуктов Informix в Informix Client SDK дано описание Informix Object Interface for Java, где приводятся многочисленные примеры, как организовать взаимодействующие RMI-приложения с доступом к базам данных Informix. Более того, имеется и соответствующий RMI-сервер, который содержит массу удобных и полезных методов, которые можно вызывать дистанционно. В сущности, приведенный выше пример можно приспособить для работы с любыми базами данных и SQL-серверами, если вы знаете каким образом устроен Java-API интерфейс для доступа к базам данных. В крайнем случае не возбраняется и использование JDBC в RMI-приложениях, хотя вряд ли это будет в достаточной степени эффективно. В Informix, например, для непосредственного взаимодействия с базами данных существуют два RMI-пакета: informix.api.remote.rmi - для удаленных клиентов и informix.api.remote.rmi.server - для rmi-сервера. При этом в клиентском приложении используется интерфейс к DBMSManager, который накапливает информацию обо всех серверах Informix и базах данных, и вы можете установить либо локальное, либо удаленное соединение с базой через RMI сервер. Для локального соединения создается DirectDBMSManager объект. Для удаленного соединения создается RMIDBMS-Manager объект и передается к соответствующему RMI серверу. Спецификация RMI сервера осуществляется в форме:
rmi://hostname[:port]/
//Создание DBMSManager объекта
DBMSManager getDBMSManager() throws Exception
{
// based on RMI Checkbox, get appropriate DBMSManager
DBMSManager dbmsManager;
if (RMIcheckbox.getState())
dbmsManager = new RMIDBMSManager(rmiServerTextField.getText());
else
dbmsManager = new DirectDBMSManager();
return dbmsManager;
Само взаимодействие с базой осуществляется с помощью специальных транзакционных методов и методов управления курсором, которые могут вызываться как локально, так и дистанционно. В различных базах и SQL-серверах это может осуществляться по разному, но если сервер кроссплатформенный - единый и универсальный Java-API интерфейс играет немаловажную роль.
2.1 Java, инкапсулированная в СУБД
Java может быть встроена в СУБД множеством различных способов и при этом всегда достигается решение сразу нескольких задач:
- Применение полноценного языка программирования для написания хранимых процедур. В сервер встраивается виртуальная машина Java и внутренний интерфейс JDBC. Встраивание мощного и безопасного языка программирования позволяет обойти ограничения хранимых процедур на языке SQL.
- Расширение объектных типов данных. Объекты, написанные на языке Java могут храниться в виде значений в реляционной таблице. Это позволяет создавать и использовать произвольные типы данных. Java-класс может быть использован в качестве типа данных для столбца таблицы. Каждое поле такой записи становится экземпляром соответствующего Java-класса. В качестве примера возьмем простой класс на языке Java, хранящий адреса. Прикладной код может быть включен в методы класса. Например, строковые данные могут содержаться только в полях Street и Postal Code(почтовый код), а значение поля City может вычисляться на основе почтового индекса. Более того, можно использовать наследование классов и методов. При этом допускается перегрузка методов в зависимости от используемого класса. Так можно создать классы US_Address и Rus_Address, унаследованные от базового класса Address. В каждом таком классе могут быть разные методы определения городов по почтовому индексу и методы проверки его значений, естественно разные для России и США. Даже сам SQL может использоваться для доступа к Java-объектам, как это уже сделано в Sybase Adaptive Server.
Следующий пример вставляет новую запись в таблицу:
INSERT INTO employees (id, name, Address)
VALUES(1789, «Serg Dunaev», new Rus_Address(«58 Gagarin Street», «153038»)
)
Следующий запрос с использованием Java-объектов возвращает список сотрудников, живущих на указанной улице:
SELECT name
FROM employees
WHERE Rus_Address.street="Gagarin Street"
- Единая программная модель. Впервые прикладные программные компоненты можно будет перемещать между клиентскими программами, серверами приложений и СУБД. Разработчики смогут использовать единую программную модель на всех уровнях информационной системы. Произвольная программа на Java состоит из набора классов. Для их использования необходимо провести инсталляцию Java-классов в СУБД. Классы должны быть откомпилированы в байт-код и тем самым, готовы для использования в любой виртуальной машине. В связи с использованием виртуальной машины для исполнения методов на Java и встроенной поддержке интерфейса JDBC, один и тот же объект на языке Java может быть использован как внутри, так и вне СУБД.
3. Java-сервлеты
Если апплеты расширяют функциональность Web-браузеров, то сервлеты расширяют функциональность Web-серверов и являются мощным средством программирования. В последнее время многие предпочитают обыкновенным апплетам, загружаемым локально или удаленно, именно сервлеты, которые не нужно никуда загружать и, которые всегда выполняются в контексте Web-сервера, обеспечивая, в отличие от обычных CGI-процессов или скриптов, куда более развитые возможности.
3.1 Сервлеты на базе Java Web Server
Сервлеты являются объектами, которые образуют специфический интерфейс, с помощью которого их можно свободно встраивать в Java-ориентированные Web-сервера. В отличие от апплетов, они являются облегченными Java-объектами (без графики и GUI-компонентов), но зато, будучи кроссплатформенными и динамически загружаемыми и, используя все возможности Java, они могут использовать практически любой HTML и взаимодействовать с любым браузером. Естественно, что сервлеты можно использовать и используют для доступа к базам данных, программируя все удаленные операции с базой из браузера. В настоящее время большинство Web серверов поддерживают технологию сервлетов. Java Web-Server хорош тем, что что он наиболее полно реализует эту технологию. Стандартный Servlet-API реализован как в самом Java Web Server, так и в отдельном продукте JSDK, но начиная с версии 1.2, он включен в состав JDK. Характерной особенностью сервлетов является то, что они не требуют создания новых процессов при каждом новом запросе. Множество сервлетов выполняются параллельно в рамках одного процесса на сервере и по своей производительности превосходят как CGI, так и Fast-CGI приложения (см. Рис.3).


Рис. 3. Возможные процессы на Web-сервере.
Несмотря на то, что сервлеты используют HTTP-протокол, им нет необходимости перезагружать процесс при каждом новом запросе и это также повышает их быстродействие. К сожалению, когда вы используете сервлеты, опять-таки, возникает проблема использования кириллических символов. Дело в том, что стандартный путь прохождения данных тут не действует, потому что Web-сервер при старте запускает так называемый Java-handler, которому передает на выполнение все Java-сервлеты и обменивается вводом-выводом именно с ним через специальную библиотеку классов. Поэтому, если вы хотите получать в браузере от вашего Web-сервера, который исполняет сервлеты, кириллические строки, вам надо использовать вместо стандартных примитивов ввода/вывода некоторые специальные методы для работы со строками, например:
// Определение необходимого кодификатора
// в зависимости от используемого типа кодировки
String dos = new String(«Cp866»);
String win = new String(«Cp1251»);
String iso = new String(«8859_5»);
String im = new String(«Кириллический текст»);
.......................................................
out.println(«»);
//out.write(im.getBytes(dos));
out.write(im1.getBytes(win));
//out.write(im2.getBytes(iso));
out.println(«