0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Grails]PostgreSQLのsequenceについて

Posted at

参考
http://nobeans.hatenablog.com/entry/20091030/1256896937
http://stackoverflow.com/questions/2822623/how-to-set-up-an-insert-to-a-grails-created-file-with-next-sequence-number

GrailsでPostgreSQLを使う場合、デフォルトだとPostgreSQLのsequenceが利用されない。
以下のように各ドメインに自分のsequenceを定義することで利用できるようになる。

package hoge

class Test{

    String word
    static constraints = {
    }
    static mapping = {
        id generator:'sequence', params: [sequence:'test_id_seq']
    }
}

ただし、これだと実は落とし穴があって、GrailsがCreateDropモードの場合は、上記のsequenceも含めてテーブルを全部自動生成してくれるけど、 そのテーブルのidには、属性としてそのsequenceのnextvalが指定されない。
そのため、生のSQLでidを省略すると、 idにnullは許されないぜ! というエラーが当然発生する。

以下はその条件でGrailsによって自動生成されるテーブルの定義

-- こっちがテーブル
mydb=# \d test
     Tabelle public.test
 Spalte  |          Typ           | Attribute
---------+------------------------+-----------
 id      | bigint                 | not null
 version | bigint                 | not null
 word    | character varying(255) | not null
Indexe:
    "test_pkey" PRIMARY KEY, btree (id)

-- こっちがsequence
mydb=# \d test_id_seq
     Sequenz public.test_id_seq
    Spalte     |   Typ   |           Wert
---------------+---------+---------------------------
 sequence_name | name    | test_id_seq
 last_value    | bigint  | 4
 start_value   | bigint  | 1
 increment_by  | bigint  | 1
 max_value     | bigint  | 9223372036854775807
 min_value     | bigint  | 1
 cache_value   | bigint  | 1
 log_cnt       | bigint  | 32
 is_cycled     | boolean | f
 is_called     | boolean | t

id のAttributeにnextvalが指定されていないので、以下のようなSQLがエラーになる。

INSERT INTO test (version,word) VALUES(1, 'aaa')

Grails自体は、ドメインに指定されたsequenceの情報を知っているので、ちゃんと生成したsequenceを使ってくれる。
でも、PostgreSQL自体は、上のテーブル定義を見れば分かるとおり、testテーブルのidに対応するsequenceの情報がどこにも書かれていないので、INSERT時にidが省略された場合、自動でsequenceを利用することが出来ない。

本来は以下のようなテーブル定義になる必要がある。
こうすれば、idを省略すると、nextvalが指定されているので自動的にPostgreSQLがそのsequenceを利用してくれる。

                          Tabelle public.test2
 Spalte |   Typ   |                       Attribute
--------+---------+--------------------------------------------------------
 id     | integer | not null Vorgabewert nextval('test2_id_seq'::regclass)
 name   | text    |

Grailsの用意している機能を使ってデータベースを利用する場合は、sequenceを使おうが使うまいが特に問題はない。
問題になるのは、SQL(INSERT)を自分で書いて実行する場合。

対応策としては以下の2点がある
1.テーブルとsequenceは全て自分で生成する or 後からnextvalを追加する。
2.SQLを直接実行する際には、idをちゃんと指定する。

2番の場合は以下のような感じになる。

def query = "INSERT INTO test (id,version,word) VALUES(nextval('test_id_seq'),?,?)"
sql.executeInsert(query, [1,"test-data-a"])

ちなみにサービスの中でSQLを直接実行してもちゃんとロールバックできる。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?