2009年8月3日月曜日

Grailsで運用中アプリのドメインクラスを変更したとき

忘れないうちに。。。

もう3ヶ月ぐらい使っていて、2500行ぐらいあるテーブルに、フィールドを2つ追加する。
これらは、NotNullにする。
前からあるフィールドのうち、2つはNotNullをとる。

1.まんず、ドメインクラスを追加、変更
class Task {
.....
 // 作業依頼時間
 Double assignTime
 // 作業依頼日時
 Date assignDate
.....
 static constraints={
.....
  assignTime(nullable:false)
  assignDate(nullable:false)
  workTime(nullable:true) ←falseからtrueに変更
  workDate(nullable:true) ←falseからtrueに変更
.....
 }
}

2.これで一回GrailsをRun-appして起動すると、、、
ポスグレさんがどういう風になるかというとで、pgAdmin3氏に聞いてみます。
すると、、、
・assign_time,assign_dateのフィールド(列)は追加されているが、NOT NULLになってない。
・work_time,work_dateがNOT NULLのまま。
となっています。
これだと、なんやかんやGrailsさんから怒られるので、DBを調整します。
調整しなければいけないのは、
・assign_time,assign_dateにデータを入れる(とりあえずwork_time,work_dateをコピー)
・assign_time,assign_dateをNOT NULLにする
・work_time,work_dateのNOT NULLを外す
の3項目。

3.pgAdmin3でできるのか?
とりあえずGrailsを停止。
pgAdmin3でTaskテーブルを右クリックでプロパティを選びます。
ここで「列」のところで、assign_timeを選んで、NOT NULLにチェックボックスを入れてみます。
そうすると、pgAdmin3氏が、「いやー、この項目はデータが入ってないから、NOT NULLにはでけんへんよ、無茶やわぁ」と言ってきます。多分、そういってる気がします。

4.psqlでデータを入れる(今回はあるフィールドの内容をそのままコピー)
$su - postgres で、ポスグレさんになります。
postgres$ psql DB名 で、データベースにアクセスできる状態にします。
DB名=>\d ドメインクラス名 で、フィールド一覧が出てきます。
Table "public.task"
Column | Type | Modifiers
--------------+-----------------------------+-----------
id      | bigint          | not null
version    | bigint          | not null
date_created | timestamp without time zone| not null
last_updated | timestamp without time zone| not null
project_id  | bigint          | not null
title     | text           |
work_date  | timestamp without time zone| not null ←ここを外す
work_time  | double precision      | not null ←ここを外す
work_user_id | bigint          | not null
assign_time | double precision      |  ←ここにnot null
assign_date | timestamp without time zone|  ←ここにnot null
多分こんな形がでてきます。
フィールドの追加はうまくいってますが、制約が微妙になっています。

↓work_timeをコピーして、assign_timeに入れる
DB名=>UPDATE テーブル名 SET assign_time=work_time; 

↓work_dateをコピーして、assign_dateに入れる
DB名=>UPDATE テーブル名 SET assign_date=work_date;
※ちなみに日付を指定していれたいときは、
DB名=>UPDATE テーブル名 SET assign_date=TO_DATE('2009/08/03 23:59:59', 'yyyy/mm/dd hh24:mi:ss');
とすれば入れられます。

5.NOT NULLをつけたり、とったりする。
↓とるのはDROP
DB名=>ALTER TABLE テーブル名 ALTER COLUMN フィールド名 DROP NOT NULL;
↓つけるのはSET
DB名=>ALTER TABLE テーブル名 ALTER COLUMN フィールド名 SET NOT NULL;
pgAdmin3氏のテーブルのプロパティにあるSQLを見ると、どんなSQL文なのかすぐわかります。

6.まとめ
一度走ってしまってから、こういう変更って結構ありそうですよね。
テーブルの追加、リレーションの追加とかは結構影響範囲も大きく、大幅な変更なので、時間をかけてやってもいいですが、これぐらいはさっくりできないと、ということで、さっくり出来たのでめでたし、めでたし。