4. Базы данных
Для того, чтобы прочитать корректно символы из БД, обычно достаточно указать JDBC-драйверу на нужную кодировку символов в БД.
Как именно - это зависит от конкретного драйвера.
Сейчас уже многие драйвера поддерживают подобную настройку, в отличии от недавнего прошлого.
Далее приведены несколько известных мне примеров.
Мост JDBC-ODBC
Это один из самых часто используемых драйверов.
Мост из JDK 1.2 и старше можно легко настроить на нужную кодировку.
Это делается добавлением дополнительного свойства charSet в набор параметров, передаваемых для открытия соединения с базой.
По умолчанию используется file.encoding.
Делается это примерно так:
//Параметры соединения с базой
Properties connInfo=new Properties();
connInfo.put("user", username);
connInfo.put("password", password);
connInfo.put("charSet", "Cp1251");
//Устанавливаем соединение
Connection db=DriverManager.getConnection(dataurl, connInfo);
Драйвер JDBC-OCI от Oracle 8.0.5 под Linux
При получении данных из БД, данный драйвер определяет "свою" кодировку при помощи переменной окружения NLS_LANG.
Если эта переменная не найдена, то он считает что кодировка - ISO-8859-1.
Весь фокус в том, что NLS_LANG должна быть именно переменной окружения (устанавливаемой командой set), а не системное свойство Java (типа file.encoding).
В случае использования драйвера внутри servlet engine Apache+JServ, переменную окружения можно задать в файле jserv.properties:
wrapper.env=NLS_LANG=American_America.CL8KOI8R
Информацию об этом прислал
Сергей Безруков, за что ему отдельное спасибо.
Драйвер JDBC-thin от Oracle
Сей драйвер вроде как не требует особой настройки.
По крайней мере в документации об этом ни слова.
По всей видимости у Oracle в протоколе обмена ходит нормальный Unicode, правда за исключением составных типов (типы Object и Collection).
Если Вы пользуетесь сложными типами, то не забудьте про отдельный zip с поддержкой кодировок (с именем типа nls_charset12.zip), который скачивается отдельно.
В противном случае драйвер будет поддерживать только минимум (US7ASCII, WE8DEC, WE8ISO8859P1 и UTF8) и, если БД была создана в другой кодировке, то при получении строковых значений из составных типов будет сплошной 16-ричный мусор (если включён log у DriverManager, то при этом будет видна ругань на неизвестную кодировку).
Самая большая проблема, с которой многие сталкиваются - некорректная кодировка сообщений об ошибках.
Дело в том, что оригинальные драйвера от Oracle 8.1.7 и 9.0.1 содержат некорректно сформированные файлы ресурсов с текстами русских сообщений.
Драйвера от 9.0.2 и 9.2.x уже нормальные.
Эти файлы ресурсов можно довольно легко пропатчить при помощи утилиты native2ascii.
Готовый скрипт патча находится в файле
oracle_jdbc_repair.bat, его также можно найти здесь:
http://java.ksu.ru/sources/oracle/.
Можно также поменять текущий язык сессии выполнив команду
"ALTER SESSION SET NLS_LANGUAGE=English".
При этом сообщения станут выдаваться на английском.
Ну а самый правильный путь - использовать последние версии драйверов от 9.2.x, благо что они совместимы со старыми версиями Oracle и к тому же содержат исправления других ошибок.
Драйвер JDBC для работы с DBF
(com.hxtt.sql.dbf.DBFDriver, бывший zyh.sql.dbf.DBFDriver)
Данный драйвер только недавно научился работать с русскими буквами. Настройка выполняется немного по разному в зависимости от версии драйвера Версии Beta 5.4 (от 02.04.2002) и более поздние уже нормально воспринимают настройку charSet. В версиях Beta 5.2 (от 30.07.2001) и Beta 5.3 (30.11.2001), хоть он и сообщает по getPropertyInfo() что он понимает свойство charSet - это фикция. Реально же настроить кодировку можно установкой свойства CODEPAGEID. Для русских символов там доступны два значения - "66" для Cp866 и "C9" для Cp1251. Пример:
//Параметры соединения с базой
Properties connInfo = new Properties();
//Кодировка Cp866
connInfo.put("charSet", "Cp866");
connInfo.put("CODEPAGEID", "66");
//Устанавливаем соединение
Connection db=
DriverManager.getConnection("jdbc:DBF:/C:/MyDBFFiles",connInfo);
Если у Вас DBF-файлы формата FoxPro, то у них своя специфика. Дело в том, что FoxPro сохраняет в заголовке файла ID кодовой страницы (байт со смещением 0x1D), которая использовалась при создании DBF-ки. При открытии таблицы драйвер использует значение из заголовка, а не параметр "CODEPAGEID" (параметр в этом случае используется только при создании новых таблиц). Соответственно, чтобы всё работало нормально, DBF-файл должен быть создан с правильной кодировкой - иначе будут проблемы.
MySQL (org.gjt.mm.mysql.Driver)
С этим драйвером тоже всё довольно просто:
//Параметры соединения с базой
Properties connInfo = new Properties();
connInfo.put("user",user);
connInfo.put("password",pass);
connInfo.put("useUnicode","true");
connInfo.put("characterEncoding","KOI8_R");
Connection conn=
DriverManager.getConnection(dbURL, props);
InterBase (interbase.interclient.Driver)
Для этого драйвера работает параметр "charSet":
//Параметры соединения с базой
Properties connInfo=new Properties();
connInfo.put("user", username);
connInfo.put("password", password);
connInfo.put("charSet", "Cp1251");
//Устанавливаем соединение
Connection db=
DriverManager.getConnection(dataurl,connInfo);
Однако не забудьте при создании БД и таблиц указать кодировку символов.
Для русского языка можно использовать значения "UNICODE_FSS" или "WIN1251".
Пример:
CREATE DATABASE
'E:\ProjectHolding\DataBase\HOLDING.GDB'
PAGE_SIZE 4096
DEFAULT CHARACTER SET UNICODE_FSS;
CREATE TABLE RUSSIAN_WORD(
"NAME1" VARCHAR(40) CHARACTER SET UNICODE_FSS NOT NULL,
"NAME2" VARCHAR(40) CHARACTER SET WIN1251 NOT NULL,
PRIMARY KEY ("NAME1")
);
В версии 2.01 InterClient присутствует ошибка - классы ресурсов с сообщениями для русского языка там неправильно скомпилированы.
Скорей всего разработчики просто забыли указать кодировку исходников при компиляции.
Есть два пути исправления этой ошибки:
- Использовать interclient-core.jar вместо interclient.jar.
При этом русских ресурсов просто не будет, и автоматом подхватятся английские.
- Перекомпилировать файлы в нормальный Unicode.
Разбор class-файлов - дело неблагодарное, поэтому лучше воспользоваться JAD-ом.
К сожалению JAD, если встречает символы из набора ISO-8859-1, выводит их в 8-иричной кодировке, так что воспользоваться стандартным перекодировщиком native2ascii не удастся - придётся написать свой (программа Decode).
Если Вам не хочется заморачиваться с этими проблемами - можете просто взять готовый файл с ресурсами (пропатченый jar с драйвером - interclient.jar, отдельные классы ресурсов - interclient-rus.jar).
JayBird (org.firebirdsql.jdbc.FBDriver)
Для этого драйвера указание кодировки отличается от InterClient:
//Параметры соединения с базой
Properties connInfo=new Properties();
connInfo.put("user",username);
connInfo.put("password",password);
connInfo.put("lc_ctype","WIN1251");
//Устанавливаем соединение
Connection db=
DriverManager.getConnection(dataurl,connInfo);
SAP DB (com.sap.dbtech.jdbc.DriverSapDB)
Для этого драйвера можно включить использование Unicode задав параметр unicode в true.
В противном случае будет использованна жёстко зашитая ISO-8859-1, с вытекающими отсюда осточертевшими "??????".
Кроме того существует доработанная напильником версия (
sapdbc.zip), которая позволяет задавать параметр charSet.
DataSource (javax.sql.DataSource)
Если получение соединение производится через использование DataSource, то поддержку настройки используемой кодировки должна содержать конкретная реализация, которая регистрируется в JNDI.
Для примера можно взять реализацию от InterClient (interbase.interclient.DataSource).
В этом классе есть метод setCharSet(), используя который можно указать необходимую кодировку.
Пример:
interbase.interclient.DataSource dataSource=
new interbase.interclient.DataSource();
dataSource.setServerName("pongo");
dataSource.setDatabaseName("/databases/employee.gdb");
dataSource.setCharSet("Cp1251");
javax.naming.Context context=new javax.naming.InitialContext();
context.bind("jdbc/EmployeeDB",dataSource);
Другой пример - реализация от DBF-драйвера (com.hxtt.sql.HxttDataSource):
Properties prop=new Properties();
prop.setProperty("urlPrefix","jdbc:DBF:");
prop.setProperty("user","aaaaa");
prop.setProperty("password","vvvvccc");
prop.setProperty("database","dbffiles");
//Change it to yourdbfdir
prop.setProperty("charSet","Cp866");
DataSource dataSource=new com.hxtt.sql.HxttDataSource(prop);
context.bind(name, dataSource);
Но даже настроив JDBC-драйвер на нужную кодировку в некоторых случаях можно нарваться на неприятности.
Например, при попытке использования новых замечательных скролируемых курсоров стандарта JDBC 2 в мосте JDBC-ODBC из JDK 1.3.x довольно быстро можно обнаружить, что русские буквы там просто не работают (метод updateString()).
С этой ошибкой связанна небольшая история.
Когда я впервые её обнаружил (под JDK 1.3 rc2), я зарегистрил её на BugParade (
4335564).
Когда вышла первая бета JDK 1.3.1, эту ошибку пометили как пофиксеную.
Обрадованный, я скачал эту бету, запустил тест - не работает.
Написал Sun-овцам об этом - в ответ мне написали, что фикс будет включён в последующие релизы.
Ну ладно, подумал я, подождём.
Время шло, вышел релиз 1.3.1, а потом и бета 1.4.
Наконец я нашёл время проверить - опять не работает.
Мать, мать, мать... - привычно откликнулось эхо.
После гневного письма в Sun они завели новую ошибку (
4486195), которую отдали на растерзание индийскому филиалу.
Индусы пошаманили над кодом, и заявили, что в 1.4 beta3 всё исправлено.
Скачал я эту версию, запустил под ним тестовый пример, вот результат -
4546083.
Как оказалось, та beta3, которую раздают на сайте (build 84) это не та beta3, куда был включён окончательный фикс (build 87).
Теперь обещают, что исправление войдёт в 1.4 rc1...
Ну, в общем, Вы понимаете :-)