Автор оригинала: Vlad Mihalcea.
Вчера мой датский друг Флемминг Хармс задал мне очень интересный вопрос, связанный с тем, когда происходит сбой пакетного обновления JDBC .
В принципе, учитывая, что мы собираемся сгруппировать несколько операторов DML в пакет, нам нужен способ определить, какой оператор является причиной сбоя. В этом посте мы ответим на этот вопрос более подробно.
Учитывая, что у нас есть Post
сущность, идентификаторы которой назначаются вручную:
@Entity(name = "Post") @Table(name = "post") public class Post { @Id private Long id; private String title; //getters and setters omitted for brevity }
Теперь мы собираемся использовать пакетирование JDBC для группировки нескольких операторов INSERT. Для этой цели мы будем использовать Подготовленную инструкцию
, так как она лучше реагирует на пакетирование JDBC, чем простая java.sql.Инструкция
.
В следующем примере для имитации сбоя мы собираемся назначить один и тот же первичный ключ нескольким записям, чтобы база данных могла вызвать исключение ConstraintViolationException
:
Session session = entityManager.unwrap(Session.class); session.doWork(connection -> { try (PreparedStatement st = connection.prepareStatement( "INSERT INTO post (id, title) " + "VALUES (?, ?)")) { for (long i = 0; i < 5; i++) { st.setLong( 1, i % 2 ); st.setString( 2, String.format( "High-Performance Java Persistence, Part %d", i ) ); st.addBatch(); } st.executeBatch(); } catch (BatchUpdateException e) { LOGGER.info( "Batch has managed to process {} entries", e.getUpdateCounts().length ); } });
При запуске приведенного выше тестового случая Hibernate генерирует следующие выходные данные:
c.v.b.h.h.b.BatchExceptionTest - testInsertPosts n.t.d.l.SLF4JQueryLoggingListener - Name:DATA_SOURCE_PROXY, Time:0, Success:False, Type:Prepared, Batch:True, QuerySize:1, BatchSize:5, Query:[ "INSERT INTO post (id, title) VALUES (?, ?)"], Params:[ (0, High-Performance Java Persistence, Part 0), (1, High-Performance Java Persistence, Part 1), (0, High-Performance Java Persistence, Part 2), (1, High-Performance Java Persistence, Part 3), (0, High-Performance Java Persistence, Part 4) ] c.v.b.h.h.b.BatchExceptionTest - Batch has managed to process 2 entries
Итак, из зарегистрированного вывода JDBC мы видим, что третий оператор будет конфликтовать с первым. Тем не менее, мы можем точно определить неудачные операторы, проанализировав результат метода getUpdateCounts
в исключении java.sql.BatchUpdateException
, которое генерируется драйвером JDBC.
Поскольку метод getUpdateCounts
возвращает массив int[]
с двумя записями, мы знаем, что только две инструкции были успешно обработаны. Итак, третье утверждение было причиной сбоя.
Чтобы завершить это, нам нужно кое-что:
- Хороший способ регистрации операторов JDBC и datasourceproxy является одним из лучших способов регистрации подготовленных утверждений JDBC
- Нам также нужно перехватить исключение
BatchUpdateException
и найти количество операторов, которые были успешно выполнены, вызвав методgetUpdateCounts
Таким образом, вы сможете определить, какое пакетное утверждение вызывает проблему, и этот метод применим к любой производственной системе. Вам просто нужно убедиться, что вы правильно собираете все журналы (например, Logstash ), чтобы вы могли запрашивать их при возникновении проблемы.
Код доступен на GitHub .