Как получить оператор sql, вызвавший исключение SQLException, с помощью драйвера JDBC Postgres в Java?

Задний план

В моем текущем проекте — серверном продукте без внешнего интерфейса с графическим интерфейсом, я пытаюсь написать лучшую поддержку обработки ошибок. Ошибки в настоящее время выводятся в журналы и обычно не читаются пользователями.

Мы используем PostgreSQL в качестве базы данных и обращаемся к ней с помощью прямых вызовов JDBC и DAO через пул базы данных. Большинство исключений, связанных с базой данных, заключены в универсальный класс DatabaseException, который реализует RuntimeException и пытается извлечь отладочную информацию и информацию о состоянии из переданного исключения. В нашем конкретном случае он будет обращаться к базовому драйверу базы данных PostgreSQL — PSQLException. До сих пор этот подход хорошо работал для получения более подробной информации о том, что вызвало ошибку базы данных, с заметным исключением, описанным ниже.

Кроме того, поскольку у нас есть очень специфические требования к производительности и устаревшей поддержке, у нас есть много пользовательских SQL, magic, которые делают отслеживание обратной трассировки стека немного более трудоемким, но не невозможным. или сложно.

Описание проблемы

Я заметил, что когда мы получаем SQLException в результате ошибочного оператора SQL, реализация драйвера не возвращает оператор SQL, вызвавший ошибку. Немного поискав, я обнаружил, что есть способ перевести драйвер PostgreSQL в режим отладки при запуске и заставить его отображать свойства внутреннего запроса. Однако для нас нежелательно запускать драйвер в режиме отладки в нашей производственной среде (и, честно говоря, я не смог понять, как перевести его в режим долбаных!).

Вопрос

Кто-нибудь еще имел дело с этой же проблемой раньше и нашел решение? Если нет, существует ли какой-то шаблон ООП для хранения информации о запросе перед выполнением, а затем присвоения этой информации сгенерированному исключению? Или большинство разработчиков просто считают, что им не нужен полный запрос для устранения проблем с базой данных? Честно говоря, мне это не нужно, потому что у меня есть полная трассировка стека, и я могу найти вызывающий запрос, но это определенно ускоряет мою отладку, так как это первое, что я вижу в журналы ошибок.


person Elijah    schedule 21.04.2009    source источник


Ответы (6)


Я предполагаю, что когда вы делаете вызов для выполнения запроса, у вас есть оператор, и вы получаете исключение, поэтому в этот момент у вас есть и то, и другое. Кажется, вы могли бы сделать свой анализ там.

Однако, может быть, вы улавливаете вещи дальше. Итак, вы можете сделать это в своем собственном подклассе Exception, DatabaseException, добавить элемент triggeringSQLStatement с геттером и сеттером, а затем в том месте, где вы пытаетесь выполнить оператор, поймать исходное исключение из PostgreSQL, создать новый DatabaseException, установите triggeringSQLStatement как оператор, который вы только что выполнили, и вызовите initCause() для DatabaseException, чтобы установить Exception, перехваченное из PostgreSQL, в качестве причины вашего исключения; затем бросьте ваше DatabaseException, и вызывающий код, который его поймает, будет иметь объект, который распечатывает очень приличную трассировку стека того, что произошло, плюс предоставляет доступ к оператору SQL, вызвавшему проблему. Для получения дополнительной информации об этом подходе вы можете изучить цепочку исключений Java. Даже если вы не используете все, что я только что описал, я думаю, вам определенно следует уже использовать цепочку исключений Java.

Если в коде нет места, где у вас есть доступ как к оператору SQL, вызвавшему проблему, так и к исключению, которое возникает, мне было бы очень любопытно, почему и как это возможно. И я бы посоветовал вам изменить код, чтобы у вас было такое место.

Редактировать: Поскольку вы хотите первым делом увидеть оператор SQL в журнале, вы, вероятно, могли бы также переопределить метод toString() вашего DatabaseException (или другие подходящие методы; я не уверен, что вызывается когда выводится исключение), чтобы распечатать включенный оператор SQL, предполагая, что вы включили его, как я описал выше.

person skiphoppy    schedule 21.04.2009

Раньше я добавлял SQL-запрос в свой пользовательский объект Exception, когда когда-либо возникало SQLException. В коде, где я когда-либо регистрировал сведения об исключении в файле журнала, я также регистрировал SQL.

person Bhushan Bhangale    schedule 21.04.2009
comment
Также лучше регистрировать параметры. +1 - person Adeel Ansari; 21.04.2009

Я думаю, что самый простой способ сделать это — использовать сторонний продукт, такой как p6spy. Он встает между вашим драйвером jdbc и вашей базой данных и сообщает о точных запросах, которые выполняются. Его очень легко запускать по запросу, поскольку он реализован как еще один драйвер JDBC, который делегирует полномочия вашему фактическому драйверу JDBC. Очень мощный инструмент, без которого я не могу представить себе работу.

http://www.p6spy.com/

person bwobbones    schedule 21.04.2009

Почему бы не добавить файловый регистратор для всех вызовов JDBC (log4j)?

Всякий раз, когда вы делаете вызов SQL, вы регистрируете SQL и время его выполнения. Простые вещи.

Мы делаем это для любого вызова внешней системы. SOAP-вызовы, RMI, Corba и т. д. Это оказалось бесценным почти каждый день, и если это влияет на производительность ... Я не заметил!

Если у вас есть проблемы с безопасностью, то есть вы не хотите, чтобы он переходил в файл журнала на клиентском компьютере, вы можете использовать SocketAppender и отправить его на удаленный компьютер для целей централизованного ведения журнала. Это не будет полностью безопасным, но остановит случайный шпион.

person Fortyrunner    schedule 21.04.2009

Трассировка стека может указать вам на метод DAO, вызвавший проблему, не так ли?

Изменить после комментария: если ваш SQL-запрос сложный и динамически генерируется из предыдущих частей кода, вы можете регистрировать (уровень TRACE или DEBUG) эти операторы перед их выполнением. В конфигурации ведения журналов вы можете включить ведение журналов только для DAO(-ов).

person cherouvim    schedule 21.04.2009
comment
В случае методов DAO этот подход работает довольно хорошо. Однако в случае некоторых из наших более сложных операторов SQL, вызываемых через JDBC, это немного сложнее, потому что мы получаем трассировку метода выполнения, и затем нам приходится работать, если мы возвращаемся к тому месту, где был определен оператор/подготовка. - person Elijah; 21.04.2009

Другим решением для отслеживания выполняемых вами запросов является pgFouine, который анализирует журналы, созданные Постгрес

person j pimmel    schedule 21.04.2009