Статьи

Ведение журнала Hibernate SQL

Существует два хорошо известных способа входа в Hibernate SQL в Grails; Одним из них является добавление logSql = true в DataSource.groovy (либо в блоке верхнего уровня для всех сред, либо для каждой среды).

1
2
3
4
5
6
dataSource {
   dbCreate = ...
   url = ...
   ...
   logSql = true
}

а другой должен использовать конфигурацию регистрации Log4j:

1
2
3
4
log4j = {
   ...
   debug 'org.hibernate.SQL'
}

Проблема с logSql том, что это слишком просто — он просто сбрасывает SQL в stdout, и нет возможности увидеть значения, которые устанавливаются для позиционного ? параметры. Подход к ведению журнала гораздо более настраиваемый, поскольку вы можете войти в консоль, если хотите, но вы можете настроить ведение журнала в файл, в файл только для этих сообщений или в любое место по вашему выбору с помощью Appender .

Но подход к ведению журнала тоже проблематичен — благодаря включению второй категории Log4j

1
2
3
4
5
log4j = {
   ...
   debug 'org.hibernate.SQL'
   trace 'org.hibernate.type'
}

мы можем видеть значения переменных, но вы видите их как для наборов PreparedStatement и для результатов ResultSet , и результаты могут привести к большим файлам журналов, полным бесполезных операторов. Это работает, потому что классы «Тип», которые Hibernate использует для хранения и загрузки значений классов Java в столбцы базы данных (например, LongType , StringType и т. Д.), Находятся в пакете org.hibernate.type и расширяют (косвенно) org.hibernate.type.NullableType который выполняет регистрацию в своих nullSafeSet и nullSafeGet .

Так что, если у вас есть класс домена GORM

1
2
3
class Person {
   String name
}

и вы сохраняете экземпляр

1
new Person(name: 'me').save()

вы увидите вывод так:

1
2
3
4
DEBUG hibernate.SQL  - insert into person (id, version, name) values (null, ?, ?)
TRACE type.LongType  - binding '0' to parameter: 1
TRACE type.StringType  - binding 'me' to parameter: 2
DEBUG hibernate.SQL  - call identity()

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

1
def allPeople = Person.list()

вы увидите вывод, как это

1
2
3
4
DEBUG hibernate.SQL  - select this_.id as id0_0_, this_.version as version0_0_, this_.name as name0_0_ from person this_
TRACE type.LongType  - returning '1' as column: id0_0_
TRACE type.LongType  - returning '0' as column: version0_0_
TRACE type.StringType  - returning 'me' as column: name0_0_

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

Я говорил об этом вчера на своем выступлении в Hibernate на SpringOne 2GX и понял, что должна быть возможность создать собственный Appender который проверяет операторы журнала для этих классов и игнорирует операторы, полученные в результате получения ResultSet . К моему удивлению, в Grails 2.x все изменилось, потому что мы обновили Hibernate 3.3 до 3.6, и эта проблема уже решена в Hibernate.

Вышеприведенный вывод фактически сделан из проекта 1.3.9, который я создал после неожиданного вывода в приложении 2.1.1. Вот что я увидел в 2.1.1:

01
02
03
04
05
06
07
08
09
10
11
12
DEBUG hibernate.SQL  -
    /* insert Person
        */ insert
        into
            person
            (id, version, name)
        values
            (null, ?, ?)
 
TRACE sql.BasicBinder  - binding parameter [1] as [BIGINT] - 0
 
TRACE sql.BasicBinder  - binding parameter [2] as [VARCHAR] - asd

и

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
DEBUG hibernate.SQL  -
    /* load Author */ select
        author0_.id as id1_0_,
        author0_.version as version1_0_,
        author0_.name as name1_0_
    from
        author author0_
    where
        author0_.id=?
 
TRACE sql.BasicBinder  - binding parameter [1] as [BIGINT] - 1
 
TRACE sql.BasicExtractor  - found [0] as column [version1_0_]
 
TRACE sql.BasicExtractor  - found [asd] as column [name1_0_]

Так что теперь вместо того, чтобы делать всю регистрацию из базового класса типов, он был переработан для делегирования org.hibernate.type.descriptor.sql.BasicBinder и org.hibernate.type.descriptor.sql.BasicExtractor . Это здорово, потому что теперь мы можем изменить конфигурацию Log4j на

1
2
3
4
5
log4j = {
   ...
   debug 'org.hibernate.SQL'
   trace 'org.hibernate.type.descriptor.sql.BasicBinder'
}

и есть наш торт и есть его тоже; SQL регистрируется в настраиваемом месте назначения Log4j, и регистрируются только наборы PreparedStatement .

Обратите внимание, что во втором примере SQL выглядит по-разному не из-за изменений в Grails или Hibernate, а потому, что я всегда format_sql форматирование SQL (с format_sql ) и комментарии (с use_sql_comments ) в тестовых приложениях, поэтому, когда я use_sql_comments ведение журнала, он оказывается более читаемый, и я забыл сделать это для приложения 1.3:

1
2
3
4
5
6
7
hibernate {
   cache.use_second_level_cache = true
   cache.use_query_cache = false
   cache.region.factory_class = 'net.sf.ehcache.hibernate.EhCacheRegionFactory'
   format_sql = true
   use_sql_comments = true
}

Ссылка: ведение журнала Hibernate SQL от нашего партнера по JCG Берта Беквита в блоге « Армия солипсистов» .