Persistentを触ってみている
{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-} {-# LANGUAGE GADTs, FlexibleContexts #-} import Database.Persist import Database.Persist.Sqlite import Database.Persist.TH import Control.Monad.IO.Class (liftIO) share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist| Person name String age Int Maybe loc String default="Japan" deriving Show |] main :: IO () main = withSqliteConn "test.db" $ runSqlConn $ do runMigration migrateAll johnId <- insert $ Person "kzfm" (Just 20) "Fuji" liftIO $ print johnId
実行するとデータベースが作られる。実行されるSQLはごく普通のcreate文
CREATE TABLE "Person"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL)
続いてEntityを変更してみる。locを追加してdefaultも付けておく。ついでにinsertのとこも修正しておく
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist| Person name String age Int Maybe loc String default="Japan" deriving Show |] main :: IO () main = withSqliteConn "test.db" $ runSqlConn $ do runMigration migrateAll johnId <- insert $ Person "kzfm" (Just 20) "Fuji" liftIO $ print johnId
バックアップテーブルにデータが移され、もとのテーブルが変更された後、データが戻されて移行が無事に完了する。
CREATE TEMP TABLE "Person_backup"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL,"loc" VARCHAR NOT NULL DEFAULT "Japan") INSERT INTO "Person_backup"("id","name","age") SELECT "id","name","age" FROM "Person" DROP TABLE "Person" CREATE TABLE "Person"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL,"loc" VARCHAR NOT NULL DEFAULT "Japan") INSERT INTO "Person" SELECT "id","name","age","loc" FROM "Person_backup" DROP TABLE "Person_backup"
Entityのデフォルトはデータ移行時のもとのカラムに付加される値であって、insertで省略できるわけではないので、insertは明示的に値を入れないといけない。databaseを見てみるともとのデータのlocにはEntityのデフォルトで指定したJapanという値が入っていることがわかる。
sqlite> select * from person; 1|kzfm|20|Japan 2|kzfm|20|Fuji
ちなみにEntityのデフォルトを指定しないとマイグレーションに失敗するが、Maybe型にしておけばnullが入るので移行できる。
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist| Person name String age Int Maybe loc String Maybe deriving Show |]
開発している時にスキーマの変更はそれなりに苦痛を伴う作業なので、ある程度面倒見てくれるのはありがたい。ダメなときはダメって言ってくれるし。
Persistentのマイグレーションは保守的なので安心らしい。次のケースで自動的にスキーマの変更がなされる。
- フィールドのデータタイプが変更された時 。ただし変更できる時のみ
- (The datatype of a field changed. However, the database may object to this modification if the data cannot be translated.)
- フィールドが追加された時で、フィールドがnot nullの場合はdefaultが指定されている時
- (A field was added. However, if the field is not null, no default value is supplied (we'll discuss defaults later) and there is already data in the database, the database will not allow this to happen.)
- フィールドがnot nullからnullに変更された時。逆のケースも試みるがデータベース次第
- (A field is converted from not null to null. In the opposite case, Persistent will attempt the conversion, contingent upon the database's approval.)
- 新しいエンティティが追加された時
- (A brand new entity is added.)
Developing Web Applications With Haskell and Yesod
Michael Snoyman
Oreilly & Associates Inc / 2805円 ( 2012-05-04 )
Michael Snoyman
Oreilly & Associates Inc / 2805円 ( 2012-05-04 )