Существует два хорошо известных способа входа в 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 Берта Беквита в блоге « Армия солипсистов» .