Scala(Playframework)でDBアクセスする方法の一つとしてAnormがあります。今回はこのAnormの記述を理解するために記事を書きました。
Anormのコンセプト
Anormのコンセプト
AnormはORMではない
生JDBCはつらい、よりよいAPIとしてのAnorm API
DBアクセスのDSLはSQLがベスト
SQLを生成するtype safe DSLは誤った方向性
ORMと格闘するよりSQL書いたほうがコスト小さい
確かにiBatis(MyBatis), 使うと、おんなじようなこと事何回も書かないといけないのツライし、HibernateはSQLの細かい記述や方言を使いはじめると結局NativeQueryで書かないといけなくなったり。辛いです。Anormの考え方は全くその通りかなと思います。
todolistの作りを見る
サンプルを見て実行して、コードを見るのが素早く理解を深めるのに重要だと思っています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package models import anorm._ import play.api.db.DB import play.api.Play.current import anorm.SqlParser._ case class Task(id: Long, label: String) object Task { val task = { get[Long]("id") ~ get[String]("label") map { case id ~ label => Task(id, label) } } def all(): List[Task] = DB.withConnection { implicit c => SQL("select * from task").as(task *) } } |
Task.allでDB内のtaskテーブルの中身を取得できるようになってますね。このtaskイミュータブルの部分がいまいち理解できません。ほかのサイトなどを覗くとこのような記述でもいけるようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
object Task { /*val task = { get[Long]("id") ~ get[String]("label") map { case id ~ label => Task(id, label) } }*/ val taskParser = get[Long]("id") ~ get[String]("name") val taskMapper = taskParser.map { case id ~ name => Task(id, name) } def all(): List[Task] = DB.withConnection { implicit c => SQL("select * from task").as(taskMapper *) } } |
つまりtaskはパーサーとマッパーに分けることができるようですね。「~」(チルダ)がどういう意味だか分かっていませんでしたが、こういう記法ができるみたいですね。
面倒くさいパーサの実装もDSLで書くだけ!そう、Scalaならね
scala-play-frameworkanormを使ってみて困ったこと、試したこと
Anormの思想自体はなんとなく分かった気がするけど、テーブルのカラム数が大規模になるとORMの時の「いっぱいカラム名書かないといけない問題」は同様に抱えそうな気が。