anormでNULLを許容するテーブルをSelectする

scala初心者、playframeworkも初心者な私が触ってみてはまったことを解説していく。

playframeworkのチュートリアルをやってみて、ちょっと変更しようと思ったら「Null列がSelectできないでござる」とか言われてしまった。
チュートリアルhttp://www.playframework.org/documentation/2.0/ScalaTodoList

例えば、タスクの情報に"登録日"(createdate)と"締切日"(deadline)を追加したい

チュートリアルで作成した1.sqlを改良して、列を追加する。

# Tasks schema
# --- !Ups
CREATE SEQUENCE task_id_seq;
CREATE TABLE task (
  id integer NOT NULL DEFAULT nextval('task_id_seq'),
  label varchar(255),
  deadline date,
  createdate date DEFAULT sysdate
);
# --- !Downs
DROP TABLE task;
DROP SEQUENCE task_id_seq;

登録日はinsertした日でいいのでDEFAULT sysdate。

次にmodels/Task.scalaを修正。
case classの部分と、

case class Task(
    id: Long,
    label: String,
    deadline: Date,
    createdate: Date
)

val taskを変更。

val task ={
  get[Long]("id") ~
  get[String]("label") ~
  get[Date]("deadline") ~
  get[Date]("createdate") map {
    case id~label~deadline~createdate =>
      Task(id,label,deadline,createdate)
  }
}

val taskってのはSQLの結果をパースするParserを変数にしている。
チュートリアルでは、SQL("select * from task").as(task *) と書いている。


そしてviewも変更する。
チュートリアルをちょっと変更してtableでタスク一覧を表示する。
index.scala.html

@(tasks: List[Task], taskForm: Form[String])

@import helper._

@main("Todo List") {

  <h1>@tasks.size task(s)</h1>

  <table border="1">
      <tr>
        <td>ID</td>
        <td>タスク</td>
        <td>締切日</td>
        <td>登録日</td>
      </tr>
      @tasks.map { task =>
        <tr>
          <td>@task.id</td>
          <td>@task.label</td>
          <td>@task.deadline</td>
          <td>@task.createdate</td>
          <td>
          @form(routes.Application.deleteTask(task.id)) {
            <input type="submit" value="Delete">
          }
          </td>
        </tr>
      }
    </table>
  <h2>Add a new task</h2>
  @form(routes.Application.newTask) {
    @inputText(taskForm("label"))
    <input type="submit" value="Create">
  }
}

これでタスクを登録してみると・・・

insertは成功して、再表示のTask.all()メソッドでエラーです。
[RuntimeException: UnexpectedNullableFound(TASK.DEADLINE)]
deadlineがNULLなので型が解析できないようです。

Optionを使ってNULLを許容できるようにしてあげます。

case class Task(
    id: Long,
    label: String,
    deadline: Option[Date],
    createdate: Date
)

object Task {
  val task ={
    get[Long]("id") ~
    get[String]("label") ~
    get[Option[Date]]("deadline") ~
    get[Date]("createdate") map {
      case id~label~deadline~createdate =>
        Task(id,label,deadline,createdate)
    }
  }
〜〜〜

これでおっけー!

こう見ると単純で簡単なんだけど、scala初心者には
SQLParserってなにこれどう動くのとか、case classにもOption必要なの?とか
ハマりどころだったわけです。


まずはScalaをじっくり学んだほうが良さそうですな。

おわり!