GORMでjoin fetchをやってみる

さっきの例でBlogController.listを実行すると,こんなSQL(HQL?)が実行される。

Hibernate: 
    select
        top ? this_.id as id0_0_,
        this_.version as version0_0_,
        this_.body as body0_0_,
        this_.title as title0_0_,
        this_.uploaded_file_id as uploaded5_0_0_ 
    from
        blog this_
Hibernate: 
    select
        fileinfo0_.id as id2_0_,
        fileinfo0_.version as version2_0_,
        fileinfo0_.content_type as content3_2_0_,
        fileinfo0_.file_content_id as file4_2_0_,
        fileinfo0_.filename as filename2_0_ 
    from
        file_info fileinfo0_ 
    where
        fileinfo0_.id=?

1件しかデータ登録してないけど,みごとにN+1問題が起きてるのがわかるね。これをなんとかしてみる。
ぱっと思いついたのが Blogドメインの mapping で eager fetch を明示する事。つまり,こんなん。

class Blog {
  :
  static mapping = {
    uploadedFile lazy:false
  }
  // または
  //static fetchMode = [ uploadedFile: 'eager' ]
}

いやいや,ちょっとまて。すでに lazy fetch してないから,これは意味無いだろう(実際,変化なかったし)。というわけで,次に試したのが GORMのfinder に fetchオプションを与える方法。

class BlogController {
  def scaffold = true
 
  def list = {
    [blogInstanceList: Blog.findAll([fetch: [uploadedFile: 'eager']])]
  }
  :

すると,こんな感じに join fetch になる(指定は eager なのにね)。

Hibernate: 
    select
        this_.id as id2_1_,
        this_.version as version2_1_,
        this_.body as body2_1_,
        this_.title as title2_1_,
        this_.uploaded_file_id as uploaded5_2_1_,
        fileinfo2_.id as id0_0_,
        fileinfo2_.version as version0_0_,
        fileinfo2_.content_type as content3_0_0_,
        fileinfo2_.file_content_id as file4_0_0_,
        fileinfo2_.filename as filename0_0_ 
    from
        blog this_ 
    left outer join
        file_info fileinfo2_ 
            on this_.uploaded_file_id=fileinfo2_.id

いちいち finder に fetchオプションを与えるのがうっとうしいけど,これにてN+1問題は回避できたよ。
ちなみにこれ,Grails 1.0.4の話な。


わざわざバージョンの話をするのは,Grails 1.1だとこんな指定方法があるからよ。

class Blog {
  : 
  static mapping = {
    uploadedFile fetch:'join'
  }
}

これだと,finderにへんなオプション付けなくても,このようにjoin fetchが実行される。

Hibernate: 
    select
        top ? this_.id as id0_1_,
        this_.version as version0_1_,
        this_.body as body0_1_,
        this_.title as title0_1_,
        this_.uploaded_file_id as uploaded5_0_1_,
        fileinfo2_.id as id1_0_,
        fileinfo2_.version as version1_0_,
        fileinfo2_.content_type as content3_1_0_,
        fileinfo2_.file_content_id as file4_1_0_,
        fileinfo2_.filename as filename1_0_ 
    from
        blog this_ 
    left outer join
        file_info fileinfo2_ 
            on this_.uploaded_file_id=fileinfo2_.id


むむぅ,このためだけでも Grails 1.1 にスイッチする価値はあるな。


ps.
ちょっと前に「いまいまさのぶ on Twitter: "なるほど。GORMでもone-to-oneはLazy fetchしてくれんのか。"」なんて毒電波垂れ流してたんだけど,あらためて試してみたら,そんなことなかった。オレは夢でもみていたのか?
そんときは one-to-one ではなく,one-to-many にして,実態は one-to-one にしてやり過ごした。...って勝手に思い込んで悦に至ってたが,なんか勘違いしてたみたい。:-(
#忘れてしまうには惜しいので,あえて恥をさらしておく。


なんにせよ,ちょっと足を踏み入れるととたんにHibernateの知識が必要になるんだなってのが,垣間みれたのは収穫だった。