Рубрики
Без рубрики

Как найти, какая инструкция не удалась при пакетном обновлении JDBC

Автор оригинала: 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[] с двумя записями, мы знаем, что только две инструкции были успешно обработаны. Итак, третье утверждение было причиной сбоя.

Чтобы завершить это, нам нужно кое-что:

  1. Хороший способ регистрации операторов JDBC и datasourceproxy является одним из лучших способов регистрации подготовленных утверждений JDBC
  2. Нам также нужно перехватить исключение BatchUpdateException и найти количество операторов, которые были успешно выполнены, вызвав метод getUpdateCounts

Таким образом, вы сможете определить, какое пакетное утверждение вызывает проблему, и этот метод применим к любой производственной системе. Вам просто нужно убедиться, что вы правильно собираете все журналы (например, Logstash ), чтобы вы могли запрашивать их при возникновении проблемы.

Код доступен на GitHub .