barbitoff programmer`s blog

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

среда, 27 января 2016 г.

Spring Boot: No qualifying bean of type [org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilder] found

Проблема

Перевели проект с spring boot 1.2.6 на 1.3.1. При старте приложения начала падать ошибка:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'xxxEntityManagerFactory' defined in class xxx.xxx: Unsatisfied dependency expressed through constructor argument with index 0 of type [org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilder]: : No qualifying bean of type [org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilder] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilder] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
    at ...
Решение

В Spring Boot 1.3.1 вместо 
org.springframework.boot.autoconfigure.orm.jpa.EntityManagerFactoryBuilder
нужно использовать 
org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder.

среда, 20 января 2016 г.

Виртуальный съемный диск под Windows

Бывают ситуации, когда нужен виртуальный съемный диск, например, чтобы использовать его в качестве контейнера закрытого ключа шифрования. Для этих целей подойдет бесплатная утилита ImDisk: http://reboot.pro/files/file/284-imdisk-toolkit/. Чтобы монтируемый этой утилитой диск был съемным, необходимо использовать ключ "-o rem" (либо при использовании ImDisk через консоль, либо в графическом интерфейсе конфигуратора на вкладке "Advanced" в поле "Additional parameters").

Директория HDImageStore КриптоПРО JCP

Windows

Контейнеры, хранящиеся в HDImagesStore КриптоПРО JCP физически лежат на диске в подпапке \AppData\Local\Crypto Pro домашней папки пользователя (путь можно изменить через ControlPane).

Linux

/var/opt/cprocsp/keys/<username>

Актуально для КриптоПРО JCP 2.0.38481

вторник, 19 января 2016 г.

Java: SimpleDateFormat и парсинг миллисекунд

Проблема

Есть строка с датой: "2016-01-01T11:05:42.1". Если ее пытаться распарсить, используя java.text.SimpleDateFormat и шаблон "yyyy-MM-dd'T'HH:mm:ss.S", в полученной дате мы получим 1 миллисекунду вместо 100. SimpleDateFormat всегда обрабатывает число после точки как количество миллисекунд вне зависимости от реального веса того разряда, на котором расположено число. Т.е. для SimpleDateFormat даты "2016-01-01T11:05:42.1", "2016-01-01T11:05:42.01" и  "2016-01-01T11:05:42.001" идентичны.

Решение

Варианта 2:
  1. Дополняем миллисекунды в исходной строке до трех разрядов
  2. Отказываемся от использования SimpleDateFormat в пользу сторонней библиотеки, например, Joda-Time (http://www.joda.org/joda-time/)
Для последнего вариант парсинг будет выглядеть следующим образом:
import java.util.Date;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

DateTimeFormatter format = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.S");
Date parsedValue = format.parseDateTime("2016-01-01T11:05:42.1").toDate();

понедельник, 18 января 2016 г.

JDBC, MS SQL Server и выполнение нескольких SQL-выражений одновременно

Проблема

Пытаюсь выполнить PreparedStatement, состоящий из 3 выражений:
DECLARE @myVar MyTableType;
INSERT INTO @myVar(a,b) VALUES(1,2);
EXECUTE someProc @myVar;
Т.е. объявляется переменная пользовательского табличного типа, в нее вставляется строка данных, после чего эта переменная передается в некоторую процедуру. 
Выполнение PreparedStatement производится методом execute().
Процедура внутри себя содержит инструкцию RAISERROR, которая в некоторых ситуациях генерирует ошибку. Проблема заключается в том, что если перед вызовом процедуры нет INSERT-выражения, RAISERROR приводит к появлению SQLException при выполнении execute(). Если же INSERT присутствует, то RAISERROR не приводит к SQLException в вызывающем коде, и execute() выполняется без ошибок.

Причина

Данное PreparedStatement генерирует несколько результатов, поскольку содержит несколько генерирующих результат выражений внутри себя (конструкция DECLARE не считается). Первый результат - это результат выполнения INSERT'а в переменную, который выполняется успешно, поэтому execute() не генерирует исключение, т.к. "курсор" установлен на первом результате. При попытке же сделать getMoreResults() для объекта PreparedStatement мы как раз и ловим ожидаемый SQLException, т.к. выполняется переход к результату выполнения команды EXECUTE, а этот EXECUTE у нас выполнился с ошибкой.