Grailsでテーブル毎にシーケンスを自動生成する方法

参考 http://grails.1312388.n4.nabble.com/One-hibernate-sequence-is-used-for-all-Postgres-tables-td1351722.html

むかしJIRAにもチケット上がっていたみたいですが、
http://jira.grails.org/browse/GRAILS-3138
GrailsとうかHibernateの問題なのでcloseされた模様。

ということで、GrailsというよりHibernateの話ではあるのですがPostgreSQLDialectなど、getNativeIdentifierGeneratorClassにデフォルトのSequenceGeneratorが使用されている場合

...
public Class getNativeIdentifierGeneratorClass() {
    return SequenceGenerator.class;
}
...

というような実装になっており、とくにマッピングの指定など行わないと全テーブル共通でidの採番がhibernate_sequenceから行われてしまいます(嬉しいことないよね?)。

これをテーブル毎にシーケンスが作成されるようにしたいということでカスタムのDialectとSequenceGeneratorを作成します。こんなん(groovyで書いています)。

class MyDialect extends PostgreSQLDialect {

    @Override
    Class<?> getNativeIdentifierGeneratorClass() {
        TableNameSequenceGenerator.class
    }

    static class TableNameSequenceGenerator extends SequenceGenerator {

        @Override
        void configure(Type type, Properties params, Dialect dialect) {
            if (!params.getProperty(SEQUENCE)) {
                String tableName = params.getProperty(PersistentIdentifierGenerator.TABLE)
                if (tableName) {
                    params.setProperty(SEQUENCE, "${tableName}_id_seq")
                }
            }
            super.configure(type, params, dialect)
        }

    }

}

こいつをDataSource.groovyのdialectに自分が作ったMyDialectを差し替えればおk。

dataSource {
    driverClassName = "org.postgresql.Driver"
    dialect = org.yamkazu.portgresql.MyDialect
    dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
    url = "jdbc:postgresql:grails"
    username = "grails"
    password = "grails"
}

bookとauthorというドメインの場合以下の様な感じで、author_id_seq、book_id_seqが生成されます。

grails=# \ds
             List of relations
 Schema |     Name      |   Type   | Owner  
--------+---------------+----------+--------
 public | author_id_seq | sequence | grails
 public | book_id_seq   | sequence | grails
(2 rows)

2012/07/13修正 DataSource.groovyの追記方法とシーケンス名の指定を少し修正しました