Статьи

Отладка в спящем режиме — поиск источника запроса

Не всегда понятно, почему и в какой части программы Hibernate генерирует данный SQL-запрос, особенно если мы имеем дело с кодом, который мы не написали сами.

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

Как выглядит журнал запросов Hibernate

Hibernate имеет встроенную регистрацию запросов, которая выглядит следующим образом:

1
2
3
4
5
select /* load your.package.Employee */ this_.code, ...
from employee this_
where this_.employee_id=?
 
TRACE 12-04-2014@16:06:02  BasicBinder - binding parameter [1] as [NUMBER] - 1000

Почему Hibernate не может регистрировать фактический запрос?

Обратите внимание, что Hibernate записывает подготовленный оператор, отправленный Hibernate драйверу JDBC, плюс его параметры. Подготовленное заявление имеет ? вместо параметров запроса сами значения параметров записываются только под подготовленным оператором.

Это не то же самое, что фактический запрос, отправленный в базу данных, так как Hibernate не может зарегистрировать фактический запрос. Причина этого заключается в том, что Hibernate знает только о подготовленных операторах и параметрах, которые он отправляет драйверу JDBC, и именно этот драйвер будет создавать фактические запросы и затем отправлять их в базу данных.

Для создания журнала с реальными запросами необходим инструмент наподобие log4jdbc , который станет темой другого поста.

Как узнать происхождение запроса

Записанный выше запрос содержит комментарий, позволяющий в большинстве случаев определить происхождение запроса: если запрос вызван загрузкой по идентификатору, это комментарий /* load your.entity.Name */ , если это именованный запрос, то комментарий будет содержать название запроса.

Если это ленивая инициализация один ко многим, комментарий будет содержать имя класса и свойство, которое его вызвало, и т. Д.

Настройка журнала запросов Hibernate

Чтобы получить журнал запросов, в конфигурации фабрики сеансов необходимо установить следующие флаги:

1
2
3
4
5
6
7
8
9
<bean id= "entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
  ...
  <property name="jpaProperties" >
  <props>
      <prop key="hibernate.show_sql" >true</ prop>
      <prop key="hibernate.format_sql" >true</ prop>
      <prop key="hibernate.use_sql_comments">true</prop>
  </props>
</property>

Приведенный выше пример относится к конфигурации Spring фабрики менеджера сущностей. Это значение флагов:

  • show_sql включает ведение журнала запросов
  • format_sql довольно печатает SQL
  • use_sql_comments добавляет пояснительный комментарий

Для регистрации параметров запроса необходима следующая log4j или эквивалентная информация:

1
2
3
<logger name="org.hibernate.type">
    <level value="trace" />
</logger >

Если ничего не помогает

Во многих случаях комментария, созданного с помощью use_sql_comments , достаточно для определения источника запроса. Если этого недостаточно, то мы можем начать с определения сущности, возвращаемой запросом, на основе соответствующих имен таблиц и поместить точку останова в конструктор возвращенной сущности.

Если у сущности нет конструктора, мы можем создать его и поставить точку останова в вызове super() :

1
2
3
4
5
6
7
@Entity
public class Employee {
    public Employee() {
        super(); // put the breakpoint here
    }
    ...
}

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