barbitoff programmer`s blog

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

вторник, 31 июля 2012 г.

Oracle: аналог LIMIT / TOP

Некоторым аналогом функционала LIMIT / TOP по ограничению числа возвращаемых строк является псевдостолбец "rownum", представляющий собой номер строки в результирующей выборке. Таким образом, запрос:
SELECT * FROM abc WHERE rownum<=10
аналогичен запросу:
SELECT * FROM abc LIMIT 10
в MySQL / PostgreSQL, или:
SELECT TOP 10 * FROM abc
в MS SQL.
Правда, у такого подхода есть важная особенность: ограничение выборки производится раньше, чем сортировка с помощью ORDER BY, в отличие от конструкции LIMIT в MySQL / PostgreSQL. Т.е.:
SELECT * FROM abc WHERE rownum<=10 ORDER BY a
вовсе не аналогичен вызову
SELECT * FROM abc ORDER BY a LIMIT 10
в других СУБД, т.к. оракл проведет сначала выборку первых 10 значений, а потом уже отсортирует их по столбцу "a". Чтобы ограничение выборки выполнялось уже после сортировки, необходимо использовать вложенный запрос:
SELECT * FROM (SELECT * FROM abc ORDER BY a) WHERE rownum<=10
Альтернативой такому, мягко говоря, несимпатичному запросу может быть использование функции ROW_NUMBER.

среда, 25 июля 2012 г.

Плагин jquery.validation и элементы формы с одинаковыми именами

Плагин  jquery.validation не умеет работать с элементами с одинаковыми именами. Использование одинаковых имен ведет, во-первых, к тому, что валидируется только первый элемент, ну и, во-вторых, ко всяким глюкам с неверным позиционированием сообщения об ошибке и пр. Всякие костыли (например, http://www.codeboss.in/web-funda/2009/05/27/jquery-validation-for-array-of-input-elements/) работают лишь частично (например, элемент начинает вроде бы валидироваться, но сообщение об ошибке все равно у всех одноименных элементов одно на всех). Поправить эту ситуацию изменением пары строк в исходниках не получится - слишком многое в них завязано на имена элементов. Поэтому остается лишь ходить обходными путями, например, назначать элементам уникальные имена и преобразовывать их при отправке провалидированной формы в обработчике, назначенном параметром "submitHandler" в вызове validate().

Hibernate: обновление идентификатора persistent-объекта

Проблема:

При попытке выполнить сохранение (session.update()) persistent-объекта после изменения поля, являющегося идентификатором в hibernate-маппинге, валится исключение:
org.hibernate.HibernateException: identifier of an instance of ... was altered from ... to ...
Решение:

Обновлять идентификаторы у persistent-объекта нельзя (вероятно, это связано со спецификой генерации Hibernate`ом SQL-запросов на обновление). Если сделать для объекта session.evict(), а потом - session.save(), то добавится новая запись в БД, с новым идентификатором. Это тоже не то, что мы хотели. Остается другой вариант - выполнить session.delete(), а потом - session.save(). Тогда мы получим желаемое - идентификатор "обновится" в БД (точнее, сначала запись удалится, а потом - вставится с новыми идентификатором).

вторник, 24 июля 2012 г.

Hibernate: SQLQuery и двоеточия в тексте запроса

Задача следующая: выполнить с помощью Hibernate некоторый SQL-запрос к БД PostgreSQL (используя session.createSQLQuery()), использующий приведение типов с помощью "::", например:
SELECT SETVAL('mysequence'::regclass,11111)
Проблема заключается в том, что Hibernate использует двоеточие для реализации именованных параметров запроса, поэтому "::regclass" воспринимается им как параметр с именем ":regclass", что в итоге приводит к ошибке при выполнении запроса, т.к. значение для этого параметра не задано:
...
Caused by: org.hibernate.exception.DataException: Не указано значение для параметра 1.
...
Caused by: org.postgresql.util.PSQLException: Не указано значение для параметра 1.
Никаких способов экранирования двоеточия, насколько я понял, нет (соответствующий тикет висит аж с 2005 года и версии 3.1: https://hibernate.onjira.com/browse/HHH-1237, - тогда как я проверяю на версии 4.1). Хорошо хоть, что в PostgreSQL есть и другой способ приведения типов - с помощью конструкции CAST:
SELECT SETVAL(CAST('mysequence' AS regclass),11111)
Но для других БД, где в SQL применяется двоеточие для такой же или другой цели, таких замен может и не быть. 

понедельник, 23 июля 2012 г.

Получение thumbprint`а для X509Certificate в Java

Класс java.security.cert.X509Certificate не предоставляет соответствующего метода, поэтому приходится делать это вручную. Можно непосредственно, используя MessageDigest (http://stackoverflow.com/questions/1270703/how-to-retrieve-compute-an-x509-certificates-thumbprint-in-java), а можно воспользоваться классом DigestUtils из библиотеки commons-codec:
public static String getHexThumbprint(X509Certificate cert) throws CertificateEncodingException
{
return DigestUtils.shaHex(cert.getEncoded());  
}
public static byte[] getThumbprint(X509Certificate cert) throws NoSuchAlgorithmException, CertificateEncodingException
{
return DigestUtils.sha(cert.getEncoded());
}


пятница, 20 июля 2012 г.

Замена анонимным блокам PostgreSQL 9+ в более ранних версиях

Такая вещь, как анонимные блоки, появилась только в девятой версии PostgreSQL, в более ранних версиях нечто подобное можно делать, создавая и затем вызывая неанонимные функции / процедуры в схеме pg_temp, которые будут удалены по окончании сессии:
create function pg_temp.f(...) returns ... as $$
begin
   ...
end
$$ language plpgsql;
pg_temp.f(...);
pg_temp.f(...);

четверг, 19 июля 2012 г.

ScheduledThreadPoolExecutor и setMaximumPoolSize

java.util.concurrent.ScheduledThreadPoolExecutor использует фиксированный пул потоков, определяемый свойством corePoooSize. Вызов scheduler.setMaximumPoolSize() ни к чему не приводит, и если число задач больше чем corePoolSize, они стоят в очереди в ожидании свободного потока, даже если время их выполнения подошло.
НО никто не мешает вызовом setCorePoolSize() в любой момент жизни ScheduledThreadPoolExecutor`а увеличить или уменьшить этот размер пула. Увеличение размера приводит к моментальному созданию нового потока (если очередь scheduler.getQueue() непуста, т.е. не все задачи разобраны свободными потоками), а вот с уменьшением не всё так просто. 
Дело в том, что класс ThreadPoolExecutor.Worker устроен таким образом, что даже если число потоков в пуле превышает установленный corePoolSize, освободившийся поток все равно попытается взять из очереди задач очередную задачу, и завершится только если задачи там нет и ждать её ему пришлось больше, чем промежуток времени, устанавливаемый вызовом setKeepAliveTime():
else if (poolSize > corePoolSize || allowCoreThreadTimeOut)
r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS);
Специфика периодических задач  ScheduledThreadPoolExecutor`а такова, что если число задач больше corePoolSize, в очереди всегда есть задачи, поэтому Worker всегда получит задачу вызовом workQueue.poll и завершаться не станет, даже если размер пула превышает заданный corePoolSize(). 
Таким образом, динамическое уменьшение corePoolSize возымеет эффект только когда число потоков в пуле станет меньше числа запланированных задач.
Ниже приведен небольшой график, полученный экспериментально (между изменениями размера corePoolSize и проверкой реального размера пула я ждал промежуток времени, достаточный, чтобы все задачи по разу отработали):