barbitoff programmer`s blog

Здесь я публикую заметки из программерской жизни: грабли, на которые мне случилось наступить, проблемы, для которых было найдено элегантное (или не очень) решение, а также все, с чем мне пришлось столкнуться и чем хотелось бы поделиться =)
PS Если хотите меня поблагодарить - на странице есть 3 места, чтобы это сделать =)

суббота, 24 сентября 2011 г.

Установка кодировки JDBC-ODBC

Установка настройки соединения для JDBC-ODBC моста осуществляется через Properties при создании соединения:
java.util.Properties connInfo = new java.util.Properties();
connInfo.put("charSet", "Cp1251");
connInfo.put("user", login);
connInfo.put("password", pass);
conn = DriverManager.getConnection(URL,connInfo);
Установленная таким образом кодировка повлияет на все символьные данные, получаемые / передаваемые с помощью этого соединения (в частности на то, какую кодировку будет использовать метод ResultSet.getString() при формировании строки).

Java - преобразование строки из одной кодировки в другую

Пример: преобразование строки oldString в кодировке UTF-8 в строку newString в кодировке cp1251:

String newString = new String(oldString.getBytes("UTF-8"), "Cp1251");

пятница, 23 сентября 2011 г.

Transbase ODBC Driver и ошибка при извлечении NUMERIC данных с помощью JDBC-ODBC моста методом getObject

Проблема: 
При попытке извлечь данные из ячейки столбца, имеющего тип "NUMERIC" с помощью метода getObject объекта ResultSet (или просто при попытке просмотра данных в таблице БД Transbase с помощью вкладки "Services" в Netbeans, который видимо тоже при извлечении данных пользуется методом ResultSet.getObject) вылетает исключение вида:

java.sql.SQLException: [Transaction][ODBC Transbase Driver][TEST_DB] unknown: FieldId=5 ???

В логах ODBC-драйвера трансбэйза та же ошибка, однако причина её всё равно не очевидна:


[ODBC Transbase Driver][E:\utmp\seibt\tbodbc3x\trunk\api\sqlcolattribute.c][SQLColAttribute][419]unknown: FieldId=5 ???
[ODBC Transbase Driver][E:\utmp\seibt\tbodbc3x\trunk\api\sqlcolattribute.c][SQLColAttribute][431]Stmt=$0075F200 / Col=27 / FieldId=5 [SQL_DESC_???] <- 0 (len:0 / max: 0)

Решение:
Если тип столбца - "NUMERIC", для извлечения данных использовать метод getFloat вместо универсального getObject (в примере ниже resMD - объект типа ResultSetMetaData, полученный вызовом sourceDataRS.getMetaData(), а sourceDataRS - собственно ResultSet, из которого получаются данные):

 if(resMD.getColumnType(i)==Types.NUMERIC)
row[i] = sourceDataRS.getFloat(i);
else
row[i] = sourceDataRS.getObject(i);

четверг, 22 сентября 2011 г.

cmd: копирование папки с подпапками

Команда copy такого делать не умеет, однако есть команда xcopy, позволяющая копировать содержимое папки со всеми поддиректориями с помощью ключа /E:

xcopy /E "C:\source_dir" "F:\dest_dir\"


Перехват вывода программы в переменную в bat-скрипте

В линуксе такая банальная задача решается элементарно (MYVAR=`COMMAND`, т.е. просто заключением команды в обратные кавычки). В Win я пока не нашел более простого способа чем этот (спасибо http://axisful.me/cactus/cmd/get-cmd-output-in-var):

for /F "tokens=*" %%i in ('COMMAND') do set MYVAR=%%i

воскресенье, 18 сентября 2011 г.

OLE DB provider "MSDASQL" for linked server returned message "[MySQL][ODBC 5.1 Driver][mysqld-5.5.8-log]Commands out of sync; you can't run this command now"

Проблема:
При попытке выполнить массовую вставку данных в таблицу MySQL в БД, подключенной к MS SQL Server 2008 по ODBC (например, конструкцией INSERT INTO ... SELECT ... ), возникает ошибка:
OLE DB provider "MSDASQL" for linked server "MY_LINKED_SERVER" returned message "[MySQL][ODBC 5.1 Driver][mysqld-5.5.8-log]Commands out of sync; you can't run this command now"
Решение:
Отключить опцию  "Don't cache results of forward-only cursors" в настройках System DSN ODBC-источника данных MySQL (или убрать "OPTION=1048576" из строки подключения к связанному серверу, если подключение производится не через System DSN).

суббота, 17 сентября 2011 г.

Подключение MySQL к MS SQL Server 2008 по ODBC

Процесс подлючения MySQL по ODBC в качестве Linked Server к MS SQL Server аналогичен тому, который я уже описывал (http://barbitoff.blogspot.com/2011/09/odbc-linked-server-ms-sql-2008.html), правда с несколькими замечаниями:
1) Для оптимизации производительности и повышения стабильности при создании System DSN нужно зайти в Details, и там установить галочки (сам различные варианты настроек не сравнивал, воспользовавшись рекомендациями отсюда http://www.ideaexcursion.com/2009/02/25/howto-setup-sql-server-linked-server-to-mysql/):
  • Connection->Allow Big Result Sets
  • Connection -> Use Compression 
  • Cursors/Results ->  Force use of forward-only cursors
Опцию "Cursors/Results -> Dont't cache results of forward-only cursors" устанавливать не следует, т.к. это приведет к ошибкам при массовых INSERT`ах (см. http://barbitoff.blogspot.com/2011/09/ole-db-provider-msdasql-for-linked.html).
2) Перед созданием Linked Server в MS SQL Server Management Studio зайти в Server Objects -> Linked Servers -> Providers -> Кликнуть правой кнопкой мыши по MSDASQL и зайти в Properties, где установить следующие галочки:
  • Nested queries
  • Level zero only
  • Allow inprocess
  • Supports ‘Like’ Operator
Такая настройка провайдера MSDASQL  отразиться на всех серверах, подключенных по ODBC, что конечно не совсем удобно. Однако без этих параметров запросы к MySQL выполняться не будут. Кстати, такая настройка провайдера подходит и для ODBC-подкючения к Transbase.

Именование таблиц связанного сервера при выполнения SQL-запросов будет следующее: LINKED_SERVER_NAME...TABLE_NAME, т.е. между именем связанного сервера и именем таблицы ставится три точки, например:

SELECT TOP 10 * FROM MYSQL_SERVER...BRANDS



вторник, 13 сентября 2011 г.

Подключение ODBC Linked Server к MS SQL 2008

Чтобы подключить к MS SQL Server связанный сервер (linked server) по ODBC (при отсутствии необходимого OLE-провайдера это - единственный остающийся вариант) нужно:

1) Добавить System DSN (Data Source Name):

Панель управления -> Администрирование -> Источники данных (ODBC) -> Системный DSN -> Добавить, где выбираем нужный ODBC-драйвер и настраиваем параметры соединения с БД

2) На SQL-сервере выполнить запрос:

EXEC sp_addlinkedserver @server='MY_LINKED_SERVER_NAME', @srvproduct='SOMETHING_U_WANT', @provider='MSDASQL', @datasrc='SYSTEM_DSN_NAME'
Также можно выполнить подключение сервера и без использования System DSN, а с помощью Connection String, как, можно прочитать тут: http://msdn.microsoft.com/en-us/library/ms190479.aspx

Используя ODBC, можно, в частности, прицепиться к MySQL-серверу (скачав ODBC-драйвер тут: http://www.mysql.com/products/connector/, тонкости настройки подключения я описал тут: http://barbitoff.blogspot.com/2011/09/mysql-ms-sql-server-2008-odbc.html).

четверг, 8 сентября 2011 г.

XPath: определение возраста

if($birthDate='') 
	then '' 
	else year-from-date(xs:date(local-date()))-year-from-date($birthDate)
		-(if(month-from-date($birthDate) > month-from-date(xs:date(local-date())))
			then 1
			else (if(month-from-date($birthDate) < month-from-date(xs:date(local-date())))
					then 0
					else (if(day-from-date($birthDate) > day-from-date(xs:date(local-date())))
							then 1
							else 0
						)
				)
			)
Возможно, можно как-то сделать это проще, но я пока способа не нашел. Вычитание дат дает результат типа xs:dayTimeDuration вместо xs:yearMonthDuration, из которого было бы удобно извлечь число лет. А вот по числу дней число лет определить очень сложно из-за високосных годов.

вторник, 6 сентября 2011 г.

XPath: проверка, что между указанной датой и текущей прошло меньше определенного количества лет

$dateToCheck + xs:yearMonthDuration('P120Y0M') > xs:date(local-date())- проверяет, что с даты $dateToCheck (типа xs:date) до текущей локальной даты прошло не более 120 лет. Если   $dateToCheck - строка, то нужно сделать xs:date($dateToCheck). 

Регулярное выражение для проверки e-mail

^[_A-Za-z0-9-]+(\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(\.[A-Za-z]{2,})$
Допускает email-адреса:
  • abc@abc.com
  • abc.abc@abc.com
  • abc.abc@abc.abc.com
  • abc-abc_abc-1999@abc-abc.abc-abc.abc-abc99.com
Некорректные адреса, подходящие с точки зрения этого шаблона (надо будет поправить):
  •  -abc@abc.com
  • _abc@abc.com
  • abc@-abc.com
  • abc@abc---.com 

четверг, 1 сентября 2011 г.

java.lang.ClassCastException: org.apache.xerces.parsers.XIncludeAwareParserConfiguration cannot be cast to org.apache.xerces.xni.parser.XMLParserConfiguration

Проблема:
при попытке деплоя веб-сервиса на tomcat вываливается исключение типа: "com.sun.xml.ws.transport.http.servlet.WSServletException: WSSERVLET11: failed to parse runtime descriptor: java.lang.ClassCastException: org.apache.xerces.parsers.XIncludeAwareParserConfiguration cannot be cast to org.apache.xerces.xni.parser.XMLParserConfiguration".
Причина:
версия библиотеки Xerces API, используемая tomcat`ом, отличается от той, с которой собран war-ник.
Решение:
  • Удалить из war-ника библиотеку xerces-impl.jar вообще, если она не используется в коде веб-сервиса
  • Используя механизм endorsed-direcory, заставить сервер использовать необходимую версию   Xerces API (как это сделать, описано тут: http://barbitoff.blogspot.com/2011/09/axb-20-api-is-being-loaded-from.html)  

JAXB 2.0 API is being loaded from the bootstrap classloader, but this RI needs 2.1 API

Проблема: 
при попытке деплоя веб-сервиса на tomcat вываливается исключение типа:
JAXB 2.0 API is being loaded from the bootstrap classloader, but this RI (from jar:file:/C:/++tomcat/webapps/testWS/WEB-INF/lib/jaxb-impl.jar!/com/sun/xml/bind/v2/model/impl/ModelBuilder.class) needs 2.1 API. Use the endorsed directory mechanism to place jaxb-api.jar in the bootstrap classloader. (See http://java.sun.com/j2se/1.5.0/docs/guide/standards/)
Причина: 
версия библиотеки JAXB, используемая tomcat`ом, отличается от той, с которой собран war-ник (в данном случае, томкат использует 2.0, а веб-сервис использует 2.1).

Решение:
Вариант 1: Обновить JDK, используемый сервером, до версии, которая использует более новую JAXB.
Вариант 2: использовать endorsed-механизм:
  1. В какую-то диреторию положить необходимую для веб-сервиса версию библиотеки jaxb-api.jar
  2. Далее либо установить переменную окружения JAVA_ENDORSED_DIRS с путем к этой директории, либо залезть в catalina.bat (или sh), запускающий tomcat, и поменять в нём 
-Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%"
 на 
-Djava.endorsed.dirs="/path/to/my/endorsed/dir"

Jar-ники из этой директории будут переопределять те, которые используются jdk`ем по-умолчанию.