Обзор:
У меня есть сохраненная функция plpgsql
, которая выполняет обновление в одной из таблиц. Перед обновлением он оценивает условие и выдает исключение, если условие оценивается как true
.
В Java-коде есть метод уровня сервиса, который несколько раз вызывает эту хранимую процедуру с разными параметрами в цикле. Метод помечен как транзакционный с весенней аннотацией.
Проблема:
Допустим, на основе входящих параметров хранимая процедура должна вызываться 2 два раза. Во время первого вызова исключение внутри SP не генерируется, поэтому обновление происходит успешно. При втором вызове генерируется исключение, которое затем обрабатывается настроенным @ExceptionHandler
. Проблема в том, что обновление, выполненное при первом обращении к хранимой процедуре, не откатывается весной.
Может ли пружина откатить изменения, сделанные при первом вызове процедуры, и если да, то в чем может быть проблема с текущей настройкой?
Пример кода:
Хранимая процедура:
create or replace function my_function(entity_id uuid)
returns integer
language plpgsql
as $$
declare
r record;
begin
select * into r from my_table where id = entity_id;
if (r.my_value > 10)
then
raise exception 'Exception message'
using errcode ='12345', hint = 'Exception hint';
end if;
update my_table
set my_date = now()
where id = entity_id;
return 0;
end;
$$;
Способ обслуживания:
@Transactional
public void update(final List<UUID> ids) {
for (final UUID id : ids) {
storedProcedureRepository.myFunction(id);
}
}
Метод репозитория:
@Transactional
@Override
public Optional<Integer> myFunction(final UUID entityId) {
return getResultSingle("myFunction", query -> {
query.setParameter("entity_id", entityId);
});
}
private <T> Optional<T> getResultSingle(String procedureName, Consumer<StoredProcedureQuery> params) {
EntityManager em = null;
try {
em = emf.createEntityManager();
StoredProcedureQuery query = em.createNamedStoredProcedureQuery(procedureName);
params.accept(query);
query.execute();
T result = (T) query.getSingleResult();
return (result == null) ? Optional.empty() : Optional.of(result);
} finally {
if (em != null && em.isOpen()) {
em.close();
}
}
}