Доступ к базам данных из Java-программ и проблемы русификации
Доступ к базам данных из Java-программ и проблемы русификации
С. Б. Дунаев
Введение
Разумеется, что организовать доступ к базам данных из современного языка программирования в наше время не представляет никакой сложности. Более того, и сами языки программирования более всего оцениваются разработчиками по типу и возможностям заложенных в них средств доступа к базам данных, удобству и полноте интерфейсов. В этом смысле 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. В случае применения ODBC, в URL-строку подставляется именно эта аббревиатура, а в качестве используется обычный DSN (Data Source Name), т.е. имя ODBC-источника из ODBC.INI файла. Например:
jdbc:odbc:dBase
В некоторых случаях вместо 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 1) System.out.print(ћ,Ћ);
//System.out.print(rsmd.getColumnLabel(i));
}
System.out.println(ћЋ);
// Показать данные, загружая их до тех пор, пока не исчерпается
// результирующий набор
boolean more = rs.next ();
while (more) {
// Цикл по столбцам
for (i=1; i
|