Автор оригинала: 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 .