文系プログラマによるTIPSブログ

文系プログラマ脳の私が開発現場で学んだ事やプログラミングのTIPSをまとめています。

【java】Grailsを学ぶ:Vol.01:GORMのDB定義【framework】

grailsも結構いいものですよ〜
f:id:treeapps:20180418115102p:plain

そろそろSaStrutsを使うのは時代遅れなので、Play FrameworkかGrailsを候補に検討中です。
Playは会社で使いそうなので、自宅ではGrailsを学ぶ事にしました。

ざっくり触っただけですが、PlayはJava JavaしてるFWで、Grailsはjsやrubyのような感じ、という印象です。Playはror風と言ってますが、全然javaの域を超えていないので、SaStrutsやSpringの上位版FWみたいな感じがしました。それと比較してGrailsはgroovyでコーディングするので、javaのような静的言語と全然違う感じです。今までと大分コーディングの仕方が変わります。

習得の容易さやいざという時に馴染んだjavaで何とかできる事を考慮するとPlay。生産性や動的言語への移行をしたいならGrails。個人的には機能的には圧倒的にGrailsの方が多機能なので、Grailsを推します。


話が逸れてしまいましたが、今回の本題はGORM(Grails ORM)です。Grails起動時にGORMがドメインを元にテーブルを自動生成するのですが、中々思うように型はフィールド長やPrimary keyが設定できなかったので、解った範囲でまとめておきたいと思います。

なお、Grailsのバージョンは2.3.4です。

生成したいDDL

以下のDDLをGORMで作成する事を目指します。

mysql> desc address;
+--------------------------+----------+------+-----+---------+-------+
| Field                    | Type     | Null | Key | Default | Extra |
+--------------------------+----------+------+-----+---------+-------+
| pref_cd                  | char(2)  | NO   | PRI |         |       |
| pref_name                | text     | YES  |     | NULL    |       |
| city_cd                  | char(5)  | NO   | PRI |         |       |
| city_name                | text     | YES  |     | NULL    |       |
| town_street_cd           | char(12) | NO   | PRI |         |       |
| town_street_name         | text     | YES  | MUL | NULL    |       |
| town_street_name_reverse | text     | YES  | MUL | NULL    |       |
| latitude                 | double   | YES  |     | NULL    |       |
| longitude                | double   | YES  |     | NULL    |       |
+--------------------------+----------+------+-----+---------+-------+

思うようにいかなかった部分

  • versionカラムが勝手に生成されてしまう。
  • 型の指定方法。
  • 複合キー(複数のPrimary Key)を指定する方法。
  • bigint auto_incrementでないカラムをidに指定する方法。
  • GORMで生成されるカラムの順番を指定する方法。

では順番に見ていきます。

versionカラムが勝手に生成されてしまう

これは簡単です。

class Address {

    ・・・略・・・

    static constraints = {
        ・・・略・・・
    }
    static mapping = {
        version false
    }
}

version flaseを指定するだけです。

型の指定方法

class Address {

    String prefCd
    String prefName
    String cityCd
    String cityName
    String townStreetCd
    String townStreetName
    String townStreetNameReverse
    Double latitude
    Double longitude

    static constraints = {
        prefCd maxSize: 2, blank: false, nullable: false
        prefName maxSize: 191, blank: true, nullable: true
        cityCd maxSize: 5, blank: false, nullable: false
        cityName maxSize: 191, blank: true, nullable: true
        townStreetCd maxSize: 12, blank: false, nullable: false
        townStreetName blank: true, nullable: true
        townStreetNameReverse blank: true, nullable: true
        latitude nullable: true
        longitude nullable: true
    }
    static mapping = {
        townStreetName sqlType: 'text'
        townStreetNameReverse sqlType: 'text'
    }
}

こんな感じでmappingの方にsqlTypeで定義できます。
sqlTypeを指定しないとbigintやvarcharになってしまいます。

型+フィールド長を指定する場合、例えば char(2) を定義する場合は以下のようにします。

    static constraints = {
        prefCd blank: false, nullable: false
    }
    static mapping = {
        prefCd sqlType: 'char(2)'
    }

mappingで型を定義する場合はconstraints側にmaxSizeを設定しても効かないので注意です。
例えば、

    static constraints = {
        prefCd maxSize: 1, blank: false, nullable: false
    }
    static mapping = {
        prefCd sqlType: 'char(2)'
    }

とした場合、生成されるのはchar(1)ではなくchar(2) になります。

複合キー(複数のPrimary Key)を指定する方法。

GORMは何も指定しないと自動的に id bigint auto_increment primary key を生成してしまいます。
マスター等の場合は自動採番カラムがあると困るので、複合キーを指定したい場合があります。
その場合、以下のように指定します。

class Address implements Serializable {

    String prefCd
    String prefName
    String cityCd
    String cityName
    String townStreetCd
    String townStreetName
    String townStreetNameReverse
    Double latitude
    Double longitude

    static constraints = {
        prefCd maxSize: 1,  blank: false, nullable: false
        prefName maxSize: 191, blank: true, nullable: true
        cityCd blank: false, nullable: false
        cityName maxSize: 191, blank: true, nullable: true
        townStreetCd blank: false, nullable: false
        townStreetName blank: true, nullable: true
        townStreetNameReverse blank: true, nullable: true
        latitude nullable: true
        longitude nullable: true
    }
    static mapping = {
        id composite: ['prefCd', 'cityCd', 'townStreetCd']
        prefCd sqlType: 'char(2)'
        cityCd sqlType: 'char(5)'
        townStreetCd sqlType: 'char(12)'
        townStreetName sqlType: 'text'
        townStreetNameReverse sqlType: 'text'
    }
}

id compositeの部分とimplements Serializableの部分がキモです。リファレンスには書いてありますが、見ないと絶対解らないので注意ですね。

複合主キーを用いるドメインクラスでは、Serializableインターフェイスの実装とequalsとhashCodeメソッドのオーバライドが必要です。 これによって複合キーのプロパティを同一性の算定に利用できるようになります。

http://grails.jp/doc/latest/guide/GORM.html#compositePrimaryKeys

ちなみに複合キーを指定した場合、idカラムは生成されませんでした。

bigint auto_incrementでないカラムをidに指定する方法

これは非常に解りにくく、調べるのに苦戦しました。

複合キーだとjoinの手間がかかったりするので、小規模なマスターの場合、PKのカラムを全て結合したカラムを用意する事があります。
今回は、address_cdをid(Primary Key)として使用します。
address_cdの内訳は、 pref_cd + city_cd + town_street_cd です。
勿論bigintではないですし、auto_incrementではありません。
これを実現するために、最終的に以下のように指定する事で定義できました。

class Address implements Serializable {

    String prefCd
    String prefName
    String cityCd
    String cityName
    String townStreetCd
    String townStreetName
    String townStreetNameReverse
    Double latitude
    Double longitude

    static constraints = {
        addressCd blank: false, nullable: false
        prefCd maxSize: 1,  blank: false, nullable: false
        prefName maxSize: 191, blank: true, nullable: true
        cityCd blank: false, nullable: false
        cityName maxSize: 191, blank: true, nullable: true
        townStreetCd blank: false, nullable: false
        townStreetName blank: true, nullable: true
        townStreetNameReverse blank: true, nullable: true
        latitude nullable: true
        longitude nullable: true
    }
    static mapping = {
        version false
        id generator: 'assigned',  column: 'address_cd', sqlType: 'char(19)'
        addressCd insertable: false, updateable: false
        prefCd sqlType: 'char(2)'
        cityCd sqlType: 'char(5)'
        townStreetCd sqlType: 'char(12)'
        townStreetName sqlType: 'text'
        townStreetNameReverse sqlType: 'text'
    }
}

重要なのは以下の部分です。

        id generator: 'assigned',  column: 'address_cd', sqlType: 'char(19)'
        addressCd insertable: false, updateable: false

generator: 'assigned'は日本語リファレンスには書いてませんが、自動採番でない、自分で設定した値を使う(auto_incrementにしない)という意味になります。
column: 'address_cd'はidはどのカラムなのかを指定します。キャメルケースで書けないのが残念・・
sqlType: 'char(19)'ですが、一見するとaddressCdの方に書きたくなりますが、idの方に記述する必要があります。
insertable: false, updateable: falseですが、すみませんよく解ってません。。こう書かないとエラーになります。

GORMで生成されるカラムの順番を指定する方法

随分調べたのですが、GORMではできないっぽいです
GORMは前述のドメインクラスを以下のように生成してしまいます。

mysql> desc address;
+--------------------------+--------------+------+-----+---------+-------+
| Field                    | Type         | Null | Key | Default | Extra |
+--------------------------+--------------+------+-----+---------+-------+
| address_cd               | char(19)     | NO   | PRI | NULL    |       |
| city_cd                  | char(5)      | NO   |     | NULL    |       |
| city_name                | varchar(191) | YES  |     | NULL    |       |
| latitude                 | double       | YES  |     | NULL    |       |
| longitude                | double       | YES  |     | NULL    |       |
| pref_cd                  | char(2)      | NO   |     | NULL    |       |
| pref_name                | varchar(191) | YES  |     | NULL    |       |
| town_street_cd           | char(12)     | NO   |     | NULL    |       |
| town_street_name         | text         | YES  |     | NULL    |       |
| town_street_name_reverse | text         | YES  |     | NULL    |       |
+--------------------------+--------------+------+-----+---------+-------+

アルファベット順にカラムが並んでしまうのです。
海外のサイトに書いてあったのですが、どうもmigration機能で手動で制御するしかないっぽい???

どなたか知っていたら是非教えて下さい。



【java】Grailsを学ぶ:Vol.01:GORMのDB定義【framework】 - 文系プログラマによるTIPSブログ
【java】Grailsを学ぶ:Vol.02:resource pluginでcss・jsをinclude!【framework】 - 文系プログラマによるTIPSブログ
Grails徹底入門

Grails徹底入門

  • 作者: 山田正樹,山本剛,上原潤二,永井昌子,杉山清美,杉浦孝博,笠原史郎,香月孝太,福岡竜一,伊堂寺北斗
  • 出版社/メーカー: 翔泳社
  • 発売日: 2008/08/26
  • メディア: 大型本
  • 購入: 3人 クリック: 42回
  • この商品を含むブログ (28件) を見る
Play Framework 2徹底入門 JavaではじめるアジャイルWeb開発

Play Framework 2徹底入門 JavaではじめるアジャイルWeb開発