Database Migration PluginでNotNull制約のカラムを追加する
既存のデータが存在する場合に、NotNull
制約が付与されたカラムを追加する場合は少し工夫が必要です。単にカラムを追加すると既存のデータがNULL
になってしまうためエラーとなります。これを回避するには一度NotNull
制約を付与せずにカラムを追加し、既存データに対してUPDATE
をかけた上で、NotNull
制約を追加してあげる必要があります。
以下のドメインがあるとします。
class Person { String name }
以下のchangesetでデータベースと同期済みであるとします。
changeSet(author: "yamkazu (generated)", id: "1362294228819-1") { createTable(tableName: "person") { column(name: "id", type: "int8") { constraints(nullable: "false", primaryKey: "true", primaryKeyName: "personPK") } column(name: "version", type: "int8") { constraints(nullable: "false") } column(name: "name", type: "varchar(255)") { constraints(nullable: "false") } } }
さらにデータベースには以下のデータが入っているとします。
id | version | name ----+---------+-------- 1 | 0 | yamada 2 | 0 | sato
単純にカラムを追加するとエラーとなる
ドメインにage
のプロパティを追加します。
class Person { String name Integer age }
この状態でdbm-gorm-diff
コマンドを使用すると以下のようなchangelogを生成します。
changeSet(author: "yamkazu (generated)", id: "1362294947235-1") { addColumn(tableName: "person") { column(name: "age", type: "int4") { constraints(nullable: "false") } } }
このchangesetを反映するためにdbm-update
を実行します。
| Error 2013-03-03 16:19:10,773 [main] ERROR liquibase - Change Set changelog-0.1.groovy::1362294947235-1::yamkazu (generated) failed. Error: Error executing SQL ALTER TABLE person ADD age int4 NOT NULL: ERROR: column "age" contains null values Message: Error executing SQL ALTER TABLE person ADD age int4 NOT NULL: ERROR: column "age" contains null values
期待した通りエラーとなりました。
addNotNullConstraintを使用する
エラーを回避するためには、はじめに記述したように一度NotNull
制約を付与せずにカラムを追加し、既存データに対してUPDATE
をかけた上で、NotNull
制約を追加します。NotNull
制約を追加するにはaddNotNullConstraintが使用できます。
addNotNullConstraintの詳細はリファレンスを参照してください。
changeSet(author: "yamkazu (generated)", id: "1362294947235-1") { addColumn(tableName: "person") { column(name: "age", type: "int4") } sql("UPDATE person SET age = 30") addNotNullConstraint(tableName: "person", columnName: "age") rollback { dropColumn(tableName: "person", columnName: "age") } }
addColumn
でconstraints(nullable: "false")
とせず、(年齢を一律30才としていいかはおいといて)一度値を設定した後に、addNotNullConstraint
を使用してNotNull
制約を追加しています。
rollback
はchangeSet配下に複数のコマンドがある場合は自動でロールバック処理を作成しません。自動生成させるためにchangeSetをコマンド毎に分けるという案もありますが、ここではグループ化して、明示的にroolback
を指定しています。
addNotNullConstraintのdefaultNullValueを使用する
上記では明示的にUPDATE
をsql
コマンドを使用して設定しましたが、単純な値セットだけならばaddNotNullConstraintのdefaultNullValueが使用できます。
changeSet(author: "yamkazu (generated)", id: "1362294947235-1") { addColumn(tableName: "person") { column(name: "age", type: "int4") } addNotNullConstraint(tableName: "person", columnName: "age", defaultNullValue: "30") rollback { dropColumn(tableName: "person", columnName: "age") } }
defaultNullValueを使用すると以下のことを自動でやってくれます。
UPDATE person SET age = '30' WHERE age IS NULL; ALTER TABLE person ALTER COLUMN age SET NOT NULL;
単純な値セットであればdefaultNullValueで十分ですが、他のテーブル、カラムから値を算出するといった場合には使用できないため、その場合は先程のsql
コマンドなどを使用してください。