grailsのHibernateでSQLのパラメータまで出力する

元ネタ http://burtbeckwith.com/blog/?p=1604

grailsSQLを出力する方法は2通りあってDataSource.groovyで

dataSource {
    ...
    logSql = true
}

とするか、Config.groovyで

log4j = {
    ...
    debug  'org.hibernate.SQL'
}

とするか。前者は標準出力で、後者はログとして出力される。これで出力されるSQLにパラメータは出力されない。

以下の様な感じ。

    insert 
    into
        book
        (id, version, title) 
    values
        (null, ?, ?)

今までパラメータまで出力する場合は、後者のログの出力レベルを変更してやる方法でorg.hibernate.typeパッケージをtraceとして方法でやっていた。

log4j = {
    ...
    trace  'org.hibernate.type'
    debug  'org.hibernate.SQL'
}

この設定でどんなログが出力されるのか以下のテストを実行。(見やすいようにDataSource.groovyにformatSql=trueを追加して実行)

    def "logの確認"() {
        given:
        new Book(title: 'a').save(flush: true)
        new Book(title: 'b').save(flush: true)
        new Book(title: 'c').save(flush: true)
        Book.withSession { it.clear() } // selectで1次キャッシュを使わないようにクリア

        when:
        def books = Book.list()

        then:
        books.size() == 3
    }

ログは以下のようなに出力される。

| Running 1 spock test... 1 of 1
--Output from logの確認--
2012-10-20 13:08:57,706 [main] DEBUG org.hibernate.SQL - 
    insert 
    into
        book
        (id, version, title) 
    values
        (null, ?, ?)
2012-10-20 13:08:57,707 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - 0
2012-10-20 13:08:57,707 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - a
2012-10-20 13:08:57,710 [main] DEBUG org.hibernate.SQL - 
    insert 
    into
        book
        (id, version, title) 
    values
        (null, ?, ?)
2012-10-20 13:08:57,710 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - 0
2012-10-20 13:08:57,710 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - b
2012-10-20 13:08:57,713 [main] DEBUG org.hibernate.SQL - 
    insert 
    into
        book
        (id, version, title) 
    values
        (null, ?, ?)
2012-10-20 13:08:57,713 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - 0
2012-10-20 13:08:57,713 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - c
2012-10-20 13:08:57,749 [main] DEBUG org.hibernate.SQL - 
    select
        this_.id as id24_0_,
        this_.version as version24_0_,
        this_.title as title24_0_ 
    from
        book this_
2012-10-20 13:08:57,750 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [1] as column [id24_0_]
2012-10-20 13:08:57,750 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [0] as column [version24_0_]
2012-10-20 13:08:57,750 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [a] as column [title24_0_]
2012-10-20 13:08:57,750 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [2] as column [id24_0_]
2012-10-20 13:08:57,750 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [0] as column [version24_0_]
2012-10-20 13:08:57,750 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [b] as column [title24_0_]
2012-10-20 13:08:57,750 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [3] as column [id24_0_]
2012-10-20 13:08:57,751 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [0] as column [version24_0_]
| Completed 1 spock test, 0 failed in 250ms

これで見れるようになる!のだけどselectした時のResultSetに含まれるログが大量に出力される。上の例では問題ないが、ResultSetの件数が多い場合はこれがノイズになる。

ResultSetのパラメータは出力されないようにしたい。上記のログが出力されているパッケージをみるとinsertの時はorg.hibernate.type.descriptor.sql.BasicBinderで、selectの時はorg.hibernate.type.descriptor.sql.BasicExtractorからログ出ていることがわかる。そこで以下のようにConfig.groovyを変更する。

log4j = {
    ...
    trace 'org.hibernate.type.descriptor.sql.BasicBinder'
    debug 'org.hibernate.SQL'
}

この状態でもう一度実行してみる。

| Running 1 spock test... 1 of 1
--Output from logの確認--
2012-10-20 13:07:53,797 [main] DEBUG org.hibernate.SQL - 
    insert 
    into
        book
        (id, version, title) 
    values
        (null, ?, ?)
2012-10-20 13:07:53,798 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - 0
2012-10-20 13:07:53,798 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - a
2012-10-20 13:07:53,801 [main] DEBUG org.hibernate.SQL - 
    insert 
    into
        book
        (id, version, title) 
    values
        (null, ?, ?)
2012-10-20 13:07:53,801 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - 0
2012-10-20 13:07:53,801 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - b
2012-10-20 13:07:53,804 [main] DEBUG org.hibernate.SQL - 
    insert 
    into
        book
        (id, version, title) 
    values
        (null, ?, ?)
2012-10-20 13:07:53,804 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - 0
2012-10-20 13:07:53,805 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - c
2012-10-20 13:07:53,849 [main] DEBUG org.hibernate.SQL - 
    select
        this_.id as id22_0_,
        this_.version as version22_0_,
        this_.title as title22_0_ 
    from
| Completed 1 spock test, 0 failed in 113ms

なかなか良い感じ。

あと元ネタのURLで

hibernate {
   ...
   use_sql_comments = true
}

というsqlコメントを出してくれるオプションがあるようだ。知らなかった。

注意としてDataSource.groovyの dataSource {...} の方ではなく hibernate {...} の方に記述する必要がある。dataSourceの方に記述する方法はないかと調べてみ見たが実装は以下のようになっており、use_sql_commentsに関する処理は無さそう。てことでhibernateに書かなければならない。

// grails 2.1.1での情報
// org.codehaus.groovy.grails.plugins.orm.hibernate.HibernatePluginSupport
// 157行目あたり
            if (ds.loggingSql || ds.logSql) {
                hibProps."hibernate.show_sql" = "true"
            }
            if (ds.formatSql) {
                hibProps."hibernate.format_sql" = "true"
            }

ということで諦めてhibernateの方に書いて実行してみる。

--Output from logの確認--
2012-10-20 13:27:10,397 [main] DEBUG org.hibernate.SQL - 
    /* insert test.Book
        */ insert 
        into
            book
            (id, version, title) 
        values
            (null, ?, ?)
2012-10-20 13:27:10,398 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - 0
2012-10-20 13:27:10,398 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - a
2012-10-20 13:27:10,401 [main] DEBUG org.hibernate.SQL - 
    /* insert test.Book
        */ insert 
        into
            book
            (id, version, title) 
        values
            (null, ?, ?)
2012-10-20 13:27:10,402 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - 0
2012-10-20 13:27:10,402 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - b
2012-10-20 13:27:10,404 [main] DEBUG org.hibernate.SQL - 
    /* insert test.Book
        */ insert 
        into
            book
            (id, version, title) 
        values
            (null, ?, ?)
2012-10-20 13:27:10,405 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [BIGINT] - 0
2012-10-20 13:27:10,405 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [2] as [VARCHAR] - c
2012-10-20 13:27:10,462 [main] DEBUG org.hibernate.SQL - 
    /* criteria query */ select
        this_.id as id32_0_,
        this_.version as version32_0_,
        this_.title as title32_0_ 
    from
        book this_
2012-10-20 13:27:10,462 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [1] as column [id32_0_]
2012-10-20 13:27:10,463 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [0] as column [version32_0_]
2012-10-20 13:27:10,463 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [a] as column [title32_0_]
2012-10-20 13:27:10,463 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [2] as column [id32_0_]
2012-10-20 13:27:10,463 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [0] as column [version32_0_]
2012-10-20 13:27:10,463 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [b] as column [title32_0_]
2012-10-20 13:27:10,463 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [3] as column [id32_0_]
2012-10-20 13:27:10,463 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [0] as column [version32_0_]
| Completed 1 spock test, 0 failed in 135ms

でたが/* criteria query */とかでそんなに嬉しくないけど、出てないより出ていたほうが何かの助けるなるかもしれない。試しに

        println '=' * 100
        Book.withCriteria {
            eq 'title', 'a'
        }

        println '=' * 100
        Book.where {
            title == 'a'
        }.list()

        println '=' * 100
        Book.findByTitle('a')

        println '=' * 100
        Book.findAll('from Book as b where b.title = :title', [title: 'a'])

というコードを実行してみたら以下の様に出た。

====================================================================================================
2012-10-20 13:34:41,168 [main] DEBUG org.hibernate.SQL - 
    /* criteria query */ select
        this_.id as id40_0_,
        this_.version as version40_0_,
        this_.title as title40_0_ 
    from
        book this_ 
    where
        this_.title=?
2012-10-20 13:34:41,168 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - a
2012-10-20 13:34:41,169 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [1] as column [id40_0_]
====================================================================================================
2012-10-20 13:34:41,184 [main] DEBUG org.hibernate.SQL - 
    /* criteria query */ select
        this_.id as id40_0_,
        this_.version as version40_0_,
        this_.title as title40_0_ 
    from
        book this_ 
    where
        this_.title=?
2012-10-20 13:34:41,185 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - a
2012-10-20 13:34:41,185 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [1] as column [id40_0_]
====================================================================================================
2012-10-20 13:34:41,207 [main] DEBUG org.hibernate.SQL - 
    /* criteria query */ select
        this_.id as id40_0_,
        this_.version as version40_0_,
        this_.title as title40_0_ 
    from
        book this_ 
    where
        this_.title=? limit ?
2012-10-20 13:34:41,209 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - a
2012-10-20 13:34:41,210 [main] TRACE org.hibernate.type.descriptor.sql.BasicExtractor - found [1] as column [id40_0_]
====================================================================================================
2012-10-20 13:34:41,226 [main] DEBUG org.hibernate.SQL - 
    /* 
from
    Book as b 
where
    b.title = :title */ select
        book0_.id as id40_,
        book0_.version as version40_,
        book0_.title as title40_ 
    from
        book book0_ 
    where
        book0_.title=?
2012-10-20 13:34:41,227 [main] TRACE org.hibernate.type.descriptor.sql.BasicBinder - binding parameter [1] as [VARCHAR] - a
| Completed 1 spock test, 0 failed in 177ms

なるほどHQLで便利は場面はあるかもしれない。てことでHibernateログを出すときは
Config.groovyに

log4j = {
    ...
    trace  'org.hibernate.type.descriptor.sql.BasicBinder'
    debug  'org.hibernate.SQL'
}

を追加。DataSource.groovyに

hibernate {
    ...
    format_sql = true
    use_sql_comments = true
}

を追加がお勧めかな。