Как сравнить строки не алфавитно в JPA Specification, преобразовав их в числа

Вводная такая. В постгресе хранятся сущности Topic со строковым полем number, которое, тем не менее, гарантированно хранит число. Так уж получается по требованиям. Нужно при поиске сконструировать предикат, который будет сравнивать это числовое значение с другими числами, т.е. нам не подходит alphabetical comparison.

Для начала поясню про алфавитное сравнение. Если рассмотреть функцию String.compareTo(), то окажется, что «10» действительно «меньше» «11» и «12», но еще «меньше» «8». Строка «10» будет, однако, «больше» строки «08», но мне от этого не легче, потому что путь дописывания ведущих нулей ведет в никуда.

API JPA-спецификации позволяет применить в SQL запросе функцию СУБД, к примеру:

// получаем номер в виде строки
Expression topicNumberString = join.get(CONST_NUMBER);
// преобразуем в число
Expression topicNumberInt = cb.function("FUNCTION_NAME", Integer.class, topicNumberString);

В SQL запросе это будет выглядеть примерно так:

FUNCTION_NAME(topic_.number) > ...

Насколько я понял, со стороны спецификации ожидается, что функция имеет один аргумент. Среди встроенных функций postgres я не нашел подходящей, поэтому написал свою:

CREATE OR REPLACE FUNCTION <your_schema>.custom_to_number(string CHARACTER VARYING)
 RETURNS BIGINT
 LANGUAGE plpgsql
AS $function$	 DECLARE BEGIN
		RETURN string::NUMERIC;
END;
$function$
</your_schema>

Подставляйте своё значение вместо
Проверить можно так:

select <your_schema>.custom_to_number('12');
select <your_schema>.custom_to_number('012');
</your_schema></your_schema>

Теперь можно задействовать так:

Integer lowerLimit = ...;
Integer upperLimit = ...;
topicNumberPredicate.getExpressions().add(cb.and(
cb.greaterThanOrEqualTo(topicNumberInt, lowerLimit),
cb.lessThanOrEqualTo(topicNumberInt, upperLimit)));
You can leave a response, or trackback from your own site.

Leave a Reply