Доступ к базам данных из Javaпрограмм и проблемы русификации

       

Схема взаимодействия интерфейсов.



Рисунок 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::


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<sizeCol*2; j++) { if (str2[j] != 0) { str3[k]=str2[j]; k=k+1; } }

String str = new String(str3,cp1); System.out.print(str); } System.out.println(ы«);

// Загрузка следующего ряда в наборе

more = rs.next (); } }

}

В этой простой программе, приводимой во множестве руководств, мною произведено одно небольшое изменение, позволяющее использовать ее для работы с различными базами данных, содержащих таблицы с полями в кириллической кодировке. Дело в том, что хотя Java автоматически производит преобразования из Unicode и обратно в соответствии с установленными на вашей машине языковыми спецификациями (так называемые locale), эти преобразования не всегда действуют по отношению к кириллическим фонтам, особенно, когда кириллические строки прописаны не непосредственно в Java-программе, а передаются из внешних источников, например из баз данных через несколько промежуточных слоев. Та же проблема, как мы увидим далее, возникает и при использовании сервлетов, работающих в тесной взаимоувязке с Web-серверами.


Содержание раздела