LoginSignup
8
8

More than 5 years have passed since last update.

[Grails]GORMの動作(テーブル編)

Last updated at Posted at 2014-01-28

参考:

前提条件

  • 以下のドメインクラスを用いる。
    Author.groovy
    Book.groovy

  • デフォルトだとGrailsが生成するでversionカラムは利用しない。

  • データベースはPostgreSQL9.3.1(for Windows)を用いた。

  • データベース名はtest。

  • パッケージ名はtestp

  • dbCreate = "create-drop"

特に関連なし

Author.groovy
class Author {

    String name

    static constraints = {
    }
    static mapping = {
        version false
    }
}
Book.groovy
class Book {

    String title

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

authorテーブル

属性
id bigint not null
name varchar(255) not null

bookテーブル

属性
id bigint not null
title varchar(255) not null

生成されるテーブルは以下。

test=# \d author
           Tabelle public.author
 Spalte |          Typ           | Attribute
--------+------------------------+-----------
 id     | bigint                 | not null
 name   | character varying(255) | not null
Indexe:
    "author_pkey" PRIMARY KEY, btree (id)

test=# \d book
            Tabelle public.book
 Spalte |          Typ           | Attribute
--------+------------------------+-----------
 id     | bigint                 | not null
 title  | character varying(255) | not null
Indexe:
    "book_pkey" PRIMARY KEY, btree (id)

別のドメインをプロパティに持つ

Author.groovy
class Author {

    String name
    Book book

    static constraints = {
    }
    static mapping = {
        version false
    }
}
Book.groovy
class Book {

    String title

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

authorテーブル

属性
id bigint not null
book_id bigint not null
name varchar(255) not null

bookテーブル

属性
id bigint not null
title varchar(255) not null

生成されるテーブルは以下。

test=# \d author
           Tabelle public.author
 Spalte  |          Typ           | Attribute
---------+------------------------+-----------
 id      | bigint                 | not null
 book_id | bigint                 | not null
 name    | character varying(255) | not null
Indexe:
    "author_pkey" PRIMARY KEY, btree (id)
Fremdschlüssel-Constraints:
    "fkac2d218b90583074" FOREIGN KEY (book_id) REFERENCES book(id)


test=# \d book
            Tabelle public.book
 Spalte |          Typ           | Attribute
--------+------------------------+-----------
 id     | bigint                 | not null
 title  | character varying(255) | not null
Indexe:
    "book_pkey" PRIMARY KEY, btree (id)
Fremdschlüsselverweise von:
    TABLE "author" CONSTRAINT "fkac2d218b90583074" FOREIGN KEY (book_id) REFEREN
CES book(id)

別のドメインにプロパティを持たれていて、そこに対してbelongsToを指定する。

上記と比べて相違は無し。

Author.groovy
class Author {

    String name
    Book book

    static constraints = {
    }
    static mapping = {
        version false
    }
}
Book.groovy
class Book {

    String title
    static belongsTo = [author: Author]

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

authorテーブル

属性
id bigint not null
book_id bigint not null
name varchar(255) not null

bookテーブル

属性
id bigint not null
title varchar(255) not null
test=# \d author
           Tabelle public.author
 Spalte  |          Typ           | Attribute
---------+------------------------+-----------
 id      | bigint                 | not null
 book_id | bigint                 | not null
 name    | character varying(255) | not null
Indexe:
    "author_pkey" PRIMARY KEY, btree (id)
Fremdschlüssel-Constraints:
    "fkac2d218b90583074" FOREIGN KEY (book_id) REFERENCES book(id)


test=# \d book
            Tabelle public.book
 Spalte |          Typ           | Attribute
--------+------------------------+-----------
 id     | bigint                 | not null
 title  | character varying(255) | not null
Indexe:
    "book_pkey" PRIMARY KEY, btree (id)
Fremdschlüsselverweise von:
    TABLE "author" CONSTRAINT "fkac2d218b90583074" FOREIGN KEY (book_id) REFEREN
CES book(id)

別のドメインをhasManyでプロパティに持つ

結合用のテーブルが生成される。

Author.groovy
class Author {

    String name
    static hasMany = [books: Book]

    static constraints = {
    }
    static mapping = {
        version false
    }
}
Book.groovy
class Book {

    String title

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

authorテーブル

属性
id bigint not null
name varchar(255) not null

bookテーブル

属性
id bigint not null
title varchar(255) not null

author_bookテーブル

属性
author_books_id bigint not null
book_id bigint not null

生成されるテーブル

test=# \d author
           Tabelle public.author
 Spalte |          Typ           | Attribute
--------+------------------------+-----------
 id     | bigint                 | not null
 name   | character varying(255) | not null
Indexe:
    "author_pkey" PRIMARY KEY, btree (id)
Fremdschlüsselverweise von:
    TABLE "author_book" CONSTRAINT "fk2a7a111dafdcb969" FOREIGN KEY (author_book
s_id) REFERENCES author(id)


test=# \d book
            Tabelle public.book
 Spalte |          Typ           | Attribute
--------+------------------------+-----------
 id     | bigint                 | not null
 title  | character varying(255) | not null
Indexe:
    "book_pkey" PRIMARY KEY, btree (id)
Fremdschlüsselverweise von:
    TABLE "author_book" CONSTRAINT "fk2a7a111d90583074" FOREIGN KEY (book_id) RE
FERENCES book(id)


test=# \d author_book
     Tabelle public.author_book
     Spalte      |  Typ   | Attribute
-----------------+--------+-----------
 author_books_id | bigint |
 book_id         | bigint |
Fremdschlüssel-Constraints:
    "fk2a7a111d90583074" FOREIGN KEY (book_id) REFERENCES book(id)
    "fk2a7a111dafdcb969" FOREIGN KEY (author_books_id) REFERENCES author(id)

別のドメインからhasManyでプロパティに持たれていて、そこに対してbelongsToを指定する。

この場合は結合用のテーブルは生成されず、その代わりに従属しているドメイン(今回はBook)に従属されているドメイン(今回はAuthor)のIDが格納されるようになる。

Author.groovy
class Author {

    String name
    static hasMany = [books: Book]

    static constraints = {
    }
    static mapping = {
        version false
    }
}
Book.groovy
class Book {

    String title
    static belongsTo = [authors: Author]

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

authorテーブル

属性
id bigint not null
name varchar(255) not null

bookテーブル

属性
id bigint not null
authors_id bigint notnull
title varchar(255) not null
test=# \d author
           Tabelle public.author
 Spalte |          Typ           | Attribute
--------+------------------------+-----------
 id     | bigint                 | not null
 name   | character varying(255) | not null
Indexe:
    "author_pkey" PRIMARY KEY, btree (id)
Fremdschlüsselverweise von:
    TABLE "book" CONSTRAINT "fk2e3ae9795e2d7" FOREIGN KEY (authors_id) REFERENCE
S author(id)


test=# \d book
              Tabelle public.book
   Spalte   |          Typ           | Attribute
------------+------------------------+-----------
 id         | bigint                 | not null
 authors_id | bigint                 | not null
 title      | character varying(255) | not null
Indexe:
    "book_pkey" PRIMARY KEY, btree (id)
Fremdschlüssel-Constraints:
    "fk2e3ae9795e2d7" FOREIGN KEY (authors_id) REFERENCES author(id)


1対1

ちょっと変わって、hasOneを使って1対1を実現すると、双方向のみサポートされる。
そのため、hasOneが付いていない(hasOneで指定される)ドメインのほうには、プロパティとして対応するドメインをもつ必要がある。

単純にプロパティにドメインを持つ場合との違いは、 "author_book_id_key" UNIQUE CONSTRAINT, btree (book_id) という制約が付与されること。
なお、hasOneで指定されると、指定されたテーブル(ドメイン)側に、その制約が付与される点に注意。

Author.groovy
class Author {

    String name
    Book book

    static constraints = {
    }
    static mapping = {
        version false
    }
}
Book.groovy
class Book {

    String title
    static hasOne = [author : Author]

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

authorテーブル

属性
id bigint not null
book_id bigint notnull
name varchar(255) not null

bookテーブル

属性
id bigint not null
title varchar(255) not null
test=# \d author
           Tabelle public.author
 Spalte  |          Typ           | Attribute
---------+------------------------+-----------
 id      | bigint                 | not null
 book_id | bigint                 | not null
 name    | character varying(255) | not null
Indexe:
    "author_pkey" PRIMARY KEY, btree (id)
    "author_book_id_key" UNIQUE CONSTRAINT, btree (book_id)
Fremdschlüssel-Constraints:
    "fkac2d218b90583074" FOREIGN KEY (book_id) REFERENCES book(id)


test=# \d book
            Tabelle public.book
 Spalte |          Typ           | Attribute
--------+------------------------+-----------
 id     | bigint                 | not null
 title  | character varying(255) | not null
Indexe:
    "book_pkey" PRIMARY KEY, btree (id)
Fremdschlüsselverweise von:
    TABLE "author" CONSTRAINT "fkac2d218b90583074" FOREIGN KEY (book_id) REFEREN
CES book(id)

1対1(uniqueをつける)

通常の1対1に書いたとおり、hasOneで指定された側のテーブルの制約が変化するのであって、hasOneを書いたドメインに対応するテーブルの制約が変わるわけではない(ややこしい)
不公平だ!ということで、公式ドキュメントにも書いてあるとおり、hasOneを記述するドメインにunique制約をつけてあげる。
データベース上は変化無し。

Author.groovy
class Author {

    String name
    Book book

    static constraints = {
    }
    static mapping = {
        version false
    }
}
Book.groovy
class Book {

    String title
    static hasOne = [author : Author]

    static constraints = {
        author unique: true
    }
    static mapping = {
        version false
    }
}

authorテーブル

属性
id bigint not null
book_id bigint notnull
name varchar(255) not null

bookテーブル

属性
id bigint not null
title varchar(255) not null
test=# \d author
           Tabelle public.author
 Spalte  |          Typ           | Attribute
---------+------------------------+-----------
 id      | bigint                 | not null
 book_id | bigint                 | not null
 name    | character varying(255) | not null
Indexe:
    "author_pkey" PRIMARY KEY, btree (id)
    "author_book_id_key" UNIQUE CONSTRAINT, btree (book_id)
Fremdschlüssel-Constraints:
    "fkac2d218b90583074" FOREIGN KEY (book_id) REFERENCES book(id)


test=# \d book
            Tabelle public.book
 Spalte |          Typ           | Attribute
--------+------------------------+-----------
 id     | bigint                 | not null
 title  | character varying(255) | not null
Indexe:
    "book_pkey" PRIMARY KEY, btree (id)
Fremdschlüsselverweise von:
    TABLE "author" CONSTRAINT "fkac2d218b90583074" FOREIGN KEY (book_id) REFEREN
CES book(id)

ちなみに、この状態で以下のようなコードを試してみると、ともにデータが保存されない。

groovy:000> new Book(title:"a").save(flush:true)
===> null
groovy:000> new Author(name:"author_a").save(flush:true)
===> null

保存するときは、Bookドメインを基準に保存しないといけない

groovy:000> import testp.*
===> [import testp.*]
groovy:000> book1 = new Book(title:"book1")
===> testp.Book : (unsaved)
groovy:000> author1 = new Author(name:"author1")
===> testp.Author : (unsaved)
groovy:000> book1.author = author1
===> testp.Author : (unsaved)
groovy:000> book1.save(flush:true)
===> testp.Book : 1
groovy:000>

逆に、Authorドメインを基準に保存しようとすると例外(NullPointerException)が発生する。

groovy:000> book2 = new Book(title:"book2")
===> testp.Book : (unsaved)
groovy:000> author2 = new Author(name:"author2")
===> testp.Author : (unsaved)
groovy:000> author2.book = book2
===> testp.Book : (unsaved)
groovy:000> author2.save(flush:true)
2014-01-27 16:25:18,761 [main] ERROR util.JDBCExceptionReporter  - FEHLER: NULL-
Wert in Spalte ÔÇ×book_idÔÇ£ verletzt Not-Null-Constraint
  Detail: Fehlgeschlagene Zeile enthält (3, null, author2).

ERROR org.springframework.dao.DataIntegrityViolationException:
could not insert: [testp.Author]; SQL [insert into author (id, book_id, na
me) values (nextval ('hibernate_sequence'), ?, ?)]; constraint [null]; nested ex
ception is org.hibernate.exception.ConstraintViolationException: could not inser
t: [testp.Author]
        at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHiberna
teAccessException (SessionFactoryUtils.java:643)
        at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernate
AccessException (HibernateAccessor.java:412)
        at org.springframework.orm.hibernate3.HibernateTemplate.doExecute (Hiber
nateTemplate.java:411)
        at org.springframework.orm.hibernate3.HibernateTemplate.execute (Hiberna
teTemplate.java:339)
        at testp.Author.save (Author.groovy)
        at testp.Author$save.call (Unknown Source)

メモ

単純にプロパティとしてドメインを持つと、そのクラス(ドメインを持った側)に外部キーがセットされる。
hasManyを使うと、専用テーブルを生成して、そこで関連を管理する。
ただし!hasManyの場合、hasManyキーワードで指定された側のクラスにbelongsToを付けると、関連を管理する専用テーブルを生成・利用せずに、
belongsToを書いている側のドメイン(テーブル)が、親側(hasManyキーワードを書いている側
)のidを外部キーとして保持するようになる。

8
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
8